亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

directory search
閱讀前篇 簡(jiǎn)介 Yii 是什么 從 Yii 1.1 升級(jí) 入門(mén) 安裝 Yii 運(yùn)行應(yīng)用 第一次問(wèn)候 使用Forms 數(shù)據(jù)庫(kù)應(yīng)用 使用 Gii 生成代碼 進(jìn)階 應(yīng)用結(jié)構(gòu) 概述 入口腳本 應(yīng)用(Applications) 應(yīng)用組件(Application Components) 控制器(Controllers) 模型(Models) 視圖(views) 模塊(Modules) 過(guò)濾器(Filters) 小部件(Widgets) 前端資源(Assets) 擴(kuò)展(Extensions) 請(qǐng)求處理 運(yùn)行概述 啟動(dòng)引導(dǎo)(Bootstrapping) 路由和創(chuàng)建URL 請(qǐng)求(Requests) 響應(yīng)(Responses) Sessions 和 Cookies 錯(cuò)誤處理(Handling Errors) 日志(Logging) 關(guān)鍵概念 組件(Component) 屬性(Property) 事件(Events) 行為(Behaviors) 配置(Configurations) 別名(Aliases) 類(lèi)自動(dòng)加載(Autoloading) 服務(wù)定位器(Service Locator) 依賴(lài)注入容器(Dependency Injection Container) 配合數(shù)據(jù)庫(kù)工作 數(shù)據(jù)庫(kù)訪(fǎng)問(wèn) (Data Access Objects) 查詢(xún)生成器(Query Builder) 活動(dòng)記錄(Active Record) 數(shù)據(jù)庫(kù)遷移(Migrations) Sphinx Redis MongoDB Elasticsearch 接收用戶(hù)數(shù)據(jù) 創(chuàng)建表單(Creating Forms) 輸入驗(yàn)證(Validating Input) 文件上傳(Uploading Files) 收集列表輸入(Collecting Tabular Input) 多模型的復(fù)合表單(Getting Data for Multiple Models) 顯示數(shù)據(jù) 格式化輸出數(shù)據(jù)(Data Formatting) 分頁(yè)(Pagination) 排序(Sorting) 數(shù)據(jù)提供器(Data Providers) 數(shù)據(jù)小部件(Data Widgets) 客戶(hù)端腳本使用(Working with Client Scripts) 主題(Theming) 安全 認(rèn)證(Authentication) 授權(quán)(Authorization) 處理密碼(Working with Passwords) 客戶(hù)端認(rèn)證(Auth Clients) 最佳安全實(shí)踐(Best Practices) 緩存 概述 數(shù)據(jù)緩存 片段緩存 頁(yè)面緩存 HTTP 緩存 RESTfull Web服務(wù) 快速入門(mén)(Quick Start) 資源(Resources) 控制器(Controllers) 路由(Routing) 格式化響應(yīng)(Response Formatting) 授權(quán)認(rèn)證(Authentication) 速率限制(Rate Limiting) 版本(Versioning) 錯(cuò)誤處理(Error Handling) 開(kāi)發(fā)工具 調(diào)試工具欄和調(diào)試器 使用Gii生成代碼 生成API文檔 測(cè)試 概述(Overview) 配置測(cè)試環(huán)境(Testing environment setup) 單元測(cè)試(Unit Tests) 功能測(cè)試(Function Tests) 驗(yàn)收測(cè)試(Acceptance Tests) 測(cè)試夾具(Fixtures) 高級(jí)專(zhuān)題 高級(jí)應(yīng)用模板 創(chuàng)建自定義應(yīng)用程序結(jié)構(gòu) 控制臺(tái)命令 核心驗(yàn)證器(Core Validators) 國(guó)際化 收發(fā)郵件 性能優(yōu)化 共享主機(jī)環(huán)境 模板引擎 集成第三方代碼 小部件 Bootstrap 小部件 Jquery UI 助手類(lèi) 概述 Array 助手(ArrayHelper) Html 助手(Html) Url 助手(Url)
characters

Active Record

Active Record

注意:該章節(jié)還在開(kāi)發(fā)中。

Active Record?(活動(dòng)記錄,以下簡(jiǎn)稱(chēng)AR)提供了一個(gè)面向?qū)ο蟮慕涌冢?用以訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)中的數(shù)據(jù)。一個(gè) AR 類(lèi)關(guān)聯(lián)一張數(shù)據(jù)表, 每個(gè) AR 對(duì)象對(duì)應(yīng)表中的一行,對(duì)象的屬性(即 AR 的特性Attribute)映射到數(shù)據(jù)行的對(duì)應(yīng)列。 一條活動(dòng)記錄(AR對(duì)象)對(duì)應(yīng)數(shù)據(jù)表的一行,AR對(duì)象的屬性則映射該行的相應(yīng)列。 您可以直接以面向?qū)ο蟮姆绞絹?lái)操縱數(shù)據(jù)表中的數(shù)據(jù),媽媽再不用擔(dān)心我需要寫(xiě)原生 SQL 語(yǔ)句啦。

例如,假定?Customer?AR 類(lèi)關(guān)聯(lián)著?customer?表,且該類(lèi)的?name?屬性代表?customer?表的?name?列。 你可以寫(xiě)以下代碼來(lái)哉customer?表里插入一行新的記錄:

用 AR 而不是原生的 SQL 語(yǔ)句去執(zhí)行數(shù)據(jù)庫(kù)查詢(xún),可以調(diào)用直觀(guān)方法來(lái)實(shí)現(xiàn)相同目標(biāo)。如,調(diào)用 yii\db\ActiveRecord::save() 方法將執(zhí)行插入或更新輪詢(xún),將在該 AR 類(lèi)關(guān)聯(lián)的數(shù)據(jù)表新建或更新一行數(shù)據(jù):

$customer = new Customer();
$customer->name = 'Qiang';
$customer->save();  // 一行新數(shù)據(jù)插入 customer 表

上面的代碼和使用下面的原生 SQL 語(yǔ)句是等效的,但顯然前者更直觀(guān), 更不易出錯(cuò),并且面對(duì)不同的數(shù)據(jù)庫(kù)系統(tǒng)(DBMS, Database Management System)時(shí)更不容易產(chǎn)生兼容性問(wèn)題。

$db->createCommand('INSERT INTO customer (name) VALUES (:name)', [
    ':name' => 'Qiang',
])->execute();

下面是所有目前被 Yii 的 AR 功能所支持的數(shù)據(jù)庫(kù)列表:

  • MySQL 4.1 及以上:通過(guò) yii\db\ActiveRecord
  • PostgreSQL 7.3 及以上:通過(guò) yii\db\ActiveRecord
  • SQLite 2 和 3:通過(guò) yii\db\ActiveRecord
  • Microsoft SQL Server 2010 及以上:通過(guò) yii\db\ActiveRecord
  • Oracle: 通過(guò) yii\db\ActiveRecord
  • CUBRID 9.1 及以上:通過(guò) yii\db\ActiveRecord
  • Sphinx:通過(guò) yii\sphinx\ActiveRecord,需求?yii2-sphinx?擴(kuò)展
  • ElasticSearch:通過(guò) yii\elasticsearch\ActiveRecord,需求?yii2-elasticsearch?擴(kuò)展
  • Redis 2.6.12 及以上:通過(guò) yii\redis\ActiveRecord,需求?yii2-redis?擴(kuò)展
  • MongoDB 1.3.0 及以上:通過(guò) yii\mongodb\ActiveRecord,需求?yii2-mongodb?擴(kuò)展

如你所見(jiàn),Yii 不僅提供了對(duì)關(guān)系型數(shù)據(jù)庫(kù)的 AR 支持,還提供了 NoSQL 數(shù)據(jù)庫(kù)的支持。 在這個(gè)教程中,我們會(huì)主要描述對(duì)關(guān)系型數(shù)據(jù)庫(kù)的 AR 用法。 然而,絕大多數(shù)的內(nèi)容在 NoSQL 的 AR 里同樣適用。

聲明 AR 類(lèi)

要想聲明一個(gè) AR 類(lèi),你需要擴(kuò)展 yii\db\ActiveRecord 基類(lèi), 并實(shí)現(xiàn)?tableName?方法,返回與之相關(guān)聯(lián)的的數(shù)據(jù)表的名稱(chēng):

namespace app\models;

use yii\db\ActiveRecord;

class Customer extends ActiveRecord{
    
    public static function tableName()
    {
        return 'customer';
    }
}

訪(fǎng)問(wèn)列數(shù)據(jù)

AR 把相應(yīng)數(shù)據(jù)行的每一個(gè)字段映射為 AR 對(duì)象的一個(gè)個(gè)特性變量(Attribute) 一個(gè)特性就好像一個(gè)普通對(duì)象的公共屬性一樣(public property)。 特性變量的名稱(chēng)和對(duì)應(yīng)字段的名稱(chēng)是一樣的,且大小姓名。

使用以下語(yǔ)法讀取列的值:

// "id" 和 "mail" 是 $customer 對(duì)象所關(guān)聯(lián)的數(shù)據(jù)表的對(duì)應(yīng)字段名$id = $customer->id;
$email = $customer->email;

要改變列值,只要給關(guān)聯(lián)屬性賦新值并保存對(duì)象即可:

$customer->email = 'james@example.com';
$customer->save();

建立數(shù)據(jù)庫(kù)連接

AR 用一個(gè) yii\db\Connection 對(duì)象與數(shù)據(jù)庫(kù)交換數(shù)據(jù)。 默認(rèn)的,它使用?db?組件作為其連接對(duì)象。詳見(jiàn)數(shù)據(jù)庫(kù)基礎(chǔ)章節(jié), 你可以在應(yīng)用程序配置文件中設(shè)置下?db?組件,就像這樣,

return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=testdb',
            'username' => 'demo',
            'password' => 'demo',
        ],
    ],
];

如果在你的應(yīng)用中應(yīng)用了不止一個(gè)數(shù)據(jù)庫(kù),且你需要給你的 AR 類(lèi)使用不同的數(shù)據(jù)庫(kù)鏈接(DB connection) ,你可以覆蓋掉 yii\db\ActiveRecord::getDb() 方法:

class Customer extends ActiveRecord{
    // ...

    public static function getDb()
    {
        return \Yii::$app->db2;  // 使用名為 "db2" 的應(yīng)用組件
    }
}

查詢(xún)數(shù)據(jù)

AR 提供了兩種方法來(lái)構(gòu)建 DB 查詢(xún)并向 AR 實(shí)例里填充數(shù)據(jù):

  • yii\db\ActiveRecord::find()
  • yii\db\ActiveRecord::findBySql()

以上兩個(gè)方法都會(huì)返回 yii\db\ActiveQuery 實(shí)例,該類(lèi)繼承自yii\db\Query, 因此,他們都支持同一套靈活且強(qiáng)大的 DB 查詢(xún)方法,如where(),join(),orderBy(),等等。 下面的這些案例展示了一些可能的玩法:

// 取回所有活躍客戶(hù)(狀態(tài)為 *active* 的客戶(hù))并以他們的 ID 排序:$customers = Customer::find()
    ->where(['status' => Customer::STATUS_ACTIVE])
    ->orderBy('id')
    ->all();

// 返回ID為1的客戶(hù):$customer = Customer::find()
    ->where(['id' => 1])
    ->one();

// 取回活躍客戶(hù)的數(shù)量:$count = Customer::find()
    ->where(['status' => Customer::STATUS_ACTIVE])
    ->count();

// 以客戶(hù)ID索引結(jié)果集:$customers = Customer::find()->indexBy('id')->all();
// $customers 數(shù)組以 ID 為索引

// 用原生 SQL 語(yǔ)句檢索客戶(hù):$sql = 'SELECT * FROM customer';
$customers = Customer::findBySql($sql)->all();

小技巧:在上面的代碼中,Customer::STATUS_ACTIVE?是一個(gè)在?Customer?類(lèi)里定義的常量。(譯注:這種常量的值一般都是tinyint)相較于直接在代碼中寫(xiě)死字符串或數(shù)字,使用一個(gè)更有意義的常量名稱(chēng)是一種更好的編程習(xí)慣。

有兩個(gè)快捷方法:findOne?和?findAll()?用來(lái)返回一個(gè)或者一組ActiveRecord實(shí)例。前者返回第一個(gè)匹配到的實(shí)例,后者返回所有。 例如:

// 返回 id 為 1 的客戶(hù)$customer = Customer::findOne(1);

// 返回 id 為 1 且狀態(tài)為 *active* 的客戶(hù)$customer = Customer::findOne([
    'id' => 1,
    'status' => Customer::STATUS_ACTIVE,
]);

// 返回id為1、2、3的一組客戶(hù)$customers = Customer::findAll([1, 2, 3]);

// 返回所有狀態(tài)為 "deleted" 的客戶(hù)$customer = Customer::findAll([
    'status' => Customer::STATUS_DELETED,
]);

以數(shù)組形式獲取數(shù)據(jù)

有時(shí)候,我們需要處理很大量的數(shù)據(jù),這時(shí)可能需要用一個(gè)數(shù)組來(lái)存儲(chǔ)取到的數(shù)據(jù), 從而節(jié)省內(nèi)存。你可以用?asArray()?函數(shù)做到這一點(diǎn):

// 以數(shù)組而不是對(duì)象形式取回客戶(hù)信息:$customers = Customer::find()
    ->asArray()
    ->all();
// $customers 的每個(gè)元素都是鍵值對(duì)數(shù)組

批量獲取數(shù)據(jù)

在?Query Builder(查詢(xún)構(gòu)造器)?里,我們已經(jīng)解釋了當(dāng)需要從數(shù)據(jù)庫(kù)中查詢(xún)大量數(shù)據(jù)時(shí),你可以用?batch query(批量查詢(xún))來(lái)限制內(nèi)存的占用。 你可能也想在 AR 里使用相同的技巧,比如這樣……

// 一次提取 10 個(gè)客戶(hù)信息foreach (Customer::find()->batch(10) as $customers) {
    // $customers 是 10 個(gè)或更少的客戶(hù)對(duì)象的數(shù)組
}
// 一次提取 10 個(gè)客戶(hù)并一個(gè)一個(gè)地遍歷處理foreach (Customer::find()->each(10) as $customer) {
    // $customer 是一個(gè) ”Customer“ 對(duì)象
}
// 貪婪加載模式的批處理查詢(xún)foreach (Customer::find()->with('orders')->each() as $customer) {
}

操作數(shù)據(jù)

AR 提供以下方法插入、更新和刪除與 AR 對(duì)象關(guān)聯(lián)的那張表中的某一行:

  • yii\db\ActiveRecord::save()
  • yii\db\ActiveRecord::insert()
  • yii\db\ActiveRecord::update()
  • yii\db\ActiveRecord::delete()

AR 同時(shí)提供了一下靜態(tài)方法,可以應(yīng)用在與某 AR 類(lèi)所關(guān)聯(lián)的整張表上。 用這些方法的時(shí)候千萬(wàn)要小心,因?yàn)樗麄冏饔糜谡麖埍恚?比如,deleteAll()?會(huì)刪除掉表里所有的記錄。

  • yii\db\ActiveRecord::updateCounters()
  • yii\db\ActiveRecord::updateAll()
  • yii\db\ActiveRecord::updateAllCounters()
  • yii\db\ActiveRecord::deleteAll()

下面的這些例子里,詳細(xì)展現(xiàn)了如何使用這些方法:

// 插入新客戶(hù)的記錄$customer = new Customer();
$customer->name = 'James';
$customer->email = 'james@example.com';
$customer->save();  // 等同于 $customer->insert();

// 更新現(xiàn)有客戶(hù)記錄$customer = Customer::findOne($id);
$customer->email = 'james@example.com';
$customer->save();  // 等同于 $customer->update();

// 刪除已有客戶(hù)記錄$customer = Customer::findOne($id);
$customer->delete();

// 刪除多個(gè)年齡大于20,性別為男(Male)的客戶(hù)記錄
Customer::deleteAll('age > :age AND gender = :gender', [':age' => 20, ':gender' => 'M']);

// 所有客戶(hù)的age(年齡)字段加1:
Customer::updateAllCounters(['age' => 1]);

須知:save()?方法會(huì)調(diào)用?insert()?和?update()?中的一個(gè), 用哪個(gè)取決于當(dāng)前 AR 對(duì)象是不是新對(duì)象(在函數(shù)內(nèi)部,他會(huì)檢查 yii\db\ActiveRecord::isNewRecord 的值)。 若 AR 對(duì)象是由?new?操作符 初始化出來(lái)的,save()?方法會(huì)在表里插入一條數(shù)據(jù); 如果一個(gè) AR 是由?find()?方法獲取來(lái)的, 則?save()?會(huì)更新表里的對(duì)應(yīng)行記錄。

數(shù)據(jù)輸入與有效性驗(yàn)證

由于AR繼承自yii\base\Model,所以它同樣也支持Model的數(shù)據(jù)輸入、驗(yàn)證等特性。例如,你可以聲明一個(gè)rules方法用來(lái)覆蓋掉yii\base\Model::rules()里的;你也可以給AR實(shí)例批量賦值;你也可以通過(guò)調(diào)用yii\base\Model::validate()執(zhí)行數(shù)據(jù)驗(yàn)證。

當(dāng)你調(diào)用?save()insert()、update()?這三個(gè)方法時(shí),會(huì)自動(dòng)調(diào)用yii\base\Model::validate()方法。如果驗(yàn)證失敗,數(shù)據(jù)將不會(huì)保存進(jìn)數(shù)據(jù)庫(kù)。

下面的例子演示了如何使用AR 獲取/驗(yàn)證用戶(hù)輸入的數(shù)據(jù)并將他們保存進(jìn)數(shù)據(jù)庫(kù):

// 新建一條記錄$model = new Customer;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
    // 獲取用戶(hù)輸入的數(shù)據(jù),驗(yàn)證并保存
}

// 更新主鍵為$id的AR$model = Customer::findOne($id);
if ($model === null) {
    throw new NotFoundHttpException;
}
if ($model->load(Yii::$app->request->post()) && $model->save()) {
    // 獲取用戶(hù)輸入的數(shù)據(jù),驗(yàn)證并保存
}

讀取默認(rèn)值

你的表列也許定義了默認(rèn)值。有時(shí)候,你可能需要在使用web表單的時(shí)候給AR預(yù)設(shè)一些值。如果你需要這樣做,可以在顯示表單內(nèi)容前通過(guò)調(diào)用loadDefaultValues()方法來(lái)實(shí)現(xiàn):?`php $customer = new Customer(); $customer->loadDefaultValues(); // ... 渲染 $customer 的 HTML 表單 ...?`

AR的生命周期

理解AR的生命周期對(duì)于你操作數(shù)據(jù)庫(kù)非常重要。生命周期通常都會(huì)有些典型的事件存在。對(duì)于開(kāi)發(fā)AR的behaviors來(lái)說(shuō)非常有用。

當(dāng)你實(shí)例化一個(gè)新的AR對(duì)象時(shí),我們將獲得如下的生命周期:

  1. constructor
  2. yii\db\ActiveRecord::init(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_INIT 事件

當(dāng)你通過(guò) yii\db\ActiveRecord::find() 方法查詢(xún)數(shù)據(jù)時(shí),每個(gè)AR實(shí)例都將有以下生命周期:

  1. constructor
  2. yii\db\ActiveRecord::init(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_INIT 事件
  3. yii\db\ActiveRecord::afterFind(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_AFTER_FIND 事件

當(dāng)通過(guò) yii\db\ActiveRecord::save() 方法寫(xiě)入或者更新數(shù)據(jù)時(shí), 我們將獲得如下生命周期:

  1. yii\db\ActiveRecord::beforeValidate(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE 事件
  2. yii\db\ActiveRecord::afterValidate(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_AFTER_VALIDATE 事件
  3. yii\db\ActiveRecord::beforeSave(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_BEFORE_INSERT 或 yii\db\ActiveRecord::EVENT_BEFORE_UPDATE 事件
  4. 執(zhí)行實(shí)際的數(shù)據(jù)寫(xiě)入或更新
  5. yii\db\ActiveRecord::afterSave(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_AFTER_INSERT 或 yii\db\ActiveRecord::EVENT_AFTER_UPDATE 事件

最后,當(dāng)調(diào)用 yii\db\ActiveRecord::delete() 刪除數(shù)據(jù)時(shí), 我們將獲得如下生命周期:

  1. yii\db\ActiveRecord::beforeDelete(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_BEFORE_DELETE 事件
  2. 執(zhí)行實(shí)際的數(shù)據(jù)刪除
  3. yii\db\ActiveRecord::afterDelete(): 會(huì)觸發(fā)一個(gè) yii\db\ActiveRecord::EVENT_AFTER_DELETE 事件

查詢(xún)關(guān)聯(lián)的數(shù)據(jù)

使用 AR 方法也可以查詢(xún)數(shù)據(jù)表的關(guān)聯(lián)數(shù)據(jù)(如,選出表A的數(shù)據(jù)可以拉出表B的關(guān)聯(lián)數(shù)據(jù))。 有了 AR, 返回的關(guān)聯(lián)數(shù)據(jù)連接就像連接關(guān)聯(lián)主表的 AR 對(duì)象的屬性一樣。

建立關(guān)聯(lián)關(guān)系后,通過(guò)?$customer->orders?可以獲取 一個(gè)?Order?對(duì)象的數(shù)組,該數(shù)組代表當(dāng)前客戶(hù)對(duì)象的訂單集。

定義關(guān)聯(lián)關(guān)系使用一個(gè)可以返回 yii\db\ActiveQuery 對(duì)象的 getter 方法, yii\db\ActiveQuery對(duì)象有關(guān)聯(lián)上下文的相關(guān)信息,因此可以只查詢(xún)關(guān)聯(lián)數(shù)據(jù)。

例如:

class Customer extends \yii\db\ActiveRecord{
    public function getOrders()
    {
        // 客戶(hù)和訂單通過(guò) Order.customer_id -> id 關(guān)聯(lián)建立一對(duì)多關(guān)系
        return $this->hasMany(Order::className(), ['customer_id' => 'id']);
    }
}

class Order extends \yii\db\ActiveRecord{
    // 訂單和客戶(hù)通過(guò) Customer.id -> customer_id 關(guān)聯(lián)建立一對(duì)一關(guān)系
    public function getCustomer()
    {
        return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
    }
}

以上使用了 yii\db\ActiveRecord::hasMany() 和 yii\db\ActiveRecord::hasOne() 方法。 以上兩例分別是關(guān)聯(lián)數(shù)據(jù)多對(duì)一關(guān)系和一對(duì)一關(guān)系的建模范例。 如,一個(gè)客戶(hù)有很多訂單,一個(gè)訂單只歸屬一個(gè)客戶(hù)。 兩個(gè)方法都有兩個(gè)參數(shù)并返回 yii\db\ActiveQuery 對(duì)象。

  • $class:關(guān)聯(lián)模型類(lèi)名,它必須是一個(gè)完全合格的類(lèi)名。
  • $link: 兩個(gè)表的關(guān)聯(lián)列,應(yīng)為鍵值對(duì)數(shù)組的形式。 數(shù)組的鍵是?$class?關(guān)聯(lián)表的列名, 而數(shù)組值是關(guān)聯(lián)類(lèi) $class 的列名。 基于表外鍵定義關(guān)聯(lián)關(guān)系是最佳方法。

建立關(guān)聯(lián)關(guān)系后,獲取關(guān)聯(lián)數(shù)據(jù)和獲取組件屬性一樣簡(jiǎn)單, 執(zhí)行以下相應(yīng)getter方法即可:

// 取得客戶(hù)的訂單$customer = Customer::findOne(1);
$orders = $customer->orders; // $orders 是 Order 對(duì)象數(shù)組

以上代碼實(shí)際執(zhí)行了以下兩條 SQL 語(yǔ)句:

SELECT * FROM customer WHERE id=1;SELECT * FROM order WHERE customer_id=1;

提示:再次用表達(dá)式?$customer->orders將不會(huì)執(zhí)行第二次 SQL 查詢(xún), SQL 查詢(xún)只在該表達(dá)式第一次使用時(shí)執(zhí)行。 數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)只返回緩存在內(nèi)部前一次取回的結(jié)果集,如果你想查詢(xún)新的 關(guān)聯(lián)數(shù)據(jù),先要注銷(xiāo)現(xiàn)有結(jié)果集:unset($customer->orders);

有時(shí)候需要在關(guān)聯(lián)查詢(xún)中傳遞參數(shù),如不需要返回客戶(hù)全部訂單, 只需要返回購(gòu)買(mǎi)金額超過(guò)設(shè)定值的大訂單, 通過(guò)以下getter方法聲明一個(gè)關(guān)聯(lián)數(shù)據(jù)?bigOrders?:

class Customer extends \yii\db\ActiveRecord{
    public function getBigOrders($threshold = 100)
    {
        return $this->hasMany(Order::className(), ['customer_id' => 'id'])
            ->where('subtotal > :threshold', [':threshold' => $threshold])
            ->orderBy('id');
    }
}

hasMany()?返回 yii\db\ActiveQuery 對(duì)象,該對(duì)象允許你通過(guò) yii\db\ActiveQuery 方法定制查詢(xún)。

如上聲明后,執(zhí)行?$customer->bigOrders?就返回 總額大于100的訂單。使用以下代碼更改設(shè)定值:

$orders = $customer->getBigOrders(200)->all();

>注意:關(guān)聯(lián)查詢(xún)返回的是 yii\db\ActiveQuery 的實(shí)例,如果像特性(如類(lèi)屬性)那樣連接關(guān)聯(lián)數(shù)據(jù), 返回的結(jié)果是關(guān)聯(lián)查詢(xún)的結(jié)果,即 yii\db\ActiveRecord 的實(shí)例, 或者是數(shù)組,或者是 null ,取決于關(guān)聯(lián)關(guān)系的多樣性。如,$customer->getOrders()?返回?ActiveQuery實(shí)例,而?$customer->orders?返回Order?對(duì)象數(shù)組 (如果查詢(xún)結(jié)果為空則返回空數(shù)組)。

中間關(guān)聯(lián)表

有時(shí),兩個(gè)表通過(guò)中間表關(guān)聯(lián),定義這樣的關(guān)聯(lián)關(guān)系, 可以通過(guò)調(diào)用 yii\db\ActiveQuery::via() 方法或 yii\db\ActiveQuery::viaTable() 方法來(lái)定制 yii\db\ActiveQuery 對(duì)象 。

舉例而言,如果?order?表和?item?表通過(guò)中間表?order_item?關(guān)聯(lián)起來(lái), 可以在?Order?類(lèi)聲明?items?關(guān)聯(lián)關(guān)系取代中間表:

class Order extends \yii\db\ActiveRecord{
    public function getItems()
    {
        return $this->hasMany(Item::className(), ['id' => 'item_id'])
            ->viaTable('order_item', ['order_id' => 'id']);
    }
}

兩個(gè)方法是相似的,除了 yii\db\ActiveQuery::via() 方法的第一個(gè)參數(shù)是使用 AR 類(lèi)中定義的關(guān)聯(lián)名。 以上方法取代了中間表,等價(jià)于:

class Order extends \yii\db\ActiveRecord{
    public function getOrderItems()
    {
        return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
    }

    public function getItems()
    {
        return $this->hasMany(Item::className(), ['id' => 'item_id'])
            ->via('orderItems');
    }
}

延遲加載和即時(shí)加載(又稱(chēng)惰性加載與貪婪加載)

如前所述,當(dāng)你第一次連接關(guān)聯(lián)對(duì)象時(shí), AR 將執(zhí)行一個(gè)數(shù)據(jù)庫(kù)查詢(xún) 來(lái)檢索請(qǐng)求數(shù)據(jù)并填充到關(guān)聯(lián)對(duì)象的相應(yīng)屬性。 如果再次連接相同的關(guān)聯(lián)對(duì)象,不再執(zhí)行任何查詢(xún)語(yǔ)句,這種數(shù)據(jù)庫(kù)查詢(xún)的執(zhí)行方法稱(chēng)為“延遲加載”。如:

// SQL executed: SELECT * FROM customer WHERE id=1$customer = Customer::findOne(1);
// SQL executed: SELECT * FROM order WHERE customer_id=1$orders = $customer->orders;
// 沒(méi)有 SQL 語(yǔ)句被執(zhí)行$orders2 = $customer->orders; //取回上次查詢(xún)的緩存數(shù)據(jù)

延遲加載非常實(shí)用,但是,在以下場(chǎng)景中使用延遲加載會(huì)遭遇性能問(wèn)題:

// SQL executed: SELECT * FROM customer LIMIT 100$customers = Customer::find()->limit(100)->all();

foreach ($customers as $customer) {
    // SQL executed: SELECT * FROM order WHERE customer_id=...
    $orders = $customer->orders;
    // ...處理 $orders...
}

假設(shè)數(shù)據(jù)庫(kù)查出的客戶(hù)超過(guò)100個(gè),以上代碼將執(zhí)行多少條 SQL 語(yǔ)句? 101 條!第一條 SQL 查詢(xún)語(yǔ)句取回100個(gè)客戶(hù),然后, 每個(gè)客戶(hù)要執(zhí)行一條 SQL 查詢(xún)語(yǔ)句以取回該客戶(hù)的所有訂單。

為解決以上性能問(wèn)題,可以通過(guò)調(diào)用 yii\db\ActiveQuery::with() 方法使用即時(shí)加載解決。

// SQL executed: SELECT * FROM customer LIMIT 100;//               SELECT * FROM orders WHERE customer_id IN (1,2,...)$customers = Customer::find()->limit(100)
    ->with('orders')->all();

foreach ($customers as $customer) {
    // 沒(méi)有 SQL 語(yǔ)句被執(zhí)行
    $orders = $customer->orders;
    // ...處理 $orders...
}

如你所見(jiàn),同樣的任務(wù)只需要兩個(gè) SQL 語(yǔ)句。 >須知:通常,即時(shí)加載 N 個(gè)關(guān)聯(lián)關(guān)系而通過(guò) via() 或者 viaTable() 定義了 M 個(gè)關(guān)聯(lián)關(guān)系, 將有 1+M+N 條 SQL 查詢(xún)語(yǔ)句被執(zhí)行:一個(gè)查詢(xún)?nèi)』刂鞅硇袛?shù), 一個(gè)查詢(xún)給每一個(gè) (M) 中間表,一個(gè)查詢(xún)給每個(gè) (N) 關(guān)聯(lián)表。 注意:當(dāng)用即時(shí)加載定制 select() 時(shí),確保連接 到關(guān)聯(lián)模型的列都被包括了,否則,關(guān)聯(lián)模型不會(huì)載入。如:

$orders = Order::find()->select(['id', 'amount'])->with('customer')->all();
// $orders[0]->customer 總是空的,使用以下代碼解決這個(gè)問(wèn)題:$orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();

有時(shí)候,你想自由的自定義關(guān)聯(lián)查詢(xún),延遲加載和即時(shí)加載都可以實(shí)現(xiàn),如:

$customer = Customer::findOne(1);
// 延遲加載: SELECT * FROM order WHERE customer_id=1 AND subtotal>100$orders = $customer->getOrders()->where('subtotal>100')->all();

// 即時(shí)加載: SELECT * FROM customer LIMIT 100//          SELECT * FROM order WHERE customer_id IN (1,2,...) AND subtotal>100$customers = Customer::find()->limit(100)->with([
    'orders' => function($query) {
        $query->andWhere('subtotal>100');
    },
])->all();

逆關(guān)系

關(guān)聯(lián)關(guān)系通常成對(duì)定義,如:Customer 可以有個(gè)名為 orders 關(guān)聯(lián)項(xiàng), 而 Order 也有個(gè)名為customer 的關(guān)聯(lián)項(xiàng):

class Customer extends ActiveRecord{
    ....
    public function getOrders()
    {
        return $this->hasMany(Order::className(), ['customer_id' => 'id']);
    }
}

class Order extends ActiveRecord{
    ....
    public function getCustomer()
    {
        return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
    }
}

如果我們執(zhí)行以下查詢(xún),可以發(fā)現(xiàn)訂單的 customer 和 找到這些訂單的客戶(hù)對(duì)象并不是同一個(gè)。連接 customer->orders 將觸發(fā)一條 SQL 語(yǔ)句 而連接一個(gè)訂單的 customer 將觸發(fā)另一條 SQL 語(yǔ)句。

// SELECT * FROM customer WHERE id=1$customer = Customer::findOne(1);
// 輸出 "不相同"// SELECT * FROM order WHERE customer_id=1// SELECT * FROM customer WHERE id=1if ($customer->orders[0]->customer === $customer) {
    echo '相同';
} else {
    echo '不相同';
}

為避免多余執(zhí)行的后一條語(yǔ)句,我們可以為 customer或 orders 關(guān)聯(lián)關(guān)系定義相反的關(guān)聯(lián)關(guān)系,通過(guò)調(diào)用 yii\db\ActiveQuery::inverseOf() 方法可以實(shí)現(xiàn)。

class Customer extends ActiveRecord{
    ....
    public function getOrders()
    {
        return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer');
    }
}

現(xiàn)在我們同樣執(zhí)行上面的查詢(xún),我們將得到:

// SELECT * FROM customer WHERE id=1$customer = Customer::findOne(1);
// 輸出相同// SELECT * FROM order WHERE customer_id=1if ($customer->orders[0]->customer === $customer) {
    echo '相同';
} else {
    echo '不相同';
}

以上我們展示了如何在延遲加載中使用相對(duì)關(guān)聯(lián)關(guān)系, 相對(duì)關(guān)系也可以用在即時(shí)加載中:

// SELECT * FROM customer// SELECT * FROM order WHERE customer_id IN (1, 2, ...)$customers = Customer::find()->with('orders')->all();
// 輸出相同if ($customers[0]->orders[0]->customer === $customers[0]) {
    echo '相同';
} else {
    echo '不相同';
}

>注意:相對(duì)關(guān)系不能在包含中間表的關(guān)聯(lián)關(guān)系中定義。 即是,如果你的關(guān)系是通過(guò)yii\db\ActiveQuery::via() 或 yii\db\ActiveQuery::viaTable()方法定義的, 就不能調(diào)用yii\db\ActiveQuery::inverseOf()方法了。

JOIN 類(lèi)型關(guān)聯(lián)查詢(xún)

使用關(guān)系數(shù)據(jù)庫(kù)時(shí),普遍要做的是連接多個(gè)表并明確地運(yùn)用各種 JOIN 查詢(xún)。 JOIN SQL語(yǔ)句的查詢(xún)條件和參數(shù),使用 yii\db\ActiveQuery::joinWith() 可以重用已定義關(guān)系并調(diào)用 而不是使用 yii\db\ActiveQuery::join() 來(lái)實(shí)現(xiàn)目標(biāo)。

// 查找所有訂單并以客戶(hù) ID 和訂單 ID 排序,并貪婪加載 "customer" 表$orders = Order::find()->joinWith('customer')->orderBy('customer.id, order.id')->all();
// 查找包括書(shū)籍的所有訂單,并以 `INNER JOIN` 的連接方式即時(shí)加載 "books" 表$orders = Order::find()->innerJoinWith('books')->all();

以上,方法 yii\db\ActiveQuery::innerJoinWith() 是訪(fǎng)問(wèn)?INNER JOIN?類(lèi)型的 yii\db\ActiveQuery::joinWith() 的快捷方式。

可以連接一個(gè)或多個(gè)關(guān)聯(lián)關(guān)系,可以自由使用查詢(xún)條件到關(guān)聯(lián)查詢(xún), 也可以嵌套連接關(guān)聯(lián)查詢(xún)。如:

// 連接多重關(guān)系// 找出24小時(shí)內(nèi)注冊(cè)客戶(hù)包含書(shū)籍的訂單$orders = Order::find()->innerJoinWith([
    'books',
    'customer' => function ($query) {
        $query->where('customer.created_at > ' . (time() - 24 * 3600));
    }
])->all();
// 連接嵌套關(guān)系:連接 books 表及其 author 列$orders = Order::find()->joinWith('books.author')->all();

代碼背后, Yii 先執(zhí)行一條 JOIN SQL 語(yǔ)句把滿(mǎn)足 JOIN SQL 語(yǔ)句查詢(xún)條件的主要模型查出, 然后為每個(gè)關(guān)系執(zhí)行一條查詢(xún)語(yǔ)句, bing填充相應(yīng)的關(guān)聯(lián)記錄。

yii\db\ActiveQuery::joinWith() 和 yii\db\ActiveQuery::with() 的區(qū)別是 前者連接主模型類(lèi)和關(guān)聯(lián)模型類(lèi)的數(shù)據(jù)表來(lái)檢索主模型, 而后者只查詢(xún)和檢索主模型類(lèi)。 檢索主模型

由于這個(gè)區(qū)別,你可以應(yīng)用只針對(duì)一條 JOIN SQL 語(yǔ)句起效的查詢(xún)條件。 如,通過(guò)關(guān)聯(lián)模型的查詢(xún)條件過(guò)濾主模型,如前例, 可以使用關(guān)聯(lián)表的列來(lái)挑選主模型數(shù)據(jù),

當(dāng)使用 yii\db\ActiveQuery::joinWith() 方法時(shí)可以響應(yīng)沒(méi)有歧義的列名。 In the above examples, we use?item.id?and?order.id?to disambiguate the?id?column references 因?yàn)橛唵伪砗晚?xiàng)目表都包括?id?列。

當(dāng)連接關(guān)聯(lián)關(guān)系時(shí),關(guān)聯(lián)關(guān)系默認(rèn)使用即時(shí)加載。你可以 通過(guò)傳參數(shù)?$eagerLoading?來(lái)決定在指定關(guān)聯(lián)查詢(xún)中是否使用即時(shí)加載。

默認(rèn) yii\db\ActiveQuery::joinWith() 使用左連接來(lái)連接關(guān)聯(lián)表。 你也可以傳?$joinType?參數(shù)來(lái)定制連接類(lèi)型。 你也可以使用 yii\db\ActiveQuery::innerJoinWith()。

以下是?INNER JOIN?的簡(jiǎn)短例子:

// 查找包括書(shū)籍的所有訂單,但 "books" 表不使用即時(shí)加載$orders = Order::find()->innerJoinWith('books', false)->all();
// 等價(jià)于:$orders = Order::find()->joinWith('books', false, 'INNER JOIN')->all();

有時(shí)連接兩個(gè)表時(shí),需要在關(guān)聯(lián)查詢(xún)的 ON 部分指定額外條件。 這可以通過(guò)調(diào)用 yii\db\ActiveQuery::onCondition() 方法實(shí)現(xiàn):

class User extends ActiveRecord{
    public function getBooks()
    {
        return $this->hasMany(Item::className(), ['owner_id' => 'id'])->onCondition(['category_id' => 1]);
    }
}

在上面, yii\db\ActiveRecord::hasMany() 方法回傳了一個(gè) yii\db\ActiveQuery 對(duì)象, 當(dāng)你用 yii\db\ActiveQuery::joinWith() 執(zhí)行一條查詢(xún)時(shí),取決于正被調(diào)用的是哪個(gè) yii\db\ActiveQuery::onCondition(), 返回?category_id?為 1 的 items

當(dāng)你用 yii\db\ActiveQuery::joinWith() 進(jìn)行一次查詢(xún)時(shí),“on-condition”條件會(huì)被放置在相應(yīng)查詢(xún)語(yǔ)句的 ON 部分, 如:

// SELECT user.* FROM user LEFT JOIN item ON item.owner_id=user.id AND category_id=1// SELECT * FROM item WHERE owner_id IN (...) AND category_id=1$users = User::find()->joinWith('books')->all();

注意:如果通過(guò) yii\db\ActiveQuery::with() 進(jìn)行貪婪加載或使用惰性加載的話(huà),則 on 條件會(huì)被放置在對(duì)應(yīng) SQL語(yǔ)句的?WHERE?部分。 因?yàn)?,此時(shí)此處并沒(méi)有發(fā)生 JOIN 查詢(xún)。比如:

// SELECT * FROM user WHERE id=10$user = User::findOne(10);
// SELECT * FROM item WHERE owner_id=10 AND category_id=1$books = $user->books;

關(guān)聯(lián)表操作

AR 提供了下面兩個(gè)方法用來(lái)建立和解除兩個(gè)關(guān)聯(lián)對(duì)象之間的關(guān)系:

  • yii\db\ActiveRecord::link()
  • yii\db\ActiveRecord::unlink()

例如,給定一個(gè)customer和order對(duì)象,我們可以通過(guò)下面的代碼使得customer對(duì)象擁有order對(duì)象:

$customer = Customer::findOne(1);
$order = new Order();
$order->subtotal = 100;
$customer->link('orders', $order);

yii\db\ActiveRecord::link() 調(diào)用上述將設(shè)置 customer_id 的順序是 $customer 的主鍵值,然后調(diào)用 yii\db\ActiveRecord::save() 要將順序保存到數(shù)據(jù)庫(kù)中。

作用域

當(dāng)你調(diào)用yii\db\ActiveRecord::find() 或 yii\db\ActiveRecord::findBySql()方法時(shí),將會(huì)返回一個(gè)yii\db\ActiveQuery實(shí)例。之后,你可以調(diào)用其他查詢(xún)方法,如 yii\db\ActiveQuery::where(),yii\db\ActiveQuery::orderBy(), 進(jìn)一步的指定查詢(xún)條件。

有時(shí)候你可能需要在不同的地方使用相同的查詢(xún)方法。如果出現(xiàn)這種情況,你應(yīng)該考慮定義所謂的作用域。作用域是本質(zhì)上要求一組的查詢(xún)方法來(lái)修改查詢(xún)對(duì)象的自定義查詢(xún)類(lèi)中定義的方法。 之后你就可以像使用普通方法一樣使用作用域。

只需兩步即可定義一個(gè)作用域。首先給你的model創(chuàng)建一個(gè)自定義的查詢(xún)類(lèi),在此類(lèi)中定義的所需的范圍方法。例如,給Comment模型創(chuàng)建一個(gè) CommentQuery類(lèi),然后在CommentQuery類(lèi)中定義一個(gè)active()的方法為作用域,像下面的代碼:

namespace app\models;

use yii\db\ActiveQuery;

class CommentQuery extends ActiveQuery{
    public function active($state = true)
    {
        $this->andWhere(['active' => $state]);
        return $this;
    }
}

重點(diǎn):

  1. 類(lèi)必須繼承 yii\db\ActiveQuery (或者是其他的 ActiveQuery ,比如 yii\mongodb\ActiveQuery)。
  2. 必須是一個(gè)public類(lèi)型的方法且必須返回 $this 實(shí)現(xiàn)鏈?zhǔn)讲僮???梢詡魅雲(yún)?shù)。
  3. 檢查 yii\db\ActiveQuery 對(duì)于修改查詢(xún)條件是非常有用的方法。

其次,覆蓋yii\db\ActiveRecord::find() 方法使其返回自定義的查詢(xún)對(duì)象而不是常規(guī)的yii\db\ActiveQuery。對(duì)于上述例子,你需要編寫(xiě)如下代碼:

namespace app\models;

use yii\db\ActiveRecord;

class Comment extends ActiveRecord{
    
    public static function find()
    {
        return new CommentQuery(get_called_class());
    }
}

就這樣,現(xiàn)在你可以使用自定義的作用域方法了:

$comments = Comment::find()->active()->all();
$inactiveComments = Comment::find()->active(false)->all();

你也能在定義的關(guān)聯(lián)里使用作用域方法,比如:

class Post extends \yii\db\ActiveRecord{
    public function getActiveComments()
    {
        return $this->hasMany(Comment::className(), ['post_id' => 'id'])->active();

    }
}

或者在執(zhí)行關(guān)聯(lián)查詢(xún)的時(shí)候使用(on-the-fly 是啥?):

$posts = Post::find()->with([
    'comments' => function($q) {
        $q->active();
    }
])->all();

默認(rèn)作用域

如果你之前用過(guò) Yii 1.1 就應(yīng)該知道默認(rèn)作用域的概念。一個(gè)默認(rèn)的作用域可以作用于所有查詢(xún)。你可以很容易的通過(guò)重寫(xiě)yii\db\ActiveRecord::find()方法來(lái)定義一個(gè)默認(rèn)作用域,例如:

public static function find(){
    return parent::find()->where(['deleted' => false]);
}

注意,你之后所有的查詢(xún)都不能用 yii\db\ActiveQuery::where(),但是可以用 yii\db\ActiveQuery::andWhere() 和 yii\db\ActiveQuery::orWhere(),他們不會(huì)覆蓋掉默認(rèn)作用域。(譯注:如果你要使用默認(rèn)作用域,就不能在 xxx::find()后使用where()方法,你必須使用andXXX()或者orXXX()系的方法,否則默認(rèn)作用域不會(huì)起效果,至于原因,打開(kāi)where()方法的代碼一看便知)

事務(wù)操作

當(dāng)執(zhí)行幾個(gè)相關(guān)聯(lián)的數(shù)據(jù)庫(kù)操作的時(shí)候

TODO: FIXME: WIP, TBD,?https://github.com/yiisoft/yii2/issues/226

, yii\db\ActiveRecord::afterSave(), yii\db\ActiveRecord::beforeDelete() and/or yii\db\ActiveRecord::afterDelete() 生命周期周期方法(life cycle methods 我覺(jué)得這句翻譯成“模板方法”會(huì)不會(huì)更好點(diǎn)?)。開(kāi)發(fā)者可以通過(guò)重寫(xiě)yii\db\ActiveRecord::save()方法然后在控制器里使用事務(wù)操作,嚴(yán)格地說(shuō)是似乎不是一個(gè)好的做法 (召回"瘦控制器 / 肥模型"基本規(guī)則)。

這些方法在這里(如果你不明白自己實(shí)際在干什么,請(qǐng)不要使用他們),Models:

class Feature extends \yii\db\ActiveRecord{
    // ...

    public function getProduct()
    {
        return $this->hasOne(Product::className(), ['id' => 'product_id']);
    }
}

class Product extends \yii\db\ActiveRecord{
    // ...

    public function getFeatures()
    {
        return $this->hasMany(Feature::className(), ['product_id' => 'id']);
    }
}

重寫(xiě) yii\db\ActiveRecord::save() 方法:

class ProductController extends \yii\web\Controller{
    public function actionCreate()
    {
        // FIXME: TODO: WIP, TBD
    }
}

(譯注:我覺(jué)得上面應(yīng)該是原手冊(cè)里的bug)

在控制器層使用事務(wù):

class ProductController extends \yii\web\Controller{
    public function actionCreate()
    {
        // FIXME: TODO: WIP, TBD
    }
}

作為這些脆弱方法的替代,你應(yīng)該使用原子操作方案特性。

class Feature extends \yii\db\ActiveRecord{
    // ...

    public function getProduct()
    {
        return $this->hasOne(Product::className(), ['product_id' => 'id']);
    }

    public function scenarios()
    {
        return [
            'userCreates' => [
                'attributes' => ['name', 'value'],
                'atomic' => [self::OP_INSERT],
            ],
        ];
    }
}

class Product extends \yii\db\ActiveRecord{
    // ...

    public function getFeatures()
    {
        return $this->hasMany(Feature::className(), ['id' => 'product_id']);
    }

    public function scenarios()
    {
        return [
            'userCreates' => [
                'attributes' => ['title', 'price'],
                'atomic' => [self::OP_INSERT],
            ],
        ];
    }

    public function afterValidate()
    {
        parent::afterValidate();
        // FIXME: TODO: WIP, TBD
    }

    public function afterSave($insert)
    {
        parent::afterSave($insert);
        if ($this->getScenario() === 'userCreates') {
            // FIXME: TODO: WIP, TBD
        }
    }
}

Controller里的代碼將變得很簡(jiǎn)潔:

class ProductController extends \yii\web\Controller{
    public function actionCreate()
    {
        // FIXME: TODO: WIP, TBD
    }
}

控制器非常簡(jiǎn)潔:

class ProductController extends \yii\web\Controller{
    public function actionCreate()
    {
        // FIXME: TODO: WIP, TBD
    }
}

樂(lè)觀(guān)鎖(Optimistic Locks)

TODO

被污染屬性

當(dāng)你調(diào)用yii\db\ActiveRecord::save()用于保存活動(dòng)記錄(Active Record)實(shí)例時(shí),只有被污染的屬性才會(huì)被保存。一個(gè)屬性是否認(rèn)定為被污染取決于它的值自從最后一次從數(shù)據(jù)庫(kù)加載或者最近一次保存到數(shù)據(jù)庫(kù)后到現(xiàn)在是否被修改過(guò)。注意:無(wú)論活動(dòng)記錄(Active Record)是否有被污染屬性,數(shù)據(jù)驗(yàn)證始終會(huì)執(zhí)行。

活動(dòng)記錄(Active Record)會(huì)自動(dòng)維護(hù)一個(gè)污染數(shù)據(jù)列表。它的工作方式是通過(guò)維護(hù)一個(gè)較舊屬性值版本,并且將它們與最新的進(jìn)行比較。你可以通過(guò)調(diào)用yii\db\ActiveRecord::getDirtyAttributes()來(lái)獲取當(dāng)前的污染屬性。你也可以調(diào)用yii\db\ActiveRecord::markAttributeDirty()來(lái)顯示的標(biāo)記一個(gè)屬性為污染屬性。

如果你對(duì)最近一次修改前的屬性值感興趣,你可以調(diào)用yii\db\ActiveRecord::getOldAttributes() 或 yii\db\ActiveRecord::getOldAttribute()。

另見(jiàn)

  • 模型(Model)
  • yii\db\ActiveRecord
Previous article: Next article: