Yii2學習-關於Model總結

控制器

控制器週期

beforeAction

/**
 * action 運行的前置操作 
 *
 * 比較適合統一化的數據驗證,請求數據的接受等
 * @param \yii\base\Action $action action 類
 * @return bool 返回ture 控制器週期纔會走下去
 */
public function beforeAction($action)
{
    var_dump($action->id, $action->controller->id);
    return parent::beforeAction($action); // TODO: Change the autogenerated stub
}

afterAction

/**
 * action 後置操作
 *
 * 比較適合返回最後的日誌蒐集
 * @param \yii\base\Action $action action 類
 * @param mixed $result
 * @return mixed
 */
public function afterAction($action, $result)
{
    return parent::afterAction($action, $result); // TODO: Change the autogenerated stub
}

模型

AR 模型 常用方法

查詢數據

// 查詢單條
$one = Post::find()->where(['id' => 1])->one();
// 或者
$one = Post::finOne(1);

// 查詢多條
$posts = Post::find()->where(['status' => 1])->all();
// 或者
$posts = Post::findAll(['status' => 1]);

// 通過sql查詢
$posts = Post::findBySql('SELECT * FROM `posts` WHERE `status` = :status', [':status' => 1])->all();

查詢條件的設定

where 查詢支持的參數

  • 字符串格式 例如: ‘status=1’
// 對應的SQL: WHERE status = 1
$query->where('status = 1');
// 使用字符串方式,可以綁定參數
$query->where('status = :status', [':status' => 1])
  • 哈希格式 例如: [‘status’ => 1, ‘type’ => 1]

哈希格式最適合用來指定多個 AND 串聯起來的簡單的”等於斷言”子條件。

// 對應的SQL: WHERE (`status`=1) AND (`type`=1)
$query->where(['status' => 1, 'type' => 1])
// 如果值爲一個數組會轉爲IN 對應SQL: WHERE (`status`=1) AND (`type` IN (1, 2, 3))
$query->where(['status' => 1, 'type' => [1, 2, 3]])
  • 操作符格式 例如: [‘=’, ‘status’, 1]

可以實現比較複雜的查詢條件

  1. 格式
['操作符', 操作數1, 操作數2, ...];
  1. 演示
// 簡單單個查詢 對應的SQL: WHERE `status` >= 1
$query->where(['>=', 'status', 1])

// 多個條件 AND 連接 對應的SQL: WHERE (`status` >= 1) AND (`type` <= 3)
$query->where(['and', ['>=', 'status', 1], ['<=', 'type', 3]])

// 多個條件 OR 連接 對應SQL: WHERE (`status` IN (1, 3)) OR (`type` >= 2)
$query->where('or', ['in', 'status', [1, 3], ['>=', 'type', 2]])

// 比較複雜組合的SQL 對應SQL: WHERE (`create_type` = 6) AND (`status` = 0) AND (`is_fail` = 0) AND (((`last_view_time` = 0) AND (`create_time` <= 1503562511)) OR ((`last_view_time` > 0) AND (`last_view_time` <= 1503562511)))
$query->where([
     'and',
    ['=', 'create_type', 6],
    ['=', 'status', 0],
    ['=', 'is_fail', 0],
    ['or',
        ['and', ['=', 'last_view_time', 0], ['<=', 'create_time', time()]],
        ['and', ['>', 'last_view_time', 0], ['<=', 'last_view_time', time()]]
    ]
]);
  1. 操作符說明

    • and 和 or 確定多個條件通過什麼方式連接
    • between 和 not between

      // 數組需要四個元素 對應SQL: WHERE `id` BETWEEN 1 AND 10
      ['between', 'id', 1, 10] 
    • in 和 not in

      // 對應SQL: WHERE `status` IN (1, 3)
      ['in', 'status', [1, 3]]
    • like

      // 默認 對應SQL: WHERE `name` LIKE '%username%'
      ['like', 'name', 'username'] 
      
      /**
       * 第二個範圍值是一個數組,那麼將會生成用 AND 串聯起來的 多個 like 語句
       * 對應SQL: WHERE `name` LIKE '%user%' AND `name` LIKE '%name%'
       */
      ['like', 'name', ['user', 'name']]
      
      // 使用前綴查詢 對應SQL: WHERE `name` LIKE 'user%'
      ['like', 'name', 'user%', false]
    • or like 與like 相同,只是第二個範圍值是一個數組,那麼將會生成用 OR 串聯起來的 多個 like 語句

      // 對應的SQL: WHERE `name` LIKE '%user%' OR `name` LIKE '%name%'
      ['or like', 'name', ['user', 'name']]
      
      // 自己定義前綴查詢還是後綴查詢 對應SQL: WHERE `name` LIKE 'user%' OR `name` LIKE '%name'
      ['or like', 'name', ['user%', '%name'], false]
    • not like 和 or not like 和 like 與 or like 用法相同

    • exists 和 not exists 需要一個操作數,該操作數必須是代表子查詢 yii\db\Query 的一個實例

      // 生成的SQL: WHERE EXISTS (SELECT * FROM `user` WHERE `status` = 1)
      $queryOne = new Query();
      $queryOne->from('user')->where(['=', 'status', 1])->one();
      $query->where(['exists', $queryOne])
    • >, >=, =, <, <=

    • andWhere 和 orWhere 在原有條件的基礎上 附加額外的條件。你可以多次調用這些方法來分別追加不同的條件
// 對應SQL: WHERE (`status` = 1) AND (`type` >= 1)
$query->where(['=', 'status', 1]);
$query->andWhere(['>=', 'type', 1]);

// 對應的SQL: WHERE ((`status`=1) AND (`type`=1)) OR (`name`='username')
$query->where(['status' => 1, 'type' => 1]);
$query->orWhere(['name' => 'username']);
  • filterWhere 過濾查詢(在用戶輸入查詢的時候比較好用) 會忽略空值(null, ”, ’ ‘, [])
// 不會添加查詢條件
$status = ''; // null, ' ', [] 都不會添加查詢條件
$query->filterWhere(['status' =>  $status]);

// 添加查詢條件: WHERE `status` = 0
$status = 0;
$query->filterWhere(['status' => $status]);

建議查詢時候直接使用where() 方法中傳遞數組的方式定義where條件; andWhere() 和 orWhere() 會增加判斷

查詢對象轉數組

asArray() 方法

在對查詢數據沒有其他操作的情況下,建議都轉爲數組(能夠節省內存)

// 查詢出來是一個數組
$posts = Post::find()->where(['status' => 1])->asArray()->all();

指定數組的鍵

indexBy() 方法

$users = User::find()->where(['status' => 1])->asArray()->indexBy('id')->all();

// 查詢出來的數組
[
    '1' => [
        'id' => 1,
        'status' => 1,
        'username' => 'username'
    ],
    '3' => [
        'id' => 3,
        'status' => 1,
        'username' => 'my name'
    ],
];

// 這樣查詢出來的數組,判斷指定ID的用戶是否存在比較方法(特別是循環中)
if (isset($user[1])) {
    ....
}

使用場景

現在後臺程序需要顯示文章信息,並且需要顯示文章的作者信息

兩個表的結構

文章表 article

字段名 字段說明
id 文章ID
user_id 文章作者ID
title 文章標題

用戶表 user

字段名 字段說明
id 用戶ID
name 用戶姓名

- 方案一 使用表關聯

// Article 模型類中定義關聯關係
class Article extends ActiveRecord
{
    public function getUser()
    {
        return $this->hasOne(User::className(), ['id' => 'user_id']);
    }
}

/**
 * 使用
 *
 * 說明這裏也是隻使用了兩次查詢
 * 第一次: SELECT * FROM `article` LIMIT 10
 * 第二次: SELECT * FROM `user` WHERE `id` IN (1, 2, ...)
 */
$arrArticles = Article::find()->with('user')->limit(10)->asArray()->all();
if ($arrArticles) {
    foreach ($arrArticles as $article) {
        if (isset($article['user'])) {
            // 使用作者名稱
            $article['user']['name'];
        }
    }
}

  • 方案二 使用indexBy()
// 第一步查詢出文章信息
$arrArticles = Article::find()->limit(10)->asArray()->all();

// 第二步獲取到用戶信息
if ($arrArticles) {
    // 獲取到用戶ID 
    $arrUserIds = array_column($arrArticles, 'user_id');
    // 查詢到客戶信息
    $arrUsers = User::find()->where(['id' => $arrUserIds])->indexBy('id')->asArray()->all();
    // 三 將用戶名稱追加到文章信息中
    foreach ($arrArticles as &$value) {
        $value['user_name'] = '';
        if (isset($arrUsers[$value['user_id']])) {
            $value['user_name'] = $arrUsers[$value['user_id']]['name'];
        }
    }
}

批處理查詢

當需要處理大數據的時候,像 yii\db\Query::all() 這樣的方法就不太合適了, 因爲它們會把所有數據都讀取到內存上。爲了保持較低的內存需求, Yii 提供了一個 所謂的批處理查詢的支持。批處理查詢會利用數據遊標 將數據以批爲單位取出來。

use yii\db\Query;

$query = (new Query())
    ->from('user')
    ->orderBy('id');

foreach ($query->batch() as $users) {
    // $users 是一個包含100條或小於100條用戶表數據的數組
}

// or if you want to iterate the row one by one
foreach ($query->each() as $user) {
    // $user 指代的是用戶表當中的其中一行數據
}

yii\db\Query::batch() 和 yii\db\Query::each() 方法將會返回一個實現了Iterator 接口 yii\db\BatchQueryResult 的對象,可以用在 foreach 結構當中使用。在第一次迭代取數據的時候, 數據庫會執行一次 SQL 查詢,然後在剩下的迭代中,將直接從結果集中批量獲取數據。默認情況下, 一批的大小爲 100,也就意味着一批獲取的數據是 100 行。你可以通過給 batch() 或者 each() 方法的第一個參數傳值來改變每批行數的大小。

查看執行語句

// 可以通過yii 自帶的 debug 工具查看

// 通過model 類
// 查看執行SQL: SELECT * FROM `user` WHERE `status` IN (1, 2)
var_dump(User::find()->where(['status' => [1, 2, 3]])->createCommand()->getRawSql());

// 查看預處理的SQL: SELECT * FROM `user` WHERE `status` IN (:qp0, :qp1)
var_dump(User::find()->where(['status' => [1, 2, 3]])->createCommand()->getSql());

驗證

驗證規則定義

定義格式

// 第一個字段可以爲字符串,也可以是數組
[[驗證字段], 驗證規則, 其他配置項...]

// 爲字符串的時候必須爲單個字段
['username', 'required', 'message' => '用戶名不能爲空']

// 多個字段驗證
[['username', 'password'], 'required']

例子:

// 在模型的 rules 方法中定義
public function rules()
{
    return [
        // 不能爲空
        [['username', 'passwrod'], 'required'],
        // 必須唯一
        [['username'], 'unique'],
    ];
}

驗證規則類型

說明
一般提示信息可以不用填寫,會自動匹配 attributeLabels() 方法返回的字段說明信息

  • required: 必須值驗證
// requiredValue, message 可以不用填寫
[['字段名'], 'required', 'requiredValue' => '必填值', 'message' => '提示信息']
  • email 郵箱格式驗證
['email', 'email']
  • match 正則表達事驗證
[['字段名'], 'match', 'pattern' => '正則表達式']
// 正則取反
[['字段名'], 'match', 'pattern' => '正則表達式', 'not' => true]
  • url 網址信息驗證
// 驗證https 或者 http 的網址
['字段名', 'url']

/**
 * 輸入字段可以不帶 http 前綴,默認幫你添加
 * 輸入的值可以是 www.baidu.com 也可以是 http://www.baidu.com
 * 輸入 www.baidu.com 會幫你加上 http:// 最終爲 http://www.baidu.com
 */
[['字段名'], 'url', 'defaultScheme' => 'http']
  • captcha 驗證碼
['驗證碼字段', 'captcha']
  • compare 比較
// compareValue 比較值 operator 比較類型
['字段', 'compare', 'compareValue' => 30, 'operator' => '>=']

// 比較兩個字段的值(確認密碼需要與密碼一致)
[['confirm_password'], 'compare', 'compareAttribute' => 'password']
  • default 默認值 當字段爲空那麼賦默認值
// value 定義默認值
['字段', 'default', 'value' => 30]
  • exist 驗證存在
// 驗證數據username 在表中必須存在
['username', 'exist']

// 定義查詢的model, 用戶名在admin 表中是否存在
['username', 'exist', 'targetClass' => Admin::className()]

// 定義數據的字段
['username', 'exist', 'targetAttribute' => 'name']
  • unique 唯一性驗證
/**
 * targetClass 可以配置查詢那個表
 * targetAttribute 可以定義對應的字段信息
 */
['username', 'unique']
  • file 文件驗證
/**
 * extension 允許上傳的文件類型
 * 其他配置項
 * checkExtensionByMimeType 是否使用mime類型檢查文件類型(擴展名)
 * mimeTypes 允許的mimeTypes 類型
 * minSize 文件最小大小
 * maxSize 文件最大大小
 * 其他配置...
 */
[['file'], 'file', 'extensions' => ['png', 'jpg', 'gif', 'jpeg']]
  • image 圖片驗證 繼承於 File
/**
 * image 驗證繼承於 file 驗證
 * file 定義的驗證配置都可以使用
 * 其他配置
 * minWidth, minHeight, maxWidth, maxLength, ...
 */
['image', 'image', 'minWidth' => 100]
  • filter 濾鏡[trim]
/**
 * filter 制定過濾的函數 可以使用匿名函數
 * skipOnArray 是否跳過數組過濾
 */
['username', 'filter', 'filter' => 'trim', 'skipOnArray' => true]
  • trim 處理
[['username', 'password'], 'trim']
  • in 範圍值驗證
// range 定義範圍值
['status', 'in', 'range' => [1, 2]]
  • integer 整數
['age', 'integer']
  • number 數字
['money', 'number']
  • double 浮點數
['money', 'double']
  • string 字符串驗證
/**
 * length 定義字符串最小長度和最大長度
 * min, max 單獨定義指定最小,最大長度
 */
['title', 'string', 'length' => [1, 2]]
  • boolean 布爾值驗證
/**
 * strict 定義是否嚴格驗證
 */
['字段名', 'boolean', 'trueValue' => true, 'falseValue' => false, 'strict' => true]
  • date 日期驗證[data, time, datetime]
['start_time', 'date']
  • ip 驗證
[['user_ip'], 'ip']

自定義驗證規則

class User extends Model
{
    public function rules()
    {
        return [
            ['username', 'validateUser', 'params' => [1, 2, 3]]
        ];
    }

    /**
     * 自定義驗證處理
     *
     * @desc 函數寫的時候可以不接受參數
     * @param string $attribute 字段名稱
     * @param mixed $params rules 中定義的params 參數信息
     * @param \yii\validators\InlineValidator $validator 驗證類
     */
    public function validateUser($attribute, $params, $validator)
    {
        if ($this->user == 2) {
            $this->addError($attribute, '不能等於二');
        }
    }
}

驗證規則生效條件

// 可以定義指定驗證的生效條件
[
    ['passowrd'], 
    'required', 

    // 指定服務器驗證什麼時候生效(當狀態爲1的時候密碼不能爲空)
    'when' => function ($model) {
        return $model->status == 1;
    },

    // 前端js 驗證什麼時候生效
    'whenClient' => "function(attribute, value) {
        return $('#status').val() == 1;
    }",
],

驗證場景

model 可能在不同的場景下使用,不同場景對應不同的業務邏輯,需要驗證的字段和方式也就不一樣,這個時候就需要用到驗證場景

定義驗證場景

model 中定義驗證場景

class User extends Model
{
    public function scenarios()
    {
        // 默認驗證場景 default 的驗證字段
        $scenarios = parent::scenarios();

        // 登錄時候,驗證用戶名和密碼
        $scenarios['login'] = ['username', 'password'];

        // 註冊的時候要驗證用戶名、密碼、確認密碼
        $scenarios['register'] = ['username', 'password', 'confirm_password'];
        return $scenarios;
    }

}

爲規則指定驗證場景

還是上面那個model

/**
 * 定義驗證規則
 */
public function rules
{
   return [
        // 一樣定義通用驗證
        [['username', 'password'], 'trim'],

        // on 定義使用的驗證場景 可以是字符串,也可以是數組
        [['username', 'password'], 'required', 'on' => ['login', 'register']],
        ['confirm_password', 'required', 'on' => 'register'],
        ['confirm_password', 'compare', 'compareAttribute' => 'password', 'on' => 'register'],
   ]; 
}

使用驗證場景

一般在控制器中

public function actionLogin()
{
    // 使用驗證場景
    $user = new User();
    $user->scenario = 'login';

    // 或者在實例化指定驗證場景
    $user = new User(['scenario' => 'login']);

    // 對model的屬性進行賦值
    $user->username = '123';
    ...

    // 顯示的調用驗證
    if ($user->validate()) {
        // 通過驗證
    } else {
        // 獲取到錯誤信息
        var_dump($user->getErrors());
    }

    /**
     * 執行save()的時候會調用validate() 方法
     * 除非save() 調用時指定不走驗證
     * $user->save(false)
     */
    if ($user->save()) {
        ...
    }  else {
        var_dump($user->getErrors());
    }
}

驗證規則使用注意

執行驗證是通過rules 中的規則,從上往下驗證的,一旦驗證不通過,不會向下驗證, 建議關於數據庫的驗證,儘量放到最後

數據的操作

塊賦值

塊賦值只用一行代碼將用戶所有輸入填充到一個模型

$user = new User();
$user->attributes = Yii::$app->request->post('User', []);

// 等同於
$user = new User();
$data = Yii::$app->request->post('User', []);
$user->username = isset($data['username']) ? $data['username'] : null;
$user->password = isset($data['password']) ? $data['password'] : null;
  • 使用load() 方法
$user = new User();

/**
 * 默認使用Yii 表單
 * 提交的表單數組應該爲 
 * 表單元素 <input type="text" name="User[username]" />
 * [
 *      'User' => ['username' => 'username', 'password' => 'password']
 * ] 
 */ 
$user->load(Yii::$app->request->post());

/**
 * 沒有使用yii表單
 * <input type="text" name="username" >
 * 對應提交的值
 * ['username' => 'username', 'password' => '']
 */
$user->load(Yii::$app->request->post(), '');
  • 使用屬性 attributes
/**
 * attributes 需要接收一個數組 
 * [屬性字段 => 對應值]
 */
$user->attributes = Yii::$app->request->post('User', []);
  • 使用 setAttributes() 方法(前面兩種最終都是調用的 setAttributes() 方法)
$user = new User();

/**
 * 需要接收一個數組
 * [屬性字段 => 對應值]
 */
$user->setAttributes(Yii::$app->request->post());

塊賦值注意

  1. 需要定義爲安全的字段纔可以賦值成功
  2. 在沒有定義場景的情況下,定義了規則的字段、便會認爲是安全的,可以塊賦值成功
  3. 定義了驗證場景,只有場景定義的字段可以批量賦值

新增數據

$user = new User();
$user->username = 'username';
$user->password = 'password';
$user->insert();

// 或者使用
$user->save();

修改數據

  • 查詢對象修改
$user = User::findOne(1);
$user->username = 'username';
$user->password = 'password';
$user->update();

// 或者使用
$user->save();
  • 修改指定數據
// 將ID 等於 2的用戶的狀態改爲 1, 第二個參數同 where() 方法中的參數
$intNumber = User::updateAll(['status' => 1], ['id' => 2]]);

// 使用字符的綁定參數查詢修改
User::updateAll(['status' => 2], 'id=:id', [':id' => 1]);
  • 屬性值實現累加
/**
 * updateAllCounters
 * 第一個參數: 字段對應累加 正數值、累減的值 負數值
 * 第二個參數: 查詢條件 同where() 方法,可以字符串,數組
 * 第三個參數: 查詢綁定的參數
 */
User::updateAllCounters(['status' => -2], 'id=:id', [':id' => 1]);

刪除數據

  • 使用查詢對象刪除
$user = User::findOne(1);
$user->delete()
  • 刪除指定數據
User::deleteAll(['status' => 0]);

AR的生命週期

AR的生命週期
理解AR的生命週期對於你操作數據庫非常重要。生命週期通常都會有些典型的事件存在。對於開發AR的behaviors來說非常有用。

  • 當你實例化一個新的AR對象時,我們將獲得如下的生命週期:

constructor
yii\db\ActiveRecord::init(): 會觸發一個 yii\db\ActiveRecord::EVENT_INIT 事件
- 當你通過 find() 方法查詢數據時,每個AR實例都將有以下生命週期:

constructor

  1. yii\db\ActiveRecord::init(): 會觸發一個 yii\db\ActiveRecord::EVENT_INIT 事件
  2. yii\db\ActiveRecord::afterFind(): 會觸發一個 yii\db\ActiveRecord::EVENT_AFTER_FIND 事件

    • 當通過 yii\db\ActiveRecord::save() 方法寫入或者更新數據時, 我們將獲得如下生命週期:
  3. yii\db\ActiveRecord::beforeValidate(): 會觸發一個 yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE 事件

  4. yii\db\ActiveRecord::afterValidate(): 會觸發一個 yii\db\ActiveRecord::EVENT_AFTER_VALIDATE 事件
  5. yii\db\ActiveRecord::beforeSave(): 會觸發一個 yii\db\ActiveRecord::EVENT_BEFORE_INSERT 或 yii\db\ActiveRecord::EVENT_BEFORE_UPDATE 事件
  6. yii\db\ActiveRecord::afterSave(): 會觸發一個 yii\db\ActiveRecord::EVENT_AFTER_INSERT 或 yii\db\ActiveRecord::EVENT_AFTER_UPDATE 事件

    • 最後,當調用 delete() 刪除數據時, 我們將獲得如下生命週期:
  7. yii\db\ActiveRecord::beforeDelete(): 會觸發一個 yii\db\ActiveRecord::EVENT_BEFORE_DELETE 事件
    執行實際的數據刪除

  8. yii\db\ActiveRecord::afterDelete(): 會觸發一個 yii\db\ActiveRecord::EVENT_AFTER_DELETE 事件

參考文檔
- Yii2中文社區 http://www.yiichina.com/
- 深入理解Yii2.0 http://www.digpage.com/index.html
- 看雲Yii學習https://www.kancloud.cn/curder/yii/247740
- 文檔 http://www.awaimai.com/patterns

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章