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() 方法的返回值,判斷執行不同的操作。