|
|
|
@ -1,12 +1,12 @@
|
|
|
|
|
アクティブレコード |
|
|
|
|
================== |
|
|
|
|
アクティブ・レコード |
|
|
|
|
==================== |
|
|
|
|
|
|
|
|
|
[アクティブレコード](http://ja.wikipedia.org/wiki/Active_Record) は、データベースに保存されているデータにアクセスするために、オブジェクト指向のインタフェイスを提供するものです。 |
|
|
|
|
アクティブレコード・クラスはデータベース・テーブルと関連付けられます。 |
|
|
|
|
アクティブレコードのインスタンスはそのテーブルの行に対応し、アクティブレコードのインスタンスの *属性* がその行にある特定のカラムの値を表現します。 |
|
|
|
|
生の SQL 文を書く代りに、アクティブレコードの属性にアクセスしたり、アクティブレコードのメソッドを呼んだりして、データベース・テーブルに保存さているデータにアクセスしたり、データを操作したりします。 |
|
|
|
|
[アクティブ・レコード](http://ja.wikipedia.org/wiki/Active_Record) は、データベースに保存されているデータにアクセスするために、オブジェクト指向のインタフェイスを提供するものです。 |
|
|
|
|
アクティブ・レコード・クラスはデータベース・テーブルと関連付けられます。 |
|
|
|
|
アクティブ・レコードのインスタンスはそのテーブルの行に対応し、アクティブ・レコードのインスタンスの *属性* がその行にある特定のカラムの値を表現します。 |
|
|
|
|
生の SQL 文を書く代りに、アクティブ・レコードの属性にアクセスしたり、アクティブ・レコードのメソッドを呼んだりして、データベース・テーブルに保存さているデータにアクセスしたり、データを操作したりします。 |
|
|
|
|
|
|
|
|
|
例えば、`Customer` が `customer` テーブルに関連付けられたアクティブレコード・クラスであり、`name` が `customer` テーブルのカラムであると仮定しましょう。 |
|
|
|
|
例えば、`Customer` が `customer` テーブルに関連付けられたアクティブ・レコード・クラスであり、`name` が `customer` テーブルのカラムであると仮定しましょう。 |
|
|
|
|
`customer` テーブルに新しい行を挿入するために次のコードを書くことが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -24,7 +24,7 @@ $db->createCommand('INSERT INTO `customer` (`name`) VALUES (:name)', [
|
|
|
|
|
])->execute(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
Yii は次のリレーショナル・データベースに対して、アクティブレコードのサポートを提供しています。 |
|
|
|
|
Yii は次のリレーショナル・データベースに対して、アクティブ・レコードのサポートを提供しています。 |
|
|
|
|
|
|
|
|
|
* MySQL 4.1 以降: [[yii\db\ActiveRecord]] による。 |
|
|
|
|
* PostgreSQL 7.3 以降: [[yii\db\ActiveRecord]] による。 |
|
|
|
@ -36,22 +36,22 @@ Yii は次のリレーショナル・データベースに対して、アクテ
|
|
|
|
|
* Sphinx: [[yii\sphinx\ActiveRecord]] による。`yii2-sphinx` エクステンションが必要。 |
|
|
|
|
* ElasticSearch: [[yii\elasticsearch\ActiveRecord]] による。`yii2-elasticsearch` エクステンションが必要。 |
|
|
|
|
|
|
|
|
|
これらに加えて、Yii は次の NoSQL データベースに対しても、アクティブレコードの使用をサポートしています。 |
|
|
|
|
これらに加えて、Yii は次の NoSQL データベースに対しても、アクティブ・レコードの使用をサポートしています。 |
|
|
|
|
|
|
|
|
|
* Redis 2.6.12 以降: [[yii\redis\ActiveRecord]] による。`yii2-redis` エクステンションが必要。 |
|
|
|
|
* MongoDB 1.3.0 以降: [[yii\mongodb\ActiveRecord]] による。`yii2-mongodb` エクステンションが必要。 |
|
|
|
|
|
|
|
|
|
このチュートリアルでは、主としてリレーショナル・データベースのためのアクティブレコードの使用方法を説明します。 |
|
|
|
|
しかし、ここで説明するほとんどの内容は NoSQL データベースのためのアクティブレコードにも適用することが出来るものです。 |
|
|
|
|
このチュートリアルでは、主としてリレーショナル・データベースのためのアクティブ・レコードの使用方法を説明します。 |
|
|
|
|
しかし、ここで説明するほとんどの内容は NoSQL データベースのためのアクティブ・レコードにも適用することが出来るものです。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## アクティブレコード・クラスを宣言する <span id="declaring-ar-classes"></span> |
|
|
|
|
## アクティブ・レコード・クラスを宣言する <span id="declaring-ar-classes"></span> |
|
|
|
|
|
|
|
|
|
まずは、[[yii\db\ActiveRecord]] を拡張してアクティブレコード・クラスを宣言するところから始めましょう。 |
|
|
|
|
まずは、[[yii\db\ActiveRecord]] を拡張してアクティブ・レコード・クラスを宣言するところから始めましょう。 |
|
|
|
|
|
|
|
|
|
### テーブル名を設定する |
|
|
|
|
|
|
|
|
|
デフォルトでは、すべてのアクティブレコード・クラスはデータベース・テーブルと関連付けられます。 |
|
|
|
|
デフォルトでは、すべてのアクティブ・レコード・クラスはデータベース・テーブルと関連付けられます。 |
|
|
|
|
[[yii\db\ActiveRecord::tableName()|tableName()]] メソッドが、クラス名を [[yii\helpers\Inflector::camel2id()]] によって変換して、テーブル名を返します。 |
|
|
|
|
テーブル名がこの規約に従っていない場合は、このメソッドをオーバライドすることが出来ます。 |
|
|
|
|
|
|
|
|
@ -62,7 +62,7 @@ Yii は次のリレーショナル・データベースに対して、アクテ
|
|
|
|
|
例えば、`{{%post}}` は `{{tbl_post}}` となります。 |
|
|
|
|
テーブル名を囲む二重波括弧は、[テーブル名を囲む引用符号](db-dao.md#quoting-table-and-column-names) となります。 |
|
|
|
|
|
|
|
|
|
次の例では、`customer` というデータベース・テーブルのための `Customer` という名前のアクティブレコード・クラスを宣言しています。 |
|
|
|
|
次の例では、`customer` というデータベース・テーブルのための `Customer` という名前のアクティブ・レコード・クラスを宣言しています。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
namespace app\models; |
|
|
|
@ -75,7 +75,7 @@ class Customer extends ActiveRecord
|
|
|
|
|
const STATUS_ACTIVE = 1; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* @return string このアクティブレコード・クラスと関連付けられるテーブルの名前 |
|
|
|
|
* @return string このアクティブ・レコード・クラスと関連付けられるテーブルの名前 |
|
|
|
|
*/ |
|
|
|
|
public static function tableName() |
|
|
|
|
{ |
|
|
|
@ -84,17 +84,17 @@ class Customer extends ActiveRecord
|
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
### アクティブレコードは「モデル」と呼ばれる |
|
|
|
|
### アクティブ・レコードは「モデル」と呼ばれる |
|
|
|
|
|
|
|
|
|
アクティブレコードのインスタンスは [モデル](structure-models.md) であると見なされます。 |
|
|
|
|
この理由により、私たちは通常 `app\models` 名前空間 (あるいはモデル・クラスを保管するための他の名前空間) の下にアクティブレコード・クラスを置きます。 |
|
|
|
|
アクティブ・レコードのインスタンスは [モデル](structure-models.md) であると見なされます。 |
|
|
|
|
この理由により、私たちは通常 `app\models` 名前空間 (あるいはモデル・クラスを保管するための他の名前空間) の下にアクティブ・レコード・クラスを置きます。 |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord]] は [[yii\base\Model]] から拡張していますので、属性、検証規則、データのシリアル化など、[モデル](structure-models.md) が持つ *全ての* 機能を継承しています。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## データベースに接続する <span id="db-connection"></span> |
|
|
|
|
|
|
|
|
|
デフォルトでは、アクティブレコードは、`db` [アプリケーション・コンポーネント](structure-application-components.md) を [[yii\db\Connection|DB 接続]] として使用して、データベースのデータにアクセスしたり操作したりします。 |
|
|
|
|
デフォルトでは、アクティブ・レコードは、`db` [アプリケーション・コンポーネント](structure-application-components.md) を [[yii\db\Connection|DB 接続]] として使用して、データベースのデータにアクセスしたり操作したりします。 |
|
|
|
|
[データベース・アクセス・オブジェクト](db-dao.md) で説明したように、次のようにして、アプリケーションの構成情報ファイルの中で `db` コンポーネントを構成することが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -127,12 +127,12 @@ class Customer extends ActiveRecord
|
|
|
|
|
|
|
|
|
|
## データをクエリする <span id="querying-data"></span> |
|
|
|
|
|
|
|
|
|
アクティブレコード・クラスを宣言した後、それを使って対応するデータベース・テーブルからデータをクエリすることが出来ます。 |
|
|
|
|
アクティブ・レコード・クラスを宣言した後、それを使って対応するデータベース・テーブルからデータをクエリすることが出来ます。 |
|
|
|
|
このプロセスは通常次の三つのステップを踏みます。 |
|
|
|
|
|
|
|
|
|
1. [[yii\db\ActiveRecord::find()]] メソッドを呼んで、新しいクエリ・オブジェクトを作成する。 |
|
|
|
|
2. [クエリ構築メソッド](db-query-builder.md#building-queries) を呼んで、クエリ・オブジェクトを構築する。 |
|
|
|
|
3. [クエリ・メソッド](db-query-builder.md#query-methods) を呼んで、アクティブレコードのインスタンスの形でデータを取得する。 |
|
|
|
|
3. [クエリ・メソッド](db-query-builder.md#query-methods) を呼んで、アクティブ・レコードのインスタンスの形でデータを取得する。 |
|
|
|
|
|
|
|
|
|
ご覧のように、このプロセスは [クエリ・ビルダ](db-query-builder.md) による手続きと非常によく似ています。 |
|
|
|
|
唯一の違いは、`new` 演算子を使ってクエリ・オブジェクトを生成する代りに、[[yii\db\ActiveQuery]] クラスであるクエリ・オブジェクトを返す [[yii\db\ActiveRecord::find()]] を呼ぶ、という点です。 |
|
|
|
@ -173,8 +173,8 @@ $customers = Customer::find()
|
|
|
|
|
|
|
|
|
|
プライマリ・キーの値や一群のカラムの値でクエリをすることはよく行われる仕事ですので、Yii はこの目的のために、二つのショートカット・メソッドを提供しています。 |
|
|
|
|
|
|
|
|
|
- [[yii\db\ActiveRecord::findOne()]]: クエリ結果の最初の行を一つのアクティブレコード・インスタンスに投入して返す。 |
|
|
|
|
- [[yii\db\ActiveRecord::findAll()]]: *全ての* クエリ結果をアクティブレコード・インスタンスの配列に投入して返す。 |
|
|
|
|
- [[yii\db\ActiveRecord::findOne()]]: クエリ結果の最初の行を一つのアクティブ・レコード・インスタンスに投入して返す。 |
|
|
|
|
- [[yii\db\ActiveRecord::findAll()]]: *全ての* クエリ結果をアクティブ・レコード・インスタンスの配列に投入して返す。 |
|
|
|
|
|
|
|
|
|
どちらのメソッドも、次のパラメータ形式のどれかを取ることが出来ます。 |
|
|
|
|
|
|
|
|
@ -234,7 +234,7 @@ $customers = Customer::findAll([
|
|
|
|
|
あなたのクエリが多数のデータ行を返すかもしれない場合は、パフォーマンスを向上させるために、`limit(1)` を明示的に呼ぶべきです。 |
|
|
|
|
例えば `Customer::find()->limit(1)->one()` のように。 |
|
|
|
|
|
|
|
|
|
クエリ構築メソッドを使う以外に、生の SQL を書いてデータをクエリして結果をアクティブレコード・オブジェクトに投入することも出来ます。 |
|
|
|
|
クエリ構築メソッドを使う以外に、生の SQL を書いてデータをクエリして結果をアクティブ・レコード・オブジェクトに投入することも出来ます。 |
|
|
|
|
そうするためには [[yii\db\ActiveRecord::findBySql()]] メソッドを呼ぶことが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -247,9 +247,9 @@ $customers = Customer::findBySql($sql, [':status' => Customer::STATUS_INACTIVE])
|
|
|
|
|
|
|
|
|
|
## データにアクセスする <span id="accessing-data"></span> |
|
|
|
|
|
|
|
|
|
既に述べたように、データベースから取得されたデータはアクティブレコードのインスタンスに投入されます。 |
|
|
|
|
そして、クエリ結果の各行がアクティブレコードの一つのインスタンスに対応します。 |
|
|
|
|
アクティブレコード・インスタンスの属性にアクセスすることによって、カラムの値にアクセスすることが出来ます。 |
|
|
|
|
既に述べたように、データベースから取得されたデータはアクティブ・レコードのインスタンスに投入されます。 |
|
|
|
|
そして、クエリ結果の各行がアクティブ・レコードの一つのインスタンスに対応します。 |
|
|
|
|
アクティブ・レコード・インスタンスの属性にアクセスすることによって、カラムの値にアクセスすることが出来ます。 |
|
|
|
|
例えば、 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -259,11 +259,11 @@ $id = $customer->id;
|
|
|
|
|
$email = $customer->email; |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
> Note: アクティブレコードの属性の名前は、関連付けられたテーブルのカラムの名前に従って、大文字と小文字を区別して名付けられます。 |
|
|
|
|
Yii は、関連付けられたテーブルの全てのカラムに対して、アクティブレコードの属性を自動的に定義します。 |
|
|
|
|
> Note: アクティブ・レコードの属性の名前は、関連付けられたテーブルのカラムの名前に従って、大文字と小文字を区別して名付けられます。 |
|
|
|
|
Yii は、関連付けられたテーブルの全てのカラムに対して、アクティブ・レコードの属性を自動的に定義します。 |
|
|
|
|
これらの属性は、すべて、再宣言してはいけません。 |
|
|
|
|
|
|
|
|
|
アクティブレコードの属性はテーブルのカラムに従って命名されるため、テーブルのカラム名がアンダースコアで単語を分ける方法で命名されている場合は、`$customer->first_name` のような属性名を使って PHP コードを書くことになります。 |
|
|
|
|
アクティブ・レコードの属性はテーブルのカラムに従って命名されるため、テーブルのカラム名がアンダースコアで単語を分ける方法で命名されている場合は、`$customer->first_name` のような属性名を使って PHP コードを書くことになります。 |
|
|
|
|
コード・スタイルの一貫性が気になるのであれば、テーブルのカラム名を (例えば camelCase を使う名前に) 変更しなければなりません。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -271,7 +271,7 @@ $email = $customer->email;
|
|
|
|
|
|
|
|
|
|
入力または表示されるデータの形式が、データベースにデータを保存するときに使われるものと異なる場合がよくあります。 |
|
|
|
|
例えば、データベースでは顧客の誕生日を UNIX タイムスタンプで保存している (まあ、あまり良い設計ではありませんが) けれども、ほとんどの場合において誕生日を `'YYYY/MM/DD'` という形式の文字列として操作したい、というような場合です。 |
|
|
|
|
この目的を達するために、次のように、`Customer` アクティブレコード・クラスにおいて *データ変換* メソッドを定義することが出来ます。 |
|
|
|
|
この目的を達するために、次のように、`Customer` アクティブ・レコード・クラスにおいて *データ変換* メソッドを定義することが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
class Customer extends ActiveRecord |
|
|
|
@ -299,7 +299,7 @@ class Customer extends ActiveRecord
|
|
|
|
|
|
|
|
|
|
### データを配列に取得する <span id="data-in-arrays"></span> |
|
|
|
|
|
|
|
|
|
データをアクティブレコード・オブジェクトの形で取得するのは便利であり柔軟ですが、大きなメモリ使用量を要するために、大量のデータを取得しなければならない場合は、必ずしも望ましい方法ではありません。 |
|
|
|
|
データをアクティブ・レコード・オブジェクトの形で取得するのは便利であり柔軟ですが、大きなメモリ使用量を要するために、大量のデータを取得しなければならない場合は、必ずしも望ましい方法ではありません。 |
|
|
|
|
そういう場合は、クエリ・メソッドを実行する前に [[yii\db\ActiveQuery::asArray()|asArray()]] を呼ぶことによって、PHP 配列を使ってデータを取得することが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -310,9 +310,9 @@ $customers = Customer::find()
|
|
|
|
|
->all(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
> Note: このメソッドはメモリを節約してパフォーマンスを向上させますが、低レベルの DB 抽象レイヤに近いものであり、あなたはアクティブレコードの機能のほとんどを失うことになります。 |
|
|
|
|
> Note: このメソッドはメモリを節約してパフォーマンスを向上させますが、低レベルの DB 抽象レイヤに近いものであり、あなたはアクティブ・レコードの機能のほとんどを失うことになります。 |
|
|
|
|
非常に重要な違いが、カラムの値のデータ型に現れます。 |
|
|
|
|
アクティブレコード・インスタンスとしてデータを返す場合、カラムの値は実際のカラムの型に従って自動的に型キャストされます。 |
|
|
|
|
アクティブ・レコード・インスタンスとしてデータを返す場合、カラムの値は実際のカラムの型に従って自動的に型キャストされます。 |
|
|
|
|
一方、配列としてデータを返す場合は、実際のカラムの型に関係なく、カラムの値は文字列になります。 |
|
|
|
|
なぜなら、何も処理をしない場合の PDO の結果は文字列だからです。 |
|
|
|
|
|
|
|
|
@ -320,7 +320,7 @@ $customers = Customer::find()
|
|
|
|
|
### データをバッチ・モードで取得する <span id="data-in-batches"></span> |
|
|
|
|
|
|
|
|
|
[クエリ・ビルダ](db-query-builder.md) において、大量のデータをデータベースから検索する場合に、メモリ使用量を最小化するために *バッチ・クエリ* を使うことが出来るということを説明しました。 |
|
|
|
|
おなじテクニックをアクティブレコードでも使うことが出来ます。 |
|
|
|
|
おなじテクニックをアクティブ・レコードでも使うことが出来ます。 |
|
|
|
|
例えば、 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -341,10 +341,10 @@ foreach (Customer::find()->with('orders')->each() as $customer) {
|
|
|
|
|
|
|
|
|
|
## データを保存する <span id="inserting-updating-data"></span> |
|
|
|
|
|
|
|
|
|
アクティブレコードを使えば、次のステップを踏んで簡単にデータをデータベースに保存することが出来ます。 |
|
|
|
|
アクティブ・レコードを使えば、次のステップを踏んで簡単にデータをデータベースに保存することが出来ます。 |
|
|
|
|
|
|
|
|
|
1. アクティブレコードのインスタンスを準備する |
|
|
|
|
2. アクティブレコードの属性に新しい値を割り当てる |
|
|
|
|
1. アクティブ・レコードのインスタンスを準備する |
|
|
|
|
2. アクティブ・レコードの属性に新しい値を割り当てる |
|
|
|
|
3. [[yii\db\ActiveRecord::save()]] を呼んでデータをデータベースに保存する |
|
|
|
|
|
|
|
|
|
例えば、 |
|
|
|
@ -361,11 +361,11 @@ $customer->email = 'james@newexample.com';
|
|
|
|
|
$customer->save(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::save()|save()]] メソッドは、アクティブレコード・インスタンスの状態に従って、データ行を挿入するか、または、更新することが出来ます。 |
|
|
|
|
[[yii\db\ActiveRecord::save()|save()]] メソッドは、アクティブ・レコード・インスタンスの状態に従って、データ行を挿入するか、または、更新することが出来ます。 |
|
|
|
|
インスタンスが `new` 演算子によって新しく作成されたものである場合は、[[yii\db\ActiveRecord::save()|save()]] を呼び出すと、新しい行が挿入されます。 |
|
|
|
|
インスタンスがクエリ・メソッドの結果である場合は、[[yii\db\ActiveRecord::save()|save()]] を呼び出すと、そのインスタンスと関連付けられた行が更新されます。 |
|
|
|
|
|
|
|
|
|
アクティブレコード・インスタンスの二つの状態は、その [[yii\db\ActiveRecord::isNewRecord|isNewRecord]] プロパティの値をチェックすることによって区別することが出来ます。 |
|
|
|
|
アクティブ・レコード・インスタンスの二つの状態は、その [[yii\db\ActiveRecord::isNewRecord|isNewRecord]] プロパティの値をチェックすることによって区別することが出来ます。 |
|
|
|
|
下記のように、このプロパティは [[yii\db\ActiveRecord::save()|save()]] によっても内部的に使用されています。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -396,8 +396,8 @@ public function save($runValidation = true, $attributeNames = null)
|
|
|
|
|
|
|
|
|
|
### 一括代入 <span id="massive-assignment"></span> |
|
|
|
|
|
|
|
|
|
通常の [モデル](structure-models.md) と同じように、アクティブレコードのインスタンスも [一括代入機能](structure-models.md#massive-assignment) を享受することが出来ます。 |
|
|
|
|
この機能を使うと、下記で示されているように、一つの PHP 文で、アクティブレコード・インスタンスの複数の属性に値を割り当てることが出来ます。 |
|
|
|
|
通常の [モデル](structure-models.md) と同じように、アクティブ・レコードのインスタンスも [一括代入機能](structure-models.md#massive-assignment) を享受することが出来ます。 |
|
|
|
|
この機能を使うと、下記で示されているように、一つの PHP 文で、アクティブ・レコード・インスタンスの複数の属性に値を割り当てることが出来ます。 |
|
|
|
|
ただし、[安全な属性](structure-models.md#safe-attributes) だけが一括代入が可能であることを記憶しておいてください。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -433,11 +433,11 @@ $post->updateCounters(['view_count' => 1]);
|
|
|
|
|
|
|
|
|
|
### ダーティな属性 <span id="dirty-attributes"></span> |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::save()|save()]] を呼んでアクティブレコード・インスタンスを保存すると、*ダーティな属性* だけが保存されます。 |
|
|
|
|
[[yii\db\ActiveRecord::save()|save()]] を呼んでアクティブ・レコード・インスタンスを保存すると、*ダーティな属性* だけが保存されます。 |
|
|
|
|
属性は、DB からロードされた後、または、最後に保存された後にその値が変更されると、*ダーティ* であると見なされます。 |
|
|
|
|
ただし、データ検証は、アクティブレコード・インスタンスがダーティな属性を持っているかどうかに関係なく実施されることに注意してください。 |
|
|
|
|
ただし、データ検証は、アクティブ・レコード・インスタンスがダーティな属性を持っているかどうかに関係なく実施されることに注意してください。 |
|
|
|
|
|
|
|
|
|
アクティブレコードはダーティな属性のリストを自動的に保守します。 |
|
|
|
|
アクティブ・レコードはダーティな属性のリストを自動的に保守します。 |
|
|
|
|
そうするために、一つ前のバージョンの属性値を保持して、最新のバージョンと比較します。 |
|
|
|
|
[[yii\db\ActiveRecord::getDirtyAttributes()]] を呼ぶと、現在ダーティである属性を取得することが出来ます。 |
|
|
|
|
また、[[yii\db\ActiveRecord::markAttributeDirty()]] を呼んで、ある属性をダーティであると明示的にマークすることも出来ます。 |
|
|
|
@ -454,8 +454,8 @@ $post->updateCounters(['view_count' => 1]);
|
|
|
|
|
### デフォルト属性値 <span id="default-attribute-values"></span> |
|
|
|
|
|
|
|
|
|
あなたのテーブルのカラムの中には、データベースでデフォルト値が定義されているものがあるかも知れません。 |
|
|
|
|
そして、場合によっては、アクティブレコード・インスタンスのウェブ・フォームに、そういうデフォルト値をあらかじめ投入したいことがあるでしょう。 |
|
|
|
|
同じデフォルト値を繰り返して書くことを避けるために、[[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] を呼んで、DB で定義されたデフォルト値を対応するアクティブレコードの属性に投入することが出来ます。 |
|
|
|
|
そして、場合によっては、アクティブ・レコード・インスタンスのウェブ・フォームに、そういうデフォルト値をあらかじめ投入したいことがあるでしょう。 |
|
|
|
|
同じデフォルト値を繰り返して書くことを避けるために、[[yii\db\ActiveRecord::loadDefaultValues()|loadDefaultValues()]] を呼んで、DB で定義されたデフォルト値を対応するアクティブ・レコードの属性に投入することが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$customer = new Customer(); |
|
|
|
@ -468,7 +468,7 @@ $customer->loadDefaultValues();
|
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord]] は、クエリの結果を投入されるときに、[データベース・テーブル・スキーマ](db-dao.md#database-schema) |
|
|
|
|
からの情報を使って、自動的な型キャストを実行します。これによって、整数として宣言されているテーブルカラムから取得されるデータを |
|
|
|
|
アクティブレコードのインスタンスでも PHP の integer として投入し、 |
|
|
|
|
アクティブ・レコードのインスタンスでも PHP の integer として投入し、 |
|
|
|
|
真偽値として宣言されているデータを boolean として投入することが出来るようになっています。 |
|
|
|
|
しかしながら、型キャストのメカニズムには、いくつかの制約があります。 |
|
|
|
|
|
|
|
|
@ -477,22 +477,22 @@ $customer->loadDefaultValues();
|
|
|
|
|
'unsigned integer' または 'big integer' として宣言されたカラムの値は、64-bit オペレーティングシステムでのみ PHP の integer に変換されます。 |
|
|
|
|
32-bit オペレーティングシステムでは、文字列として表されます。 |
|
|
|
|
|
|
|
|
|
属性の型キャストは、アクティブレコードのインスタンスにクエリの結果から値を投入するときだけしか実行されないことに注意してください。 |
|
|
|
|
属性の型キャストは、アクティブ・レコードのインスタンスにクエリの結果から値を投入するときだけしか実行されないことに注意してください。 |
|
|
|
|
HTTP リクエストから値をロードしたり、プロパティにアクセスして直接に値を設定したりするときには、自動的な変換は行われません。 |
|
|
|
|
また、アクティブレコードのデータ保存のための SQL 文を準備する際にもテーブル・スキーマが使用されて、値が正しい型でクエリにバインドされることを保証します。 |
|
|
|
|
しかし、アクティブレコードのインスタンスの属性値は保存の過程において変換されることはありません。 |
|
|
|
|
また、アクティブ・レコードのデータ保存のための SQL 文を準備する際にもテーブル・スキーマが使用されて、値が正しい型でクエリにバインドされることを保証します。 |
|
|
|
|
しかし、アクティブ・レコードのインスタンスの属性値は保存の過程において変換されることはありません。 |
|
|
|
|
|
|
|
|
|
> Tip: アクティブレコードの検証や保存の際の属性型キャストを楽にするために |
|
|
|
|
> Tip: アクティブ・レコードの検証や保存の際の属性型キャストを楽にするために |
|
|
|
|
[[yii\behaviors\AttributeTypecastBehavior]] を使うことが出来ます。 |
|
|
|
|
|
|
|
|
|
2.0.14 以降、Yii のアクティブレコードは、JSON や多次元配列のような複雑な型をサポートしています。 |
|
|
|
|
2.0.14 以降、Yii のアクティブ・レコードは、JSON や多次元配列のような複雑な型をサポートしています。 |
|
|
|
|
|
|
|
|
|
#### MySQL および PostgreSQL における JSON |
|
|
|
|
|
|
|
|
|
データが取得された後、JSON カラムの値は標準的な JSON デコード規則に従って、 |
|
|
|
|
自動的に JSON からデコードされます。 |
|
|
|
|
|
|
|
|
|
アクティブレコードは、属性値を JSON カラムに保存するために [[yii\db\JsonExpression|JsonExpression]] |
|
|
|
|
アクティブ・レコードは、属性値を JSON カラムに保存するために [[yii\db\JsonExpression|JsonExpression]] |
|
|
|
|
オブジェクトを自動的に生成します。このオブジェクトが [クエリ・ビルダ](db-query-builder.md) レベルで JSON 文字列にエンコードされます。 |
|
|
|
|
|
|
|
|
|
#### PostgreSQL における配列 |
|
|
|
@ -501,7 +501,7 @@ HTTP リクエストから値をロードしたり、プロパティにアクセ
|
|
|
|
|
このオブジェクトは PHP の `ArrayAccess` インタフェイスを実装しているため、これを配列として使うこと事が出来ます。 |
|
|
|
|
また、`->getValue()` を呼んで配列そのものを取得することも出来ます。 |
|
|
|
|
|
|
|
|
|
アクティブレコードは、属性値を配列カラムに保存するために [[yii\db\ArrayExpression|ArrayExpression]] |
|
|
|
|
アクティブ・レコードは、属性値を配列カラムに保存するために [[yii\db\ArrayExpression|ArrayExpression]] |
|
|
|
|
オブジェクトを生成します。このオブジェクトが [クエリ・ビルダ](db-query-builder.md) のレベルで配列を表す PgSQL 文字列にエンコードされます。 |
|
|
|
|
|
|
|
|
|
JSON カラムに対して条件を使用することも出来ます。 |
|
|
|
@ -515,7 +515,7 @@ $query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])
|
|
|
|
|
|
|
|
|
|
### 複数の行を更新する <span id="updating-multiple-rows"></span> |
|
|
|
|
|
|
|
|
|
上述のメソッドは、すべて、個別のアクティブレコード・インスタンスに対して作用し、個別のテーブル行を挿入したり更新したりするものです。 |
|
|
|
|
上述のメソッドは、すべて、個別のアクティブ・レコード・インスタンスに対して作用し、個別のテーブル行を挿入したり更新したりするものです。 |
|
|
|
|
複数の行を同時に更新するためには、代りに、スタティックなメソッドである [[yii\db\ActiveRecord::updateAll()|updateAll()]] を呼ばなければなりません。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -533,7 +533,7 @@ Customer::updateAllCounters(['age' => 1]);
|
|
|
|
|
|
|
|
|
|
## データを削除する <span id="deleting-data"></span> |
|
|
|
|
|
|
|
|
|
一行のデータを削除するためには、最初にその行に対応するアクティブレコード・インスタンスを取得して、次に [[yii\db\ActiveRecord::delete()]] メソッドを呼びます。 |
|
|
|
|
一行のデータを削除するためには、最初にその行に対応するアクティブ・レコード・インスタンスを取得して、次に [[yii\db\ActiveRecord::delete()]] メソッドを呼びます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$customer = Customer::findOne(123); |
|
|
|
@ -550,37 +550,37 @@ Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]);
|
|
|
|
|
なぜなら、条件の指定を間違うと、あなたのテーブルからすべてのデータを完全に消し去ってしまうことになるからです。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## アクティブレコードのライフ・サイクル <span id="ar-life-cycles"></span> |
|
|
|
|
## アクティブ・レコードのライフサイクル <span id="ar-life-cycles"></span> |
|
|
|
|
|
|
|
|
|
アクティブレコードがさまざまな目的で使用される場合のそれぞれのライフ・サイクルを理解しておくことは重要なことです。 |
|
|
|
|
それぞれのライフ・サイクルにおいては、特定の一続きのメソッドが呼び出されます。 |
|
|
|
|
そして、これらのメソッドをオーバーライドして、ライフ・サイクルをカスタマイズするチャンスを得ることが出来ます。 |
|
|
|
|
また、ライフ・サイクルの中でトリガされる特定のアクティブレコード・イベントに反応して、あなたのカスタム・コードを挿入することも出来ます。 |
|
|
|
|
これらのイベントが特に役に立つのは、アクティブレコードのライフ・サイクルをカスタマイズする必要のあるアクティブレコード・[ビヘイビア](concept-behaviors.md) を開発する際です。 |
|
|
|
|
アクティブ・レコードがさまざまな目的で使用される場合のそれぞれのライフサイクルを理解しておくことは重要なことです。 |
|
|
|
|
それぞれのライフサイクルにおいては、特定の一続きのメソッドが呼び出されます。 |
|
|
|
|
そして、これらのメソッドをオーバーライドして、ライフサイクルをカスタマイズするチャンスを得ることが出来ます。 |
|
|
|
|
また、ライフサイクルの中でトリガされる特定のアクティブ・レコード・イベントに反応して、あなたのカスタム・コードを挿入することも出来ます。 |
|
|
|
|
これらのイベントが特に役に立つのは、アクティブ・レコードのライフサイクルをカスタマイズする必要のあるアクティブ・レコード・[ビヘイビア](concept-behaviors.md) を開発する際です。 |
|
|
|
|
|
|
|
|
|
次に、さまざまなアクティブレコードのライフ・サイクルと、そのライフ・サイクルに含まれるメソッドやイベントを要約します。 |
|
|
|
|
次に、さまざまなアクティブ・レコードのライフサイクルと、そのライフサイクルに含まれるメソッドやイベントを要約します。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 新しいインスタンスのライフ・サイクル <span id="new-instance-life-cycle"></span> |
|
|
|
|
### 新しいインスタンスのライフサイクル <span id="new-instance-life-cycle"></span> |
|
|
|
|
|
|
|
|
|
`new` 演算子によって新しいアクティブレコード・インスタンスを作成する場合は、次のライフ・サイクルを経ます。 |
|
|
|
|
`new` 演算子によって新しいアクティブ・レコード・インスタンスを作成する場合は、次のライフサイクルを経ます。 |
|
|
|
|
|
|
|
|
|
1. クラスのコンストラクタ。 |
|
|
|
|
2. [[yii\db\ActiveRecord::init()|init()]]: [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] イベントをトリガ。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### データをクエリする際のライフ・サイクル <span id="querying-data-life-cycle"></span> |
|
|
|
|
### データをクエリする際のライフサイクル <span id="querying-data-life-cycle"></span> |
|
|
|
|
|
|
|
|
|
[クエリ・メソッド](#querying-data) のどれか一つによってデータをクエリする場合は、新しくデータを投入されるアクティブレコードは次のライフ・サイクルを経ます。 |
|
|
|
|
[クエリ・メソッド](#querying-data) のどれか一つによってデータをクエリする場合は、新しくデータを投入されるアクティブ・レコードは次のライフサイクルを経ます。 |
|
|
|
|
|
|
|
|
|
1. クラスのコンストラクタ。 |
|
|
|
|
2. [[yii\db\ActiveRecord::init()|init()]]: [[yii\db\ActiveRecord::EVENT_INIT|EVENT_INIT]] イベントをトリガ。 |
|
|
|
|
3. [[yii\db\ActiveRecord::afterFind()|afterFind()]]: [[yii\db\ActiveRecord::EVENT_AFTER_FIND|EVENT_AFTER_FIND]] イベントをトリガ。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### データを保存する際のライフ・サイクル <span id="saving-data-life-cycle"></span> |
|
|
|
|
### データを保存する際のライフサイクル <span id="saving-data-life-cycle"></span> |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::save()|save()]] を呼んでアクティブレコード・インスタンスを挿入または更新する場合は、次のライフ・サイクルを経ます。 |
|
|
|
|
[[yii\db\ActiveRecord::save()|save()]] を呼んでアクティブ・レコード・インスタンスを挿入または更新する場合は、次のライフサイクルを経ます。 |
|
|
|
|
|
|
|
|
|
1. [[yii\db\ActiveRecord::beforeValidate()|beforeValidate()]]: [[yii\db\ActiveRecord::EVENT_BEFORE_VALIDATE|EVENT_BEFORE_VALIDATE]] イベントをトリガ。 |
|
|
|
|
このメソッドが `false` を返すか、[[yii\base\ModelEvent::isValid]] が `false` であった場合、残りのステップはスキップされる。 |
|
|
|
@ -592,9 +592,9 @@ Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]);
|
|
|
|
|
6. [[yii\db\ActiveRecord::afterSave()|afterSave()]]: [[yii\db\ActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] または [[yii\db\ActiveRecord::EVENT_AFTER_UPDATE|EVENT_AFTER_UPDATE]] イベントをトリガ。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### データを削除する際のライフ・サイクル <span id="deleting-data-life-cycle"></span> |
|
|
|
|
### データを削除する際のライフサイクル <span id="deleting-data-life-cycle"></span> |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::delete()|delete()]] を呼んでアクティブレコード・インスタンスを削除する際は、次のライフ・サイクルを経ます。 |
|
|
|
|
[[yii\db\ActiveRecord::delete()|delete()]] を呼んでアクティブ・レコード・インスタンスを削除する際は、次のライフサイクルを経ます。 |
|
|
|
|
|
|
|
|
|
1. [[yii\db\ActiveRecord::beforeDelete()|beforeDelete()]]: [[yii\db\ActiveRecord::EVENT_BEFORE_DELETE|EVENT_BEFORE_DELETE]] イベントをトリガ。 |
|
|
|
|
このメソッドが `false` を返すか、[[yii\base\ModelEvent::isValid]] が `false` であった場合は、残りのステップはスキップされる。 |
|
|
|
@ -602,7 +602,7 @@ Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]);
|
|
|
|
|
3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] イベントをトリガ。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
> Note: 次のメソッドを呼んだ場合は、いずれの場合も、上記のライフ・サイクルのどれかを開始させることはありません。 |
|
|
|
|
> Note: 次のメソッドを呼んだ場合は、いずれの場合も、上記のライフサイクルのどれかを開始させることはありません。 |
|
|
|
|
> これらのメソッドは、レコード単位ではなく、データベース上で直接に動作するためです。 |
|
|
|
|
> |
|
|
|
|
> - [[yii\db\ActiveRecord::updateAll()]] |
|
|
|
@ -610,17 +610,17 @@ Customer::deleteAll(['status' => Customer::STATUS_INACTIVE]);
|
|
|
|
|
> - [[yii\db\ActiveRecord::updateCounters()]] |
|
|
|
|
> - [[yii\db\ActiveRecord::updateAllCounters()]] |
|
|
|
|
|
|
|
|
|
### データをリフレッシュする際のライフ・サイクル <span id="refreshing-data-life-cycle"></span> |
|
|
|
|
### データをリフレッシュする際のライフサイクル <span id="refreshing-data-life-cycle"></span> |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::refresh()|refresh()]] を呼んでアクティブレコード・インスタンスをリフレッシュする際は、リフレッシュが成功してメソッドが `true` を返すと |
|
|
|
|
[[yii\db\ActiveRecord::refresh()|refresh()]] を呼んでアクティブ・レコード・インスタンスをリフレッシュする際は、リフレッシュが成功してメソッドが `true` を返すと |
|
|
|
|
[[yii\db\ActiveRecord::EVENT_AFTER_REFRESH|EVENT_AFTER_REFRESH]] イベントがトリガされます。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## トランザクションを扱う <span id="transactional-operations"></span> |
|
|
|
|
|
|
|
|
|
アクティブレコードを扱う際には、二つの方法で [トランザクション](db-dao.md#performing-transactions) を処理することができます。 |
|
|
|
|
アクティブ・レコードを扱う際には、二つの方法で [トランザクション](db-dao.md#performing-transactions) を処理することができます。 |
|
|
|
|
|
|
|
|
|
最初の方法は、次に示すように、アクティブレコードのメソッドの呼び出しを明示的にトランザクションのブロックで囲む方法です。 |
|
|
|
|
最初の方法は、次に示すように、アクティブ・レコードのメソッドの呼び出しを明示的にトランザクションのブロックで囲む方法です。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$customer = Customer::findOne(123); |
|
|
|
@ -696,12 +696,12 @@ class Post extends \yii\db\ActiveRecord
|
|
|
|
|
|
|
|
|
|
楽観的ロックを使用するためには、次のようにします。 |
|
|
|
|
|
|
|
|
|
1. アクティブレコード・クラスと関連付けられている DB テーブルに、各行のバージョン番号を保存するカラムを作成します。 |
|
|
|
|
1. アクティブ・レコード・クラスと関連付けられている DB テーブルに、各行のバージョン番号を保存するカラムを作成します。 |
|
|
|
|
カラムは長倍精度整数 (big integer) タイプでなければなりません (MySQL では `BIGINT DEFAULT 0` です)。 |
|
|
|
|
2. [[yii\db\ActiveRecord::optimisticLock()]] メソッドをオーバーライドして、このカラムの名前を返すようにします。 |
|
|
|
|
3. ユーザ入力を収集するウェブフォームに、更新されるレコードの現在のバージョン番号を保持する隠しフィールドを追加します。 |
|
|
|
|
バージョン属性が入力の検証規則を持っており、検証が成功することを確かめてください。 |
|
|
|
|
4. アクティブレコードを使って行の更新を行うコントローラ・アクションにおいて、[[\yii\db\StaleObjectException]] 例外を捕捉して、衝突を解決するために必要なビジネス・ロジック (例えば、変更をマージしたり、データの陳腐化を知らせたり) を実装します。 |
|
|
|
|
4. アクティブ・レコードを使って行の更新を行うコントローラ・アクションにおいて、[[\yii\db\StaleObjectException]] 例外を捕捉して、衝突を解決するために必要なビジネス・ロジック (例えば、変更をマージしたり、データの陳腐化を知らせたり) を実装します。 |
|
|
|
|
|
|
|
|
|
例えば、バージョン番号のカラムが `version` と名付けられているとすると、次のようなコードによって楽観的ロックを実装することが出来ます。 |
|
|
|
|
|
|
|
|
@ -739,15 +739,15 @@ public function actionUpdate($id)
|
|
|
|
|
|
|
|
|
|
## リレーショナル・データを扱う <span id="relational-data"></span> |
|
|
|
|
|
|
|
|
|
個々のデータベース・テーブルを扱うだけでなく、アクティブレコードは関連したテーブルのデータも一緒に読み出して、主たるデータを通して簡単にアクセス出来るようにすることが出来ます。 |
|
|
|
|
個々のデータベース・テーブルを扱うだけでなく、アクティブ・レコードは関連したテーブルのデータも一緒に読み出して、主たるデータを通して簡単にアクセス出来るようにすることが出来ます。 |
|
|
|
|
例えば、一人の顧客は一つまたは複数の注文を発することがあり得ますので、顧客のデータは注文のデータと関連を持っていることになります。 |
|
|
|
|
このリレーションが適切に宣言されていれば、`$customer->orders` という式を使って顧客の注文情報にアクセスすることが出来ます。 |
|
|
|
|
`$customer->orders` は、顧客の注文情報を `Order` アクティブレコード・インスタンスの配列として返してくれます。 |
|
|
|
|
`$customer->orders` は、顧客の注文情報を `Order` アクティブ・レコード・インスタンスの配列として返してくれます。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### リレーションを宣言する <span id="declaring-relations"></span> |
|
|
|
|
|
|
|
|
|
アクティブレコードを使ってリレーショナル・データを扱うためには、最初に、アクティブレコード・クラスの中でリレーションを宣言する必要があります。 |
|
|
|
|
アクティブ・レコードを使ってリレーショナル・データを扱うためには、最初に、アクティブ・レコード・クラスの中でリレーションを宣言する必要があります。 |
|
|
|
|
これは、以下のように、関心のあるそれぞれのリレーションについて *リレーション・メソッド* を宣言するだけの簡単な作業です。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -782,11 +782,11 @@ class Order extends ActiveRecord
|
|
|
|
|
|
|
|
|
|
- リレーションの多重性: [[yii\db\ActiveRecord::hasMany()|hasMany()]] または [[yii\db\ActiveRecord::hasOne()|hasOne()]] のどちらかを呼ぶことによって指定されます。 |
|
|
|
|
上記の例では、リレーションの宣言において、顧客は複数の注文を持ち得るが、一方、注文は一人の顧客しか持たない、ということが容易に読み取れます。 |
|
|
|
|
- 関連するアクティブレコード・クラスの名前: [[yii\db\ActiveRecord::hasMany()|hasMany()]] または [[yii\db\ActiveRecord::hasOne()|hasOne()]] の最初のパラメータとして指定されます。 |
|
|
|
|
- 関連するアクティブ・レコード・クラスの名前: [[yii\db\ActiveRecord::hasMany()|hasMany()]] または [[yii\db\ActiveRecord::hasOne()|hasOne()]] の最初のパラメータとして指定されます。 |
|
|
|
|
クラス名を取得するのに `Xyz::className()` を呼ぶのが推奨されるプラクティスです。 |
|
|
|
|
そうすれば、IDE の自動補完のサポートを得ることことが出来るだけでなく、コンパイル段階でエラーを検出することが出来ます。 |
|
|
|
|
- 二つのデータタイプ間のリンク: 二つのデータタイプの関連付けに用いられるカラムを指定します。 |
|
|
|
|
配列の値は主たるデータ (リレーションを宣言しているアクティブレコード・クラスによって表されるデータ) のカラムであり、配列のキーは関連するデータのカラムです。 |
|
|
|
|
配列の値は主たるデータ (リレーションを宣言しているアクティブ・レコード・クラスによって表されるデータ) のカラムであり、配列のキーは関連するデータのカラムです。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### リレーショナル・データにアクセスする <span id="accessing-relational-data"></span> |
|
|
|
@ -808,8 +808,8 @@ $orders = $customer->orders;
|
|
|
|
|
> Info: `xyz` という名前のリレーションを getter メソッド `getXyz()` によって宣言すると、`xyz` を [オブジェクト・プロパティ](concept-properties.md) のようにアクセスすることが出来るようになります。 |
|
|
|
|
名前は大文字と小文字を区別することに注意してください。 |
|
|
|
|
|
|
|
|
|
リレーションが [[yii\db\ActiveRecord::hasMany()|hasMany()]] によって宣言されている場合は、このリレーション・プロパティにアクセスすると、関連付けられたアクティブレコード・インスタンスの配列が返されます。 |
|
|
|
|
リレーションが [[yii\db\ActiveRecord::hasOne()|hasOne()]] によって宣言されている場合は、このリレーション・プロパティにアクセスすると、関連付けられたアクティブレコード・インスタンスか、関連付けられたデータが見つからないときは `null` が返されます。 |
|
|
|
|
リレーションが [[yii\db\ActiveRecord::hasMany()|hasMany()]] によって宣言されている場合は、このリレーション・プロパティにアクセスすると、関連付けられたアクティブ・レコード・インスタンスの配列が返されます。 |
|
|
|
|
リレーションが [[yii\db\ActiveRecord::hasOne()|hasOne()]] によって宣言されている場合は、このリレーション・プロパティにアクセスすると、関連付けられたアクティブ・レコード・インスタンスか、関連付けられたデータが見つからないときは `null` が返されます。 |
|
|
|
|
|
|
|
|
|
リレーション・プロパティに最初にアクセスしたときは、上記の例で示されているように、SQL 文が実行されます。 |
|
|
|
|
その同じプロパティに再びアクセスしたときは、SQL 文を再実行することなく、以前の結果が返されます。 |
|
|
|
@ -960,7 +960,7 @@ class Customer extends ActiveRecord
|
|
|
|
|
|
|
|
|
|
### レイジー・ローディングとイーガー・ローディング <span id="lazy-eager-loading"></span> |
|
|
|
|
|
|
|
|
|
[リレーショナル・データにアクセスする](#accessing-relational-data) において、通常のオブジェクト・プロパティにアクセスするのと同じようにして、アクティブレコード・インスタンスのリレーション・プロパティにアクセスすることが出来ることを説明しました。 |
|
|
|
|
[リレーショナル・データにアクセスする](#accessing-relational-data) において、通常のオブジェクト・プロパティにアクセスするのと同じようにして、アクティブ・レコード・インスタンスのリレーション・プロパティにアクセスすることが出来ることを説明しました。 |
|
|
|
|
SQL 文は、リレーション・プロパティに最初にアクセスするときにだけ実行されます。 |
|
|
|
|
このようなリレーショナル・データのアクセス方法を *レイジー・ローディング* と呼びます。 |
|
|
|
|
例えば、 |
|
|
|
@ -977,7 +977,7 @@ $orders2 = $customer->orders;
|
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
レイジー・ローディングは非常に使い勝手が良いものです。 |
|
|
|
|
しかし、複数のアクティブレコード・インスタンスの同じリレーション・プロパティにアクセスする必要がある場合は、パフォーマンスの問題を生じ得ます。 |
|
|
|
|
しかし、複数のアクティブ・レコード・インスタンスの同じリレーション・プロパティにアクセスする必要がある場合は、パフォーマンスの問題を生じ得ます。 |
|
|
|
|
次のコードサンプルを考えて見てください。実行される SQL 文の数はいくらになるでしょう? |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -1009,12 +1009,12 @@ foreach ($customers as $customer) {
|
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveQuery::with()]] を呼ぶことによって、最初の 100 人の顧客の注文をたった一つの SQL 文で返すように、アクティブレコードに指示をしています。 |
|
|
|
|
[[yii\db\ActiveQuery::with()]] を呼ぶことによって、最初の 100 人の顧客の注文をたった一つの SQL 文で返すように、アクティブ・レコードに指示をしています。 |
|
|
|
|
結果として、実行される SQL 文の数は 101 から 2 に減ります。 |
|
|
|
|
|
|
|
|
|
イーガー・ローディングは、一つだけでなく、複数のリレーションに対しても使うことが出来ます。 |
|
|
|
|
さらには、*ネストされたリレーション* でさえ、イーガー・ロードすることが出来ます。 |
|
|
|
|
ネストされたリレーションというのは、関連するアクティブレコードの中で宣言されているリレーションです。 |
|
|
|
|
ネストされたリレーションというのは、関連するアクティブ・レコードの中で宣言されているリレーションです。 |
|
|
|
|
例えば、`Cutomer` が `orders` リレーションによって `Order` と関連しており、`Order` が `items` リレーションによって `Item` と関連している場合です。 |
|
|
|
|
`Customer` に対するクエリを実行するときに、ネストされたリレーションの記法である `orders.items` を使って、`items` をイーガー・ロードすることが出来ます。 |
|
|
|
|
|
|
|
|
@ -1196,7 +1196,7 @@ $query->joinWith(['orders o' => function($q) {
|
|
|
|
|
|
|
|
|
|
### 逆リレーション <span id="inverse-relations"></span> |
|
|
|
|
|
|
|
|
|
リレーションの宣言は、たいていの場合、二つのアクティブレコード・クラスの間で相互的なものになります。 |
|
|
|
|
リレーションの宣言は、たいていの場合、二つのアクティブ・レコード・クラスの間で相互的なものになります。 |
|
|
|
|
例えば、`Customer` は `orders` リレーションによって `Order` に関連付けられ、逆に、`Order` は`customer` リレーションによって `Customer` に関連付けられる、という具合です。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -1263,7 +1263,7 @@ echo $customer2 === $customer ? '同じ' : '異なる';
|
|
|
|
|
|
|
|
|
|
リレーショナル・データを扱う時には、たいてい、さまざまなデータ間にリレーションを確立したり、既存のリレーションを破棄したりする必要があります。 |
|
|
|
|
そのためには、リレーションを定義するカラムの値を適切に設定することが必要です。 |
|
|
|
|
アクティブレコードを使う場合は、結局の所、次のようなコードを書くことになるでしょう。 |
|
|
|
|
アクティブ・レコードを使う場合は、結局の所、次のようなコードを書くことになるでしょう。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$customer = Customer::findOne(123); |
|
|
|
@ -1276,7 +1276,7 @@ $order->customer_id = $customer->id;
|
|
|
|
|
$order->save(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
アクティブレコードは、この仕事をもっと楽に達成することが出来るように、[[yii\db\ActiveRecord::link()|link()]] メソッドを提供しています。 |
|
|
|
|
アクティブ・レコードは、この仕事をもっと楽に達成することが出来るように、[[yii\db\ActiveRecord::link()|link()]] メソッドを提供しています。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$customer = Customer::findOne(123); |
|
|
|
@ -1287,11 +1287,11 @@ $order->subtotal = 100;
|
|
|
|
|
$order->link('customer', $customer); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::link()|link()]] メソッドは、リレーション名と、リレーションを確立する対象のアクティブレコード・インスタンスを指定することを要求します。 |
|
|
|
|
このメソッドは、二つのアクティブレコード・インスタンスをリンクする属性の値を修正して、それをデータベースに書き込みます。 |
|
|
|
|
[[yii\db\ActiveRecord::link()|link()]] メソッドは、リレーション名と、リレーションを確立する対象のアクティブ・レコード・インスタンスを指定することを要求します。 |
|
|
|
|
このメソッドは、二つのアクティブ・レコード・インスタンスをリンクする属性の値を修正して、それをデータベースに書き込みます。 |
|
|
|
|
上記の例では、`Order` インスタンスの `customer_id` 属性を `Customer` インスタンスの `id` 属性の値になるようにセットして、それをデータベースに保存します。 |
|
|
|
|
|
|
|
|
|
> Note: 二つの新規作成されたアクティブレコード・インスタンスをリンクすることは出来ません。 |
|
|
|
|
> Note: 二つの新規作成されたアクティブ・レコード・インスタンスをリンクすることは出来ません。 |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::link()|link()]] を使用することの利点は、リレーションが [中間テーブル](#junction-table) によって定義されている場合に、さらに明白になります。 |
|
|
|
|
例えば、一つの `Order` インスタンスと一つの`Item` インスタンスをリンクするのに、次のコードを使うことが出来ます。 |
|
|
|
@ -1302,11 +1302,11 @@ $order->link('items', $item);
|
|
|
|
|
|
|
|
|
|
上記のコードによって、`order_item` 中間テーブルに、注文と商品を関連付けるための行が自動的に挿入されます。 |
|
|
|
|
|
|
|
|
|
> Info: [[yii\db\ActiveRecord::link()|link()]] メソッドは、影響を受けるアクティブレコード・インスタンスを保存する際に、データ検証を実行しません。 |
|
|
|
|
> Info: [[yii\db\ActiveRecord::link()|link()]] メソッドは、影響を受けるアクティブ・レコード・インスタンスを保存する際に、データ検証を実行しません。 |
|
|
|
|
このメソッドを呼ぶ前にすべての入力値を検証することはあなたの責任です。 |
|
|
|
|
|
|
|
|
|
[[yii\db\ActiveRecord::link()|link()]] の逆の操作が [[yii\db\ActiveRecord::unlink()|unlink()]] です。 |
|
|
|
|
これは、既存の二つのアクティブレコード・インスタンスのリレーションを破棄します。 |
|
|
|
|
これは、既存の二つのアクティブ・レコード・インスタンスのリレーションを破棄します。 |
|
|
|
|
例えば、 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -1322,7 +1322,7 @@ $customer->unlink('orders', $customer->orders[0]);
|
|
|
|
|
|
|
|
|
|
## DBMS 間のリレーション <span id="cross-database-relations"></span> |
|
|
|
|
|
|
|
|
|
アクティブレコードは、異なるデータベースをバックエンドに持つアクティブレコードの間でリレーションを宣言することを可能にしています。 |
|
|
|
|
アクティブ・レコードは、異なるデータベースをバックエンドに持つアクティブ・レコードの間でリレーションを宣言することを可能にしています。 |
|
|
|
|
データベースは異なるタイプ (例えば、MySQL と PostgreSQL、または、MS SQL と MongoDB) であってもよく、別のサーバで動作していても構いません。 |
|
|
|
|
同じ構文を使ってリレーショナル・クエリを実行することが出来ます。 |
|
|
|
|
例えば、 |
|
|
|
@ -1370,8 +1370,8 @@ $customers = Customer::find()->with('comments')->all();
|
|
|
|
|
|
|
|
|
|
## クエリ・クラスをカスタマイズする <span id="customizing-query-classes"></span> |
|
|
|
|
|
|
|
|
|
デフォルトでは、全てのアクティブレコードのクエリは [[yii\db\ActiveQuery]] によってサポートされます。 |
|
|
|
|
カスタマイズされたクエリ・クラスをアクティブレコードで使用するためには、[[yii\db\ActiveRecord::find()]] メソッドをオーバーライドして、カスタマイズされたクエリ・クラスのインスタンスを返すようにしなければなりません。 |
|
|
|
|
デフォルトでは、全てのアクティブ・レコードのクエリは [[yii\db\ActiveQuery]] によってサポートされます。 |
|
|
|
|
カスタマイズされたクエリ・クラスをアクティブ・レコードで使用するためには、[[yii\db\ActiveRecord::find()]] メソッドをオーバーライドして、カスタマイズされたクエリ・クラスのインスタンスを返すようにしなければなりません。 |
|
|
|
|
例えば、 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
@ -1431,7 +1431,7 @@ $inactiveComments = Comment::find()->active(false)->all();
|
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
> Tip: 大きなプロジェクトでは、アクティブレコード・クラスをクリーンに保つことが出来るように、クエリ関連のコードのほとんどをカスタマイズされたクエリ・クラスに保持することが推奨されます。 |
|
|
|
|
> Tip: 大きなプロジェクトでは、アクティブ・レコード・クラスをクリーンに保つことが出来るように、クエリ関連のコードのほとんどをカスタマイズされたクエリ・クラスに保持することが推奨されます。 |
|
|
|
|
|
|
|
|
|
この新しいクエリ構築メソッドは、`Comment` に関するリレーションを定義するときや、リレーショナル・クエリを実行するときにも使用することが出来ます。 |
|
|
|
|
|
|
|
|
@ -1469,15 +1469,15 @@ $customers = Customer::find()->with([
|
|
|
|
|
|
|
|
|
|
## 追加のフィールドを選択する |
|
|
|
|
|
|
|
|
|
アクティブレコードのインスタンスにクエリ結果からデータが投入されるときは、受け取ったデータセットのカラムの値が対応する属性に入れられます。 |
|
|
|
|
アクティブ・レコードのインスタンスにクエリ結果からデータが投入されるときは、受け取ったデータセットのカラムの値が対応する属性に入れられます。 |
|
|
|
|
|
|
|
|
|
クエリ結果から追加のカラムや値を取得して、アクティブレコードの内部に格納することが出来ます。 |
|
|
|
|
クエリ結果から追加のカラムや値を取得して、アクティブ・レコードの内部に格納することが出来ます。 |
|
|
|
|
例えば、ホテルの客室の情報を含む `room` という名前のテーブルがあるとしましょう。 |
|
|
|
|
そして、全ての客室のデータは `length` (長さ)、`width` (幅)、`height` (高さ) というフィールドを使って、部屋の幾何学的なサイズに関する情報を格納しているとします。 |
|
|
|
|
空いている全ての部屋の一覧を容積の降順で取得する必要がある場合を考えて見てください。 |
|
|
|
|
レコードをその値で並べ替える必要があるので、PHP を使って容積を計算することは出来ません。 |
|
|
|
|
しかし、同時に、一覧には `volume` (容積) も表示したいでしょう。 |
|
|
|
|
目的を達するためには、`Room` アクティブレコード・クラスにおいて追加のフィールドを宣言し、`volume` の値を格納する必要があります。 |
|
|
|
|
目的を達するためには、`Room` アクティブ・レコード・クラスにおいて追加のフィールドを宣言し、`volume` の値を格納する必要があります。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
class Room extends \yii\db\ActiveRecord |
|
|
|
|