Browse Source

Merge pull request #15926 from yiijan/guide-ja-1803

docs/guide-ja updated
tags/2.0.16
Dmitry Naumenko 7 years ago committed by GitHub
parent
commit
b4f1a507a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      docs/guide-ja/caching-data.md
  2. 3
      docs/guide-ja/caching-fragment.md
  3. 3
      docs/guide-ja/concept-aliases.md
  4. 17
      docs/guide-ja/concept-service-locator.md
  5. 68
      docs/guide-ja/db-active-record.md
  6. 24
      docs/guide-ja/db-dao.md
  7. 296
      docs/guide-ja/db-query-builder.md

33
docs/guide-ja/caching-data.md

@ -277,19 +277,13 @@ $result = Customer::getDb()->cache(function ($db) {
> Info: いくつかの DBMS (例えば [MySQL](http://dev.mysql.com/doc/refman/5.1/ja/query-cache.html)) でもデータベースのサーバサイドのクエリキャッシュをサポートしています。
どちらのクエリキャッシュメカニズムも選べますが、前述した Yii のクエリキャッシュにはキャッシュの依存を柔軟に指定できるという利点があり、潜在的にはより効率的でしょう。
2.0.14 以降は、下記のショートカットを使用することが出来ます。
### キャッシュのフラッシュ <span id="cache-flushing">
保存されている全てのキャッシュデータを無効化する必要がある場合は、[[yii\caching\Cache::flush()]] を呼ぶことが出来ます。
コンソールから `yii cache/flush` を呼ぶことによっても、キャッシュをフラッシュすることが出来ます。
- `yii cache`: アプリケーションで利用可能なキャッシュのリストを表示します。
- `yii cache/flush cache1 cache2`: キャッシュコンポーネント `cache1``cache2` をフラッシュします
(複数のコンポーネント名をスペースで区切って渡すことが出来ます)
- `yii cache/flush-all`: アプリケーションの全てのキャッシュコンポーネントをフラッシュします。
> Info: デフォルトでは、コンソールアプリケーションは独立した構成情報ファイルを使用します。
正しい結果を得るためには、ウェブとコンソールのアプリケーション構成で同じキャッシュコンポーネントを使用していることを確認してください。
```php
(new Query())->cache(7200)->all();
// および
User::find()->cache(7200)->all();
```
### 構成 <span id="query-caching-configs"></span>
@ -376,3 +370,18 @@ $result = $db->cache(function ($db) {
例えば、いくつかの DBMS において BLOB 型のカラムを用いる場合、クエリ結果はカラムデータに対するリソースハンドラを返します。
いくつかのキャッシュストレージはサイズに制約があります。例えば Memcache では、各エントリのサイズは 1MB が上限値です。そのためクエリ結果のサイズがこの制約を越える場合、キャッシュは失敗します。
### キャッシュのフラッシュ <span id="cache-flushing">
保存されている全てのキャッシュデータを無効化する必要がある場合は、[[yii\caching\Cache::flush()]] を呼ぶことが出来ます。
コンソールから `yii cache/flush` を呼ぶことによっても、キャッシュをフラッシュすることが出来ます。
- `yii cache`: アプリケーションで利用可能なキャッシュのリストを表示します。
- `yii cache/flush cache1 cache2`: キャッシュコンポーネント `cache1``cache2` をフラッシュします
(複数のコンポーネント名をスペースで区切って渡すことが出来ます)
- `yii cache/flush-all`: アプリケーションの全てのキャッシュコンポーネントをフラッシュします。
> Info: デフォルトでは、コンソールアプリケーションは独立した構成情報ファイルを使用します。
正しい結果を得るためには、ウェブとコンソールのアプリケーション構成で同じキャッシュコンポーネントを使用していることを確認してください。

3
docs/guide-ja/caching-fragment.md

@ -163,3 +163,6 @@ if ($this->beginCache($id1)) {
[[yii\base\View::renderDynamic()|renderDynamic()]] メソッドはパラメータとして PHP コードを取ります。
この PHP コードの戻り値が、ダイナミックコンテントとして扱われます。
囲んでいる断片がキャッシュから提供されるか否かにかかわらず、同じ PHP コードがすべてのリクエストに対して実行されます。
> Note: バージョン 2.0.14 以降、[[yii\base\DynamicContentAwareInterface]] インターフェイスとその [[yii\base\DynamicContentAwareTrait]] トレイトによって、ダイナミックコンテント API が公開されています。.
その一例としては、 [[yii\widgets\FragmentCache]] クラスを参照して下さい。

3
docs/guide-ja/concept-aliases.md

@ -19,6 +19,9 @@ Yii::setAlias('@foo', '/path/to/foo');
// URL のエイリアス
Yii::setAlias('@bar', 'http://www.example.com');
// \foo\Bar クラスを保持する具体的なファイルのエイリアス
Yii::setAlias('@foo/Bar.php', '/definitely/not/foo/Bar.php');
```
> Note: エイリアスされているファイルパスや URL は、必ずしも実在するファイルまたはリソースを参照しない場合があります。

17
docs/guide-ja/concept-service-locator.md

@ -11,7 +11,7 @@ Yii の中で最も一般的に使用されるサービスロケータは、`\Yi
`response``urlManager` などのコンポーネントです。あなたはサービスロケータによって提供される機能を通じて、
簡単に、これらのコンポーネントを構成、あるいは独自の実装に置き換え、といったことができます。
アプリケーションオブジェクトの他に、各モジュールオブジェクトもまたサービスロケータです。
アプリケーションオブジェクトの他に、各モジュールオブジェクトもまたサービスロケータです。 モジュールは [ツリー走査](#tree-traversal) を実装しています。
サービスロケータを使用する最初のステップは、コンポーネントを登録することです。コンポーネントは、 [[yii\di\ServiceLocator::set()]]
を通じて登録することができます。次のコードは、コンポーネントを登録するさまざまな方法を示しています。
@ -60,7 +60,7 @@ $cache = $locator->cache;
サービスロケータは多くの場合、 [構成情報](concept-configurations.md) で作成されるため、
[[yii\di\ServiceLocator::setComponents()|components]] という名前の書き込み可能プロパティが提供されています。
これで一度に複数のコンポーネントを設定して登録することができます。
次のコードは、サービスロケータ (例えば [アプリケーション](structure-applications.md)) を `db`、`cache`、`search` コンポーネントとともに構成するための構成情報配列を示しています。
次のコードは、サービスロケータ (例えば [アプリケーション](structure-applications.md)) を `db`、`cache`、`tz`、`search` コンポーネントとともに構成するための構成情報配列を示しています。
```php
return [
@ -73,6 +73,9 @@ return [
'password' => '',
],
'cache' => 'yii\caching\ApcCache',
'tz' => function() {
return new \DateTimeZone(Yii::$app->formatter->defaultTimeZone);
},
'search' => function () {
$solr = new app\components\SolrService('127.0.0.1');
// ... その他の初期化 ...
@ -110,3 +113,13 @@ return [
この方法は、Yii に属さないサードパーティのライブラリをカプセル化する Yii コンポーネントをリリースしようとする場合に、特に推奨される代替手法です。
上で示されているようなスタティックなメソッドを使ってサードパーティのオブジェクトを構築する複雑なロジックを表現します。
そうすれば、あなたのコンポーネントのユーザは、コンポーネントを構成するスタティックなメソッドを呼ぶ必要があるだけになります。
## ツリー走査 <span id="tree-traversal"></span>
モジュールは任意にネストすることが出来ます。Yii アプリケーションは本質的にモジュールのツリーなのです。
これらのモジュールのそれぞれがサービスロケータである訳ですから、子がその親にアクセスできるようにするのは理にかなった事です。
これによって、モジュールは、ルートのサービスロケータを参照して `Yii::$app->get('db')` とする代りに、`$this->get('db')` とすることが出来ます。
また、開発者にモジュール内で構成をオーバーライドするオプションを提供できることも、この仕組の利点です。
モジュールからサービスを引き出そうとする全てのリクエストは、そのモジュールが要求に応じられない場合は、すべてその親に渡されます。

68
docs/guide-ja/db-active-record.md

@ -464,6 +464,34 @@ HTTP リクエストから値をロードしたり、プロパティにアクセ
> Tip: アクティブレコードのバリデーションや保存の際の属性型キャストを楽にするために
[[yii\behaviors\AttributeTypecastBehavior]] を使うことが出来ます。
2.0.14 以降、Yii のアクティブレコードは、JSON や多次元配列のような複雑な型をサポートしています。
#### MySQL および PostgreSQL における JSON
データが取得された後、JSON カラムの値は標準的な JSON デコード規則に従って、
自動的に JSON からデコードされます。
アクティブレコードは、属性値を JSON カラムに保存するために [[yii\db\JsonExpression|JsonExpression]]
オブジェクトを自動的に生成します。このオブジェクトが [クエリビルダー](db-query-builder.md) レベルで JSON 文字列にエンコードされます。
#### PostgreSQL における配列
データが取得された後、配列カラムの値は PgSQL 記法から自動的に [[yii\db\ArrayExpression|ArrayExpression]] オブジェクトにデコードされます。
このオブジェクトは PHP の `ArrayAccess` インターフェイスを実装しているため、これを配列として使うこと事が出来ます。
また、`->getValue()` を呼んで配列そのものを取得することも出来ます。
アクティブレコードは、属性値を配列カラムに保存するために [[yii\db\ArrayExpression|ArrayExpression]]
オブジェクトを生成します。このオブジェクトが [クエリビルダー](db-query-builder.md) のレベルで配列を表す PgSQL 文字列にエンコードされます。
JSON カラムに対して条件を使用することも出来ます。
```php
$query->andWhere(['=', 'json', new ArrayExpression(['foo' => 'bar'])
```
式を構築するシステムについて更に学習するためには [クエリビルダー – 特製の条件や式を追加する](db-query-builder.md#adding-custom-conditions-and-expressions)
という記事を参照して下さい。
### 複数の行を更新する <span id="updating-multiple-rows"></span>
上述のメソッドは、すべて、個別のアクティブレコードインスタンスに対して作用し、個別のテーブル行を挿入したり更新したりするものです。
@ -874,6 +902,42 @@ $items = $order->items;
```
### 複数のテーブルを経由するリレーション定義の連鎖<span id="multi-table-relations"></span>
さらに、[[yii\db\ActiveQuery::via()|via()]] を使ってリレーション定義を連鎖させ、複数のテーブルを経由するリレーションを定義することも可能です。
上記の例で考えましょう。そこには `Customer`(顧客)、`Order`(注文) そして `Item`(品目) というクラスがあります。
`Customer` クラスに、発注された全ての注文によって購入された全ての品目を列挙するリレーションを追加して、
それに `getPurchasedItems()` という名前を付けることが出来ます。
リレーション定義の連鎖が次のコードサンプルで示されています。
```php
class Customer extends ActiveRecord
{
// ...
public function getPurchasedItems()
{
// 顧客の購入品目、すなわち、`Item` の 'id' カラムが OrderItem の 'item_id' に合致するもの
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->via('orderItems');
}
public function getOrderItems()
{
// 顧客の OrderItems、すなわち、`Order` の `id` カラムが `OrderItem` の 'order_id' に合致するもの
return $this->hasMany(OrderItem::className(), ['order_id' => 'id'])
->via('orders');
}
public function getOrders()
{
// 顧客の注文
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
}
```
### レイジーローディングとイーガーローディング <span id="lazy-eager-loading"></span>
[リレーショナルデータにアクセスする](#accessing-relational-data) において、通常のオブジェクトプロパティにアクセスするのと同じようにして、アクティブレコードインスタンスのリレーションプロパティにアクセスすることが出来ることを説明しました。
@ -1037,6 +1101,10 @@ $customers = Customer::find()
デフォルトでは、[[yii\db\ActiveQuery::joinWith()|joinWith()]] を呼ぶと、リレーションのデータが [イーガーロード](#lazy-eager-loading) されます。
リレーションのデータを読み取りたくない場合は、第二のパラメータ `$eagerLoading``false` に指定することが出来ます。
> Note: たとえイーガーローディングを有効にして [[yii\db\ActiveQuery::joinWith()|joinWith()]] や [[yii\db\ActiveQuery::innerJoinWith()|innerJoinWith()]] を使う場合でも、
リレーションのデータを取得するのには `JOIN` クエリの結果は**使われません**。
その場合でも、やはり、[イーガーローディング](#lazy-eager-loading) の節で説明したように、結合されたリレーションごとに追加のクエリが実行されます。
[[yii\db\ActiveQuery::with()|with()]] と同じように、一つまたは複数のリレーションを結合したり、リレーションクエリをその場でカスタマイズしたり、ネストされたリレーションを結合したりすることが出来ます。
また、[[yii\db\ActiveQuery::with()|with()]] と [[yii\db\ActiveQuery::joinWith()|joinWith()]] を混ぜて使用することも出来ます。
例えば、

24
docs/guide-ja/db-dao.md

@ -201,6 +201,12 @@ $post2 = $command->queryOne();
クエリの実行の前にプレースホルダを変数 `$id` にバインドし、そして、後に続く各回の実行の前にその変数の値を変更していること (これは、たいてい、ループで行います) に着目してください。
このやり方でクエリを実行すると、パラメータの値が違うごとに新しいクエリを実行するのに比べて、はるかに効率を良くすることが出来ます。
> Info: パラメータバインディングは、素の SQL を含む文字列に値を挿入しなければならない場所でのみ使用されます。
> [クエリビルダー](db-query-builder.md) や [アクティブレコード](db-active-record.md) のような高レベルの抽象的レイヤーでは、
> 多くの場所で SQL に変換される値の配列を指定する場合がよくあります。
> これらの場所では Yii によってパラメータバインディングが内部的に実行されますので、
> パラメータを手動で指定する必要はありません。
### SELECT しないクエリを実行する <span id="non-select-queries"></span>
@ -245,6 +251,22 @@ Yii::$app->db->createCommand()->batchInsert('user', ['name', 'age'], [
])->execute();
```
もう一つの有用なメソッドは [[yii\db\Command::upsert()|upsert()]] です。
upsert は、(ユニーク制約に合致する)行がまだ存在しない場合はデータベーステーブルに行を挿入し、
既に行が存在している場合は行を更新する、アトミックな操作です。
```php
Yii::$app->db->createCommand()->upsert('pages', [
'name' => 'フロント・ページ',
'url' => 'http://example.com/', // url はユニーク
'visits' => 0,
], [
'visits' => new \yii\db\Expression('visits + 1'),
], $params)->execute();
```
上記のコードは、新しいページのレコードを挿入するか、または、既存のレコードの訪問者カウンタをインクリメントします。
上述のメソッド群はクエリを生成するだけであり、実際にそれを実行するためには、常に [[yii\db\Command::execute()|execute()]]
を呼び出す必要があることに注意してください。
@ -372,7 +394,7 @@ Yii は、最もよく使われる分離レベルのために、四つの定数
- [[\yii\db\Transaction::SERIALIZABLE]] - 最も強いレベル。上記の問題を全て回避。
分離レベルを指定するためには、上記の定数を使う以外に、あなたが使っている DBMS によってサポートされている有効な構文の文字列を使うことも出来ます。
例えば、PostreSQL では、`SERIALIZABLE READ ONLY DEFERRABLE` を使うことが出来ます。
例えば、PostreSQL では、`"SERIALIZABLE READ ONLY DEFERRABLE"` を使うことが出来ます。
DBMS によっては、接続全体に対してのみ分離レベルの設定を許容しているものがあることに注意してください。
その場合、すべての後続のトランザクションは、指定しなくても、それと同じ分離レベルで実行されます。

296
docs/guide-ja/db-query-builder.md

@ -149,11 +149,12 @@ $query->from(['u' => $subQuery]);
### [[yii\db\Query::where()|where()]] <span id="where"></span>
[[yii\db\Query::where()|where()]] メソッドは、SQL クエリの `WHERE` 句を定義します。
`WHERE` の条件を指定するために、次のつの形式から一つを選んで使うことが出来ます。
`WHERE` の条件を指定するために、次の4つの形式から一つを選んで使うことが出来ます。
- 文字列形式、例えば、`'status=1'`
- ハッシュ形式、例えば、`['status' => 1, 'type' => 2]`
- 演算子形式、例えば、`['like', 'name', 'test']`
- オブジェクト形式、例えば、`new LikeCondition('name', 'LIKE', 'test')`
#### 文字列形式 <span id="string-format"></span>
@ -233,15 +234,22 @@ $query->where(['id' => $userQuery]);
ここで、各オペランドは、文字列形式、ハッシュ形式、あるいは、再帰的に演算子形式として指定することが出来ます。
そして、演算子には、次のどれか一つを使うことが出来ます。
- `and`: 二つのオペランドが `AND` を使って結合されます。例えば、`['and', 'id=1', 'id=2']` は `id=1 AND id=2` を生成します。
- `and`: 複数のオペランドが `AND` を使って結合されます。例えば、`['and', 'id=1', 'id=2']` は `id=1 AND id=2` を生成します。
オペランドが配列である場合は、ここで説明されている規則に従って文字列に変換されます。
例えば、`['and', 'type=1', ['or', 'id=1', 'id=2']]` は `type=1 AND (id=1 OR id=2)` を生成します。
このメソッドは、文字列を引用符で囲ったりエスケープしたりしません。
- `or`: 二つのオペランドが `OR` を使って結合されること以外は `and` 演算子と同じです。
- `or`: オペランドが `OR` を使って結合されること以外は `and` 演算子と同じです。
- `not`: オペランド1 だけを受け取って `NOT()` で包みます。例えば、`['not', 'id=1']` は `NOT (id=1)` を生成します。
オペランド1 は、それ自体も複数の式を表す配列であっても構いません。例えば、`['not', ['status' => 'draft', 'name' => 'example']]` は `NOT ((status='draft') AND (name='example'))` を生成します。
- `between`: オペランド 1 はカラム名、オペランド 2 と 3 はカラムの値が属すべき範囲の開始値と終了値としなければなりません。
例えば、`['between', 'id', 1, 10]` は `id BETWEEN 1 AND 10` を生成します。
値が二つのカラムの値の間にあるという条件 (例えば、`11 BETWEEN min_id AND max_id`) を構築する必要がある場合は、
[[yii\db\conditions\BetweenColumnsCondition|BetweenColumnsCondition]] を使用しなければなりません。
条件定義のオブジェクト形式について更に学習するためには [条件 – オブジェクト形式](#object-format) の節を参照して下さい。
- `not between`: 生成される条件において `BETWEEN``NOT BETWEEN` に置き換えられる以外は、`between` と同じです。
@ -286,6 +294,39 @@ $query->where(['id' => $userQuery]);
従って、[文字列形式](#string-format) とは対照的に、ここでは手動でパラメータを追加する必要はありません。
#### オブジェクト形式 <span id="object-format"></span>
オブジェクト形式は 2.0.14 から利用可能な、条件を定義するための最も強力でもあり、最も複雑でもある方法です。
クエリビルダの上にあなた自身の抽象レイヤを構築したいか、または独自の複雑な条件を実装したいかする場合には、この形式を採用する必要があります。
条件クラスのインスタンスはイミュータブルです。条件クラスのインスタンスは条件データを保持し、条件ビルダーにゲッターを提供することを唯一の目的とします。
そして、条件ビルダーが、条件クラスのインスタンスに保存されたデータを SQL の式に変換するロジックを持つクラスです。
内部的には、上述の三つの形式は、生の SQL を構築するに先立って、暗黙のうちにオブジェクト形式に変換されます。
従って、複数の形式を単一の条件に結合することが可能です。
```php
$query->andWhere(new OrCondition([
new InCondition('type', 'in', $types),
['like', 'name', '%good%'],
'disabled=false'
]))
```
演算子形式からオブジェクト形式への変換は、演算子の名前とそれを表すクラス名を対応づける
[[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] プロパティに従って行われます。
- `AND`, `OR` -> `yii\db\conditions\ConjunctionCondition`
- `NOT` -> `yii\db\conditions\NotCondition`
- `IN`, `NOT IN` -> `yii\db\conditions\InCondition`
- `BETWEEN`, `NOT BETWEEN` -> `yii\db\conditions\BetweenCondition`
等々。
オブジェクト形式を使うことによって、あなた独自の条件を作成したり、デフォルトの条件が作成される方法を変更したりすることが可能になります。
詳細は [特製の条件や式を追加する](#adding-custom-conditions-and-expressions) の節を参照して下さい。
#### 条件を追加する <span id="appending-conditions"></span>
[[yii\db\Query::andWhere()|andWhere()]] または [[yii\db\Query::orWhere()|orWhere()]] を使って、既存の条件に別の条件を追加することが出来ます。
@ -611,9 +652,11 @@ $query = (new \yii\db\Query())
## バッチクエリ <span id="batch-query"></span>
大量のデータを扱う場合は、[[yii\db\Query::all()]] のようなメソッドは適していません。
なぜなら、それらのメソッドは、全てのデータをメモリ上に読み込むことを必要とするためです。
必要なメモリ量を低く抑えるために、Yii はいわゆるバッチクエリのサポートを提供しています。
バッチクエリはデータカーソルを利用して、バッチモードでデータを取得します。
なぜなら、それらのメソッドは、クエリの結果全てをクライアントのメモリに読み込むことを必要とするためです。
この問題を解決するために、Yii はバッチクエリのサポートを提供しています。
クエリ結果はサーバに保持し、クライアントはカーソルを利用して1回に1バッチずつリザルトセットを反復取得するのです。
> Warning: MySQL のバッチクエリの実装には既知の制約と回避策があります。下記を参照して下さい。
バッチクエリは次のようにして使うことが出来ます。
@ -628,9 +671,10 @@ foreach ($query->batch() as $users) {
// $users は user テーブルから取得した 100 以下の行の配列
}
// または、一行ずつ反復したい場合は
// または、一行ずつ反復する場合は
foreach ($query->each() as $user) {
// $user は user テーブルから取得した一つの行を表す
// データはサーバから 100 行のバッチで取得される
// しかし $user は user テーブルの一つの行を表す
}
```
@ -638,12 +682,11 @@ foreach ($query->each() as $user) {
このオブジェクトは `Iterator` インタフェイスを実装しており、従って、`foreach` 構文の中で使うことが出来ます。
初回の反復の際に、データベースに対する SQL クエリが作成されます。データは、その後、反復のたびにバッチモードで取得されます。
デフォルトでは、バッチサイズは 100 であり、各バッチにおいて 100 行のデータが取得されます。
`batch()` または `each()` メソッドに最初のパラメータを渡すことによって、バッチサイズを変更することが出来ます。
バッチサイズは、`batch()` または `each()` メソッドに最初のパラメータを渡すことによって変更することが出来ます。
[[yii\db\Query::all()]] とは対照的に、バッチクエリは一度に 100 行のデータしかメモリに読み込みません。
データを処理した後、すぐにデータを破棄するようにすれば、バッチクエリの助けを借りてメモリ消費量を削減することが出来ます。
[[yii\db\Query::indexBy()]] によってクエリ結果をあるカラムでインデックスするように指定している場合でも、バッチクエリは正しいインデックスを保持します。
[[yii\db\Query::indexBy()]] によって、いずれかのカラムでクエリ結果をインデックスするように指定している場合でも、バッチクエリは正しいインデックスを保持します。
例えば、
```php
@ -659,3 +702,234 @@ foreach ($query->each() as $username => $user) {
// ...
}
```
#### MySQL におけるバッチクエリの制約 <span id="batch-query-mysql"></span>
MySQL のバッチクエリの実装は PDO ドライバのライブラリに依存しています。
デフォルトでは、MySQL のクエリは [`バッファモード`](http://php.net/manual/ja/mysqlinfo.concepts.buffering.php) で実行されます。
このことが、カーソルを使ってデータを取得する目的を挫折させます。
というのは、バッファモードでは、ドライバによってリザルトセット全体がクライアントのメモリに読み込まれることを防止できないからです。
> Note: `libmysqlclient` が使われている場合 (PHP5 ではそれが普通ですが) は、リザルトセットに使用されたメモリは PHP のメモリ使用量としてカウントされません。
そのため、一見、バッチクエリが正しく動作するように見えますが、実際には、データセット全体がクライアントのメモリに読み込まれて、クライアントのメモリを使い果たす可能性があります。
バッファモードを無効化してクライアントのメモリ要求量を削減するためには、PDO 接続のプロパティ `PDO::MYSQL_ATTR_USE_BUFFERED_QUERY``false` に設定しなければなりません。
しかし、そうすると、データセット全体を取得するまでは、同じ接続を通じては別のクエリを実行できなくなります。
これによって `ActiveRecord` が必要に応じてテーブルスキーマを取得するためのクエリを実行できなくなる可能性があります。
これが問題にならない場合 (テーブルスキーマが既にキャッシュされている場合) は、元の接続を非バッファモードに切り替えて、バッチクエリを実行した後に元に戻すということが可能です。
```php
Yii::$app->db->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
// バッチクエリを実行
Yii::$app->db->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
```
> Note: MyISAM の場合は、バッチクエリが継続している間、テーブルがロックされて、他の接続からの書き込みアクセスが遅延または拒絶されることがあります。
非バッファモードのクエリを使う場合は、カーソルを開いている時間を可能な限り短くするように努めて下さい。
スキーマがキャッシュされていない場合、またはバッチクエリを処理している間に他のクエリを走らせる必要がある場合は、
独立した非バッファモードのデータベース接続を作成することが出来ます。
```php
$unbufferedDb = new \yii\db\Connection([
'dsn' => Yii::$app->db->dsn,
'username' => Yii::$app->db->username,
'password' => Yii::$app->db->password,
'charset' => Yii::$app->db->charset,
]);
$unbufferedDb->open();
$unbufferedDb->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
```
`$unbufferedDb``PDO::MYSQL_ATTR_USE_BUFFERED_QUERY``false` であること以外は、元のバッファモードの `$db` と同じ PDO 属性を持つことを保証したい場合は、
[`$db` のディープコピー](https://github.com/yiisoft/yii2/issues/8420#issuecomment-301423833) をしてから、手動で false に設定することを考慮して下さい。
そして、クエリは普通に作成します。新しい接続を使ってバッチクエリを走らせ、結果をバッチで取得、または一つずつ取得します。
```php
// データを 1000 のバッチで取得
foreach ($query->batch(1000, $unbufferedDb) as $users) {
// ...
}
// データは 1000 のバッチでサーバから取得されるが、一つずつ反復処理される
foreach ($query->each(1000, $unbufferedDb) as $user) {
// ...
}
```
リザルトセットが全て取得されて接続が必要なくなったら、接続を閉じることが出来ます。
```php
$unbufferedDb->close();
```
> Note: 非バッファモードのクエリは PHP 側でのメモリ消費は少なくなりますが、MySQL サーバの負荷を増加させ得ます。
特別に巨大なデータに対するアプリの動作については、あなた自身のコードを設計することが推奨されます。
例えば、[整数のキーで範囲を分割して、非バッファモードのクエリでループする](https://github.com/yiisoft/yii2/issues/8420#issuecomment-296109257) など。
### 特製の条件や式を追加する <span id="adding-custom-conditions-and-expressions"></span>
[条件 – オブジェクト形式](#object-format) の節で触れたように、特製の条件クラスを作成することが可能です。
例として、特定のカラムが一定の値より小さいことをチェックする条件を作ってみましょう。
演算子形式を使えば、それは次のようになるでしょう。
```php
[
'and',
'>', 'posts', $minLimit,
'>', 'comments', $minLimit,
'>', 'reactions', $minLimit,
'>', 'subscriptions', $minLimit
]
```
このような条件を一度に適用できたら良いですね。
一つのクエリの中で複数回使われる場合には、最適化の効果が大きいでしょう。
特製の条件オブジェクトを作って、それを実証しましょう。
Yii には、条件を表現するクラスを特徴付ける [[yii\db\conditions\ConditionInterface|ConditionInterface]] があります。
このインターフェイスは、配列形式から条件を作ることを可能にするための `fromArrayDefinition()` メソッドを実装することを要求します。
あなたがそれを必要としない場合は、例外を投げるだけのメソッドとして実装しても構いません。
特製の条件クラスを作るのですから、私たちの仕事に最適な API を構築すれば良いのです。
```php
namespace app\db\conditions;
class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
{
private $columns;
private $value;
/**
* @param string[] $columns $value よりも大きくなければならないカラムの配列
* @param mixed $value 各カラムと比較する値
*/
public function __construct(array $columns, $value)
{
$this->columns = $columns;
$this->value = $value;
}
public static function fromArrayDefinition($operator, $operands)
{
throw new InvalidArgumentException('未実装、あとでやる');
}
public function getColumns() { return $this->columns; }
public function getValue() { return $this->vaule; }
}
```
これで条件オブジェクトを作ることが出来ます。
```php
$conditon = new AllGreaterCondition(['col1', 'col2'], 42);
```
しかし `QueryBuilder` は、このオブジェクトから SQL 条件式を作る方法を知りません。
次に、この条件に対する式ビルダを作成する必要があります。
式ビルダは `build()` メソッドを提供する [[yii\db\ExpressionBuilderInterface]] を実装しなければいけません。
```php
namespace app\db\conditions;
class AllGreaterConditionBuilder implements \yii\db\ExpressionBuilderInterface
{
use \yii\db\Condition\ExpressionBuilderTrait; // コンストラクタと `queryBuilder` プロパティを含む。
/**
* @param AllGreaterCondition $condition ビルドすべき条件
* @param array $params バインディング・パラメータ
*/
public function build(ConditionInterface $condition, &$params)
{
$value = $condition->getValue();
$conditions = [];
foreach ($condition->getColumns() as $column) {
$conditions[] = new SimpleCondition($column, '>', $value);
}
return $this->queryBuider->buildCondition(new AndCondition($conditions), $params);
}
}
```
後は、単に [[yii\db\QueryBuilder|QueryBuilder]] に私たちの新しい条件について知らせるだけです – 
条件のマッピングを `expressionBuilders` 配列に追加します。
次のように、アプリケーション構成で直接に追加することが出来ます。
```php
'db' => [
'class' => 'yii\db\mysql\Connection',
// ...
'queryBuilder' => [
'expressionBuilders' => [
'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
],
],
],
```
これで、私たちの新しい条件を `where()` で使用することが出来るようになりました。
```php
$query->andWhere(new AllGreaterCondition(['posts', 'comments', 'reactions', 'subscriptions'], $minValue));
```
演算子形式を使って私たちの特製の条件を作成することが出来るようにしたい場合は、
演算子を [[yii\db\QueryBuilder::conditionClasses|QueryBuilder::conditionClasses]] の中で宣言しなければなりません。
```php
'db' => [
'class' => 'yii\db\mysql\Connection',
// ...
'queryBuilder' => [
'expressionBuilders' => [
'app\db\conditions\AllGreaterCondition' => 'app\db\conditions\AllGreaterConditionBuilder',
],
'conditionClasses' => [
'ALL>' => 'app\db\conditions\AllGreaterCondition',
],
],
],
```
そして、`app\db\conditions\AllGreaterCondition` の中で `AllGreaterCondition::fromArrayDefinition()` メソッドの本当の実装を作成します。
```php
namespace app\db\conditions;
class AllGreaterCondition implements \yii\db\conditions\ConditionInterface
{
// ... 上記の実装を参照
public static function fromArrayDefinition($operator, $operands)
{
return new static($operands[0], $operands[1]);
}
}
```
これ以降は、私たちの特製の条件をより短い演算子形式を使って作成することが出来ます。
```php
$query->andWhere(['ALL>', ['posts', 'comments', 'reactions', 'subscriptions'], $minValue]);
```
お気付きのことと思いますが、ここには二つの概念があります。Expression(式)と Condition(条件)です。
[[yii\db\ExpressionInterface]] は、それを構築するために [[yii\db\ExpressionBuilderInterface]] を実装した式ビルダクラスを必要とするオブジェクトを特徴付けるインターフェイスです。
また [[yii\db\condition\ConditionInterface]] は、[[yii\db\ExpressionInterface|ExpressionInterface]] を拡張して、上述されたように配列形式の定義から作成できるオブジェクトに対して使用されるべきものですが、同様にビルダを必要とするものです。
要約すると、
- Expression(式) – データセットのためのデータ転送オブジェクトであり、最終的に何らかの SQL 文にコンパイルされる。(演算子、文字列、配列、JSON、等)
- Condition(条件) – Expression(式) のスーパーセットで、一つの SQL 条件にコンパイルすることが可能な複数の式(またはスカラ値)の集合。
[[yii\db\ExpressionInterface|ExpressionInterface]] を実装する独自のクラスを作成して、データを SQL 文に変換することの複雑さを隠蔽することが出来ます。
[次の記事](db-active-record.md) では、式について、さらに多くの例を学習します。

Loading…
Cancel
Save