1. 程式人生 > >Yii2.0 資料庫新增資料的技巧

Yii2.0 資料庫新增資料的技巧

我們以User表為例,假設User表就3個欄位,id, username, password。
當PHP從瀏覽器接收POST資料後,Yii提供了一種推薦的如下方式:

方法一、load

首先要new User;
load方法的原始碼在:vendor/yiisoft/yii2/base/Model.php,786行左右(根據版本可能有差異),定義如下:
public function load( data, formName = null)
第一個引數 dataUser:username:admin,

paafssword:adminPassword data中有以new User的User這個類相同的鍵,即“User”。
如果傳遞,則傳遞在 dataUer data中的有效資料在User這個鍵下面。另一方面,如果某一天new User改為new UserModel,我們不需要修改其它程式碼。
dataPOST _POST,yii中我們習慣用Yii::$app->request->post()。
而為什麼一定要將給User表的資料儲存到一個User的鍵下面呢?這是為了在一個頁面提交過來的資料可以提供給多個表儲存使用,比如可以從頁面中傳輸user表和user_profile表儲存的資料。
那麼,當我們一個頁面只管理一個表的時候,完全可以用這種方式。當然,在Yii框架中,如果開啟了CSRF防跨站攻擊,POST到php後臺的資料就帶有_csrf鍵,如果YII_DEBUG常量設定為TRUE了,網頁會丟擲異常的!所以,多一事不如少一事,yii框架就讓我們堅決用”表名[欄位名]”的方式給input做name吧。

$user = new User();
$data = Yii::$app->request->post();
if (!$user->load($data, 'User')) {
    $this->showErrorPage('提供的資料格式不正確!');
}
if (!$user->save()) {
    $this->showErrorPage(current($user->getFirstErrors()));
}

這種方式接收表單資料儲存到資料庫時,表單資料是如同:

['User' => [
    'username' => 'admin',
    'password' => 'admin',
]]

這種資料,也就是說,在頁面表單中有如同下面的程式碼:

<form action="save.php" method="POST">
    <input type="text" name="User[username]" value="">
    <input type="password" name="User[password]" value="">
    <button type="submit">儲存</button>
</form>

這種方式特別適合後臺的編輯和儲存,因為後臺編輯儲存資料往往比較單一,大多數針對同一個資料表。
但是,如果是使用者註冊、使用者登記資訊、提交訂單等場景,就不那麼好使了。因為後臺要根據資料進行重組。
當然,如果整理好資料,也是可以做到的,但你必須刪除掉該陣列內不屬於資料表字段的鍵名
但是萬一表單提交過來的資料如果太少了怎麼辦?比如User表還有使用者暱稱欄位nick必須提供,但是你還是希望儲存使用者提交過來的資料(即相容儲存,很多時候業務會在JS層做好使用者必填欄位的限制,但是後臺就放寬鬆了很多,尤其是大表單)。
沒關係,還是有辦法的,我們可以利用Yii2.0自帶的資料庫操作:
參考:http://www.yiichina.com/doc/guide/2.0/db-active-record#

方法二、精確操作–只針對欄位數極少的資料表

$user = new User();
$user->username = @$_POST['username'];
$user->password= @$_POST['password'];
if ($user->save()) {
    ...
}

這種方式是可以成功儲存資料的,但是一定要保證nick有預設值比如空字串,或者關閉MySQL的嚴格模式:

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

去掉STRICT_TRANS_TABLES,即可成功儲存而不報錯誤。

但是如果欄位數太多了,比如30多個,難道要寫30多行程式碼嗎(核對30個欄位名有沒有寫對,真是崩潰)?如果資料表字段有增減,豈不是很麻煩?

沒關係,我們還有辦法:

方法四、setAttributes的不安全模式

$user = new User();
$data = Yii::$app->request->post();
$user->seAttributes($data['User'], false));
if (!$user->save()) {
    $this->showErrorPage(current($user->getFirstErrors()));
}

所以:
這裡就要提到load方法的一個缺點了,它依賴於User.php這個Model的rules,如果rules裡忘記增加某個欄位的規則定義,無論定義其為required還是string資料型別,哪怕出現一個,也可以順利load上值,如果一個都沒有,比如username在rules裡沒出現,那麼load以後,$user->username的值為NULL。所以,我們無論何時何地,一定要確保rules裡對欄位有齊全的定義。
但是:如果某個資料表可以給使用者自定義欄位呢?使用者通過網頁操作,給這個表增加欄位後,我們總不能讓使用者生成一個Model吧?這時候,就必須用方法三。我們可以將那個setAllAttributes方法放到一個基類比如裡面,讓這個基類繼承\yii\db\ActiveRecord,讓我們所有通過gii建立的Model類繼承這個Model(反正有名稱空間):

<?php
namespace common\models;

class Model extends \yii\db\ActiveRecord {
    function loadData($data, $key) {
        $data = $key ? @$data[$key] : $data;
        if (!is_array($data)) {
            return false;
        }
        $this->setAttributes($data, false);
        return true;
    }
}

用法還是那樣,只不過這次不依賴rules了:

$user = new User(); $data = Yii::$app->request->post(); if (!$user->loadData($data, 'User')) { $this->showErrorPage('提供的資料格式不正確!'); } if (!$user->save()) { $this->showErrorPage(current($user->getFirstErrors())); }