yii2 之 ActiveRecord 模型

OnlyDawn發表於2019-06-24

      Active Record 模型是一種設計模式,用物件導向的方式抽象地訪問資料的模式。在 Yii2 中,每一個 Active Record 模型物件的例項是 yii\db\ActiveRecord 類或它的子類,它封裝了資料庫表或檢視中的一行記錄,並封裝了所有邏輯和訪問資料庫的細節,如果有大部分的業務邏輯,很適合使用這種模式。

1. ActiveRecord 模型概述

       在大多數企業級開發中,都需要用到物件導向方法和關係型資料庫。在軟體的業務邏輯層和使用者介面層,都需要操作物件,而在操作物件後,需要把物件的資訊儲存至資料庫中。因此,在 MVC 模式下開放一個應用程式時,程式設計師要寫很多資料訪問層的程式碼,用來執行增刪改查。通常情況下,這些資料訪問層的程式碼基本上都是先傳入操作物件,然後設定儲存過程,再設定物件與屬性對應,最後執行儲存過程。這些具有相同模式的程式碼,在每個軟體專案都重複出現,這顯然是一種資源的浪費,由此,可以使用 ActiveRecord 模型解決這些問題。

       ActiveRecord (AR)模型是一種流行的物件——關係對映技術。物件——關係對映(Object Relational Mapping,ORM)是一種為解決物件導向與關係型資料庫存在的互不匹配現象的技術。ORM 在關係型資料庫和物件之間產生一個自動對映,這樣在具體的資料庫操作中就不需要再與複雜的 SQL 語句打交道。軟體設計人員只需要關注業務邏輯中的物件架構,而不是底層重複性的資料庫 SQL 語句。

下面是一個 user 表的模型類

<?php
namespace common\models;

use Yii;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;

/**
 * User model
 *
 * @property integer $id
 * @property string $username
 * @property string $password_hash
 * @property string $password_reset_token
 * @property string $email
 * @property string $auth_key
 * @property integer $status
 * @property integer $created_at
 * @property integer $updated_at
 * @property string $password write-only password
 */
class User extends ActiveRecord implements IdentityInterface
{

    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%user}}';
    }

    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            TimestampBehavior::className(),
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['status', 'default', 'value' => self::STATUS_ACTIVE],
            ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]],
        ];
    }

}

演示下 AR 的新增操作吧

$user = new User(); // 例項化 user 表對應的 ActiveRecord 模型類
$user->username = '小牛'; // 給表中的 username 欄位賦值
$user->email = '18810980488@163.com'; // 給表中的 email 欄位賦值
$user->save(); // 執行 insert into 語句完成一次插入記錄的操作

上面的程式碼相當於執行了下面的 SQL 語句。

insert into user('username','email') value('小牛','18810980499@163.com');

ActiveRecord 的優點是簡單、直觀,一個類就包括了資料訪問和業務邏輯。減少了軟體開發時間和成本,極大的提高了資料的可讀性,也簡化了程式碼的調優和測試。

2. 透過查詢操作理解 ActiveRecord 類

靜態方法 find() 返回了 ActiveRecord 類的靜態例項物件,從而可以呼叫其他的方法,查詢例項。

我們列印了一個 one() 方法查詢返回的資訊,如下:

api\models\UserRole Object
(
    [_attributes:yii\db\BaseActiveRecord:private] => Array
        (
            [role_id] => 1
            [role_name] => 超級管理員
            [role_desc] => 超級管理員
            [role_sort] => 10
            [created_at] => 2019-06-17 15:10:21
            [delete_flg] => 0
            [updated_uid] => 1
            [updated_at] => 2019-06-19 18:12:40
        )

    [_oldAttributes:yii\db\BaseActiveRecord:private] => Array
        (
            [role_id] => 1
            [role_name] => 超級管理員
            [role_desc] => 超級管理員
            [role_sort] => 10
            [created_at] => 2019-06-17 15:10:21
            [delete_flg] => 0
            [updated_uid] => 1
            [updated_at] => 2019-06-19 18:12:40
        )

    [_related:yii\db\BaseActiveRecord:private] => Array
        (
        )

    [_relationsDependencies:yii\db\BaseActiveRecord:private] => Array
        (
        )

    [_errors:yii\base\Model:private] => 
    [_validators:yii\base\Model:private] => 
    [_scenario:yii\base\Model:private] => default
    [_events:yii\base\Component:private] => Array
        (
        )

    [_eventWildcards:yii\base\Component:private] => Array
        (
        )

    [_behaviors:yii\base\Component:private] => Array
        (
        )

)

one()方法找到了一個滿足查詢條件的行,返回一個例項物件,例項的屬性含有資料錶行中相應列的值。然後,就可以像讀取普通物件的屬性那樣讀取查詢結構

$role->role_name; // 輸出 “超級管理員”

如果是傳統的面嚮物件語言,如 C++ 或 Java,這裡就會報編譯錯誤,因為類沒有定義“role_name”成員屬性。而對於 PHP 而言,語言有著對動態特性(魔術方法)的支援,當寫入【讀取】物件屬性不存在時,會觸發 ActiveRecord 中定義的 set()【get()】 方法,這樣的呼叫就沒有任何問題。

3. 透過插入和更新操作理解 ActiveRecord 類

同樣使用 save() 方法執行插入和更新操作。如果例項物件是 new 運算子建立的,呼叫 save() 方法會新增一條資料,而例項物件是 find() 方法的結果,那麼呼叫 save() 將更新表中現有的行。

檢視在 vendor\yiisoft\yii2\db\BaseActiveRecord.php 檔案中的程式碼

    /*
     * 儲存當前的記錄
     * 插入記錄到資料表的一行,如果它的 isNewRecord 屬性為 true(通常情況下使用 new 運算子來建立記錄),
     * 否則,將被用於更新表中的相應行(通常情況下使用 find 方法查詢記錄
     */
  public function save($runValidation = true, $attributeNames = null)
  {
      if ($this->getIsNewRecord()) {
          return $this->insert($runValidation, $attributeNames);
      }

      return $this->update($runValidation, $attributeNames) !== false;
  }

如上程式碼所示,透過判斷 $this->getIsNewRecord() 方法的返回值,判斷執行不同的操作。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章