Commit b2d5d59a by Carsten Brandt

Merge pull request #6972 from softark/docs-guide-ja-updates

Docs guide ja updates (db-dao.md and others) [ci skip]
parents 7f670aaf b4cac987
データベースアクセスオブジェクト データベースアクセスオブジェクト
================================ ================================
> Note|注意: この節はまだ執筆中です。 [PDO](http://www.php.net/manual/ja/book.pdo.php) の上に構築された Yii DAO (データベースアクセスオブジェクト) は、リレーショナルデータベースにアクセスするためのオブジェクト指向 API を提供するものです。
これは、データベースにアクセスする他のもっと高度な方法、例えば [クエリビルダ](db-query-builder.md)[アクティブレコード](db-active-record.md) の基礎でもあります。
Yii は、PHP の [PDO](http://www.php.net/manual/ja/book.pdo.php) の上に構築されたデータベースアクセスレイヤを含んでいます。 Yii DAO を使うときは、主として素の SQL と PHP 配列を扱う必要があります。
データベースアクセスオブジェクト (DAO) のインタフェイスは、統一された API を提供し、さまざまなデータベース製品間に存在する不統一のいくらかを解決します。 結果として、Yii DAO はデータベースにアクセスする方法としては最も効率的なものになります。
アクティブレコードは、モデルを通じてのデータベースとの相互作用を提供し、クエリビルダは、動的なクエリの作成を支援します。 しかし、SQL の構文はデータベースによってさまざまに異なる場合がありますので、Yii DAO を使用するということは、特定のデータベースに依存しないアプリケーションを作るためには追加の労力が必要になる、ということをも同時に意味します。
一方、DAO はデータベースに対して直接に SQL を実行する単純で効率的な方法を提供します。
実行すべきクエリが高価なものである場合、かつ/または、アプリケーションモデル (および対応するビジネスロジック) が必要でない場合に、あなたは DAO を使いたいと思うでしょう。
Yii はデフォルトで下記の DBMS をサポートしています。 Yii は下記の DBMS のサポートを内蔵しています。
- [MySQL](http://www.mysql.com/) - [MySQL](http://www.mysql.com/)
- [MariaDB](https://mariadb.com/) - [MariaDB](https://mariadb.com/)
- [SQLite](http://sqlite.org/) - [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/) - [PostgreSQL](http://www.postgresql.org/)
- [CUBRID](http://www.cubrid.org/): バージョン 9.3 以上。(cubrid PDO 拡張の [バグ](http://jira.cubrid.org/browse/APIS-658) - [CUBRID](http://www.cubrid.org/): バージョン 9.3 以上。
のために、値を引用符で囲む機能が動作しません。そのため、サーバだけでなくクライアントも CUBRID 9.3 が必要になります)
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html) - [Oracle](http://www.oracle.com/us/products/database/overview/index.html)
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): バージョン 2008 以上。 - [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): バージョン 2008 以上。
構成 ## DB 接続を作成する <a name="creating-db-connections"></a>
----
データベースとの相互作用を開始するためには (DAO を使うにしろ使わないにしろ)、アプリケーションのデータベース接続コンポーネントを構成する必要があります。 データベースにアクセスするために、まずは、データベースに接続するために [[yii\db\Connection]] のインスタンスを作成する必要があります。
データソース名 (DSN) が、どのデータベース製品のどの特定のデータベースにアプリケーションが接続すべきかを構成します。
```php
$db = new yii\db\Connection([
'dsn' => 'mysql:host=localhost;dbname=example',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
]);
```
DB 接続は、たいていは、さまざまな場所でアクセスする必要がありますので、次のように、[アプリケーションコンポーネント](structure-application-components.md) の形式で構成するのが通例です。
```php ```php
return [ return [
...@@ -34,14 +41,7 @@ return [ ...@@ -34,14 +41,7 @@ return [
// ... // ...
'db' => [ 'db' => [
'class' => 'yii\db\Connection', 'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB 'dsn' => 'mysql:host=localhost;dbname=example',
//'dsn' => 'sqlite:/path/to/database/file', // SQLite
//'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL
//'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID
//'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv ドライバ
//'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib ドライバ
//'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql ドライバ
//'dsn' => 'oci:dbname=//localhost:1521/mydatabase', // Oracle
'username' => 'root', 'username' => 'root',
'password' => '', 'password' => '',
'charset' => 'utf8', 'charset' => 'utf8',
...@@ -51,8 +51,23 @@ return [ ...@@ -51,8 +51,23 @@ return [
]; ];
``` ```
DSN 文字列のフォーマットに関する詳細は、[PHP マニュアル](http://www.php.net/manual/ja/function.PDO-construct.php) を参照してください。 こうすると `Yii::$app->db` という式で DB 接続にアクセスすることが出来るようになります。
このクラスで構成可能なプロパティの全てのリストについては、[[yii\db\Connection]] を参照してください。
> Tip|ヒント: あなたのアプリケーションが複数のデータベースにアクセスする必要がある場合は、複数の DB アプリケーションコンポーネントを構成することが出来ます。
DB 接続を構成するときは、つねに [[yii\db\Connection::dsn|dsn]] プロパティによってデータソース名 (DNS) を指定しなければなりません。
DSN の形式はデータベースによってさまざまに異なります。
詳細は [PHP マニュアル](http://www.php.net/manual/ja/function.PDO-construct.php) を参照して下さい。
下記にいくつかの例を挙げます。
* MySQL, MariaDB: `mysql:host=localhost;dbname=mydatabase`
* SQLite: `sqlite:/path/to/database/file`
* PostgreSQL: `pgsql:host=localhost;port=5432;dbname=mydatabase`
* CUBRID: `cubrid:dbname=demodb;host=localhost;port=33000`
* MS SQL Server (via sqlsrv driver): `sqlsrv:Server=localhost;Database=mydatabase`
* MS SQL Server (via dblib driver): `dblib:host=localhost;dbname=mydatabase`
* MS SQL Server (via mssql driver): `mssql:host=localhost;dbname=mydatabase`
* Oracle: `oci:dbname=//localhost:1521/mydatabase`
ODBC 経由でデータベースに接続しようとする場合は、[[yii\db\Connection::driverName]] プロパティを構成して、Yii に実際のデータベースのタイプを知らせなければならないことに注意してください。 ODBC 経由でデータベースに接続しようとする場合は、[[yii\db\Connection::driverName]] プロパティを構成して、Yii に実際のデータベースのタイプを知らせなければならないことに注意してください。
例えば、 例えば、
...@@ -67,50 +82,50 @@ ODBC 邨檎罰縺ァ繝繧ソ繝吶繧ケ縺ォ謗・邯壹@繧医≧縺ィ縺吶k蝣エ蜷医縲ー[yii\db\ ...@@ -67,50 +82,50 @@ ODBC 邨檎罰縺ァ繝繧ソ繝吶繧ケ縺ォ謗・邯壹@繧医≧縺ィ縺吶k蝣エ蜷医縲ー[yii\db\
], ],
``` ```
主たる `db` 接続には、`\Yii::$app->db` という式によってアクセスすることが出来ます。 [[yii\db\Connection::dsn|dsn]] プロパティに加えて、たいていは [[yii\db\Connection::username|username]] と [[yii\db\Connection::password|password]] も構成しなければなりません。
一つのアプリケーションで複数の DB 接続を構成することも出来ます。 構成可能なプロパティの全てのリストは [[yii\db\Connection]] を参照して下さい。
アプリケーションの構成情報において、それらに別々の ID を割り当てるだけのことです。
```php > Info|情報: DB 接続のインスタンスを作成するとき、実際のデータベース接続は、最初の SQL を実行するか、[[yii\db\Connection::open()|open()]] メソッドを明示的に呼ぶかするまでは確立されません。
return [
// ...
'components' => [
// ...
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=mydatabase',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
],
'secondDb' => [
'class' => 'yii\db\Connection',
'dsn' => 'sqlite:/path/to/database/file',
],
],
// ...
];
```
これで、必要に応じて両方のデータベース接続を同時に使用することが出来ます。
```php ## SQL クエリを実行する <a name="executing-sql-queries"></a>
$primaryConnection = \Yii::$app->db;
$secondaryConnection = \Yii::$app->secondDb; いったんデータベース接続のインスタンスを得てしまえば、次の手順に従って SQL クエリを実行することが出来ます。
```
1. 素の SQL で [[yii\db\Command]] を作成する。
2. パラメータをバインドする (オプション)。
3. [[yii\db\Command]] の SQL 実行メソッドの一つを呼ぶ。
DB 接続を [アプリケーションコンポーネント](structure-application-components.md) として定義したくない場合は、インスタンスを直接に作成することも出来ます。 次に、データベースからデータを読み出すさまざまな方法を例示します。
```php ```php
$connection = new \yii\db\Connection([ $db = new yii\db\Connection(...);
'dsn' => $dsn,
'username' => $username, // 行のセットを返す。各行は、カラム名と値の連想配列。
'password' => $password, // 結果が無い場合は空の配列が返される。
]); $posts = $db->createCommand('SELECT * FROM post')
$connection->open(); ->queryAll();
// 一つの行 (最初の行) を返す。
// 結果が無い場合は false が返される。
$post = $db->createCommand('SELECT * FROM post WHERE id=1')
->queryOne();
// 一つのカラム (最初のカラム) を返す。
// 結果が無い場合は空の配列が返される。
$titles = $db->createCommand('SELECT title FROM post')
->queryColumn();
// スカラ値を返す。
// 結果が無い場合は false が返される。
$count = $db->createCommand('SELECT COUNT(*) FROM post')
->queryScalar();
``` ```
> Tip|ヒント: 接続を確立した直後に SQL クエリを実行する必要がある場合 (例えば、タイムゾーンや文字セットを設定するなどの場合) は、アプリケーションの構成情報ファイルに次のように追記することが出来ます。 > Note|注意: 精度を保つために、対応するデータベースカラムの型が数値である場合でも、データベースから取得されたデータは、全て文字列として表現されます。
> Tip|ヒント: 接続を確立した直後に実行したい SQL がある場合 (例えば、タイムゾーンや文字セットを設定したい場合) は、[[yii\db\Connection::EVENT_AFTER_OPEN]] ハンドラの中でそれをすることが出来ます。
> 例えば、
> >
```php ```php
return [ return [
...@@ -121,6 +136,7 @@ return [ ...@@ -121,6 +136,7 @@ return [
'class' => 'yii\db\Connection', 'class' => 'yii\db\Connection',
// ... // ...
'on afterOpen' => function($event) { 'on afterOpen' => function($event) {
// $event->sender は DB 接続を指す
$event->sender->createCommand("SET time_zone = 'UTC'")->execute(); $event->sender->createCommand("SET time_zone = 'UTC'")->execute();
} }
], ],
...@@ -129,97 +145,134 @@ return [ ...@@ -129,97 +145,134 @@ return [
]; ];
``` ```
基本的な SQL クエリを実行する
-----------------------------
いったんデータベース接続のインスタンスを得てしまえば、[[yii\db\Command]] を使って SQL クエリを実行することが出来るようになります。 ### パラメータをバインドする <a name="binding-parameters"></a>
### SELECT クエリを実行する パラメータを持つ SQL から DB コマンドを作成するときは、SQL インジェクション攻撃を防止するために、ほとんど全ての場合においてパラメータをバインドする手法を用いるべきです。
例えば、
実行されるクエリが行のセットを返す場合は、`queryAll` を使います。
```php ```php
$command = $connection->createCommand('SELECT * FROM post'); $post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
$posts = $command->queryAll(); ->bindValue(':id', $_GET['id'])
->bindValue(':status', 1)
->queryOne();
``` ```
実行されるクエリが一つの行だけを返す場合は、`queryOne` を使います。 SQL 文において、一つまたは複数のパラメータプレースホルダ (例えば、上記のサンプルにおける `:id`) を埋め込むことが出来ます。
パラメータプレースホルダは、コロンから始まる文字列でなければなりません。
そして、次に掲げるパラメータをバインドするメソッドの一つを使って、パラメータの値をバインドします。
* [[yii\db\Command::bindValue()|bindValue()]]: 一つのパラメータの値をバインドします。
* [[yii\db\Command::bindValues()|bindValues()]]: 一回の呼び出しで複数のパラメータの値をバインドします。
* [[yii\db\Command::bindParam()|bindParam()]]: [[yii\db\Command::bindValue()|bindValue()]] と似ていますが、パラメータを参照渡しでバインドすることもサポートしています。
次の例はパラメータをバインドする別の方法を示すものです。
```php ```php
$command = $connection->createCommand('SELECT * FROM post WHERE id=1'); $params = [':id' => $_GET['id'], ':status' => 1];
$post = $command->queryOne();
$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status')
->bindValues($params)
->queryOne();
$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status', $params)
->queryOne();
``` ```
クエリが複数行ではあっても一つのカラムだけを返す場合は、`queryColumn` を使います。 パラメータバインディングは [プリペアドステートメント](http://php.net/manual/ja/mysqli.quickstart.prepared-statements.php) によって実装されています。
パラメータバインディングには、SQL インジェクション攻撃を防止する以外にも、SQL 文を一度だけ準備して異なるパラメータで複数回実行することにより、パフォーマンスを向上させる効果もあります。
例えば、
```php ```php
$command = $connection->createCommand('SELECT title FROM post'); $command = $db->createCommand('SELECT * FROM post WHERE id=:id');
$titles = $command->queryColumn();
$post1 = $command->bindValue(':id', 1)->queryOne();
$post2 = $command->bindValue(':id', 2)->queryOne();
``` ```
クエリがスカラ値だけを返す場合は、`queryScalar` を使います。 [[yii\db\Command::bindParam()|bindParam()]] はパラメータを参照渡しでバインドすることをサポートしていますので、上記のコードは次のように書くことも出来ます。
```php ```php
$command = $connection->createCommand('SELECT COUNT(*) FROM post'); $command = $db->createCommand('SELECT * FROM post WHERE id=:id')
$postCount = $command->queryScalar(); ->bindParam(':id', $id);
$id = 1;
$post1 = $command->queryOne();
$id = 2;
$post2 = $command->queryOne();
``` ```
### 値を返さないクエリを実行する クエリの実行の前にプレースホルダを変数 `$id` にバインドし、そして、後に続く各回の実行の前にその変数の値を変更していること (これは、たいてい、ループで行います) に着目してください。
このやり方でクエリを実行すると、パラメータの値が違うごとに新しいクエリを実行するのに比べて、はるかに効率が良くすることが出来ます。
実行されるクエリがデータを一つも返さない場合、例えば、INSERT、UPDATE、DELETE などの場合は、コマンドの `execute` メソッドを使うことが出来ます。
### SELECT しないクエリを実行する <a name="non-select-queries"></a>
今までのセクションで紹介した `queryXyz()` メソッドは、すべて、データベースからデータを取得する SELECT クエリを扱うものでした。
データを返さないクエリのためには、代りに [[yii\db\Command::execute()]] メソッドを呼ばなければなりません。
例えば、
```php ```php
$command = $connection->createCommand('UPDATE post SET status=1 WHERE id=1'); $db->createCommand('UPDATE post SET status=1 WHERE id=1')
$command->execute(); ->execute();
``` ```
あるいはまた、専用の `insert``update``delete` のメソッドを使うことも出来ます。 [[yii\db\Command::execute()]] メソッドは SQL の実行によって影響を受けた行の数を返します。
これらのメソッドは、クエリの中で使用されるテーブルとカラムの名前を引用符で適切に囲んでくれますので、あなたは単に必要な値を提供するだけで済みます。
[[Ought to put a link to the reference docs here.]] INSERT、UPDATE および DELETE クエリのためには、素の SQL を書く代りに、それぞれ、[[yii\db\Command::insert()|insert()]]、[[yii\db\Command::update()|update()]]、[[yii\db\Command::delete()|delete()]] を呼んで、対応する SQL を構築することが出来ます。
これらのメソッドは、テーブルとカラムの名前を適切に引用符で囲み、パラメータの値をバインドします。
例えば、
```php ```php
// INSERT // INSERT (テーブル名, カラムの値)
$connection->createCommand()->insert('user', [ $db->createCommand()->insert('user', [
'name' => 'Sam', 'name' => 'Sam',
'age' => 30, 'age' => 30,
])->execute(); ])->execute();
// INSERT 複数行を一度に // UPDATE (テーブル名, カラムの値, 条件)
$connection->createCommand()->batchInsert('user', ['name', 'age'], [ $db->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();
// DELETE (テーブル名, 条件)
$db->createCommand()->delete('user', 'status = 0')->execute();
```
[[yii\db\Command::batchInsert()|batchInsert()]] を呼んで複数の行を一気に挿入することも出来ます。
この方法は、一度に一行を挿入するよりはるかに効率的です。
```php
// テーブル名, カラム名, カラムの値
$db->createCommand()->batchInsert('user', ['name', 'age'], [
['Tom', 30], ['Tom', 30],
['Jane', 20], ['Jane', 20],
['Linda', 25], ['Linda', 25],
])->execute(); ])->execute();
```
// UPDATE ## テーブルとカラムの名前を引用符で囲む <a name="quoting-table-and-column-names"></a>
$connection->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();
// DELETE 特定のデータベースに依存しないコードを書くときには、テーブルとカラムの名前を適切に引用符で囲むことが、たいてい、頭痛の種になります。
$connection->createCommand()->delete('user', 'status = 0')->execute(); データベースによって名前を引用符で囲む規則がさまざまに異なるからです。
``` この問題を克服するために、次のように、Yii によって導入された引用符の構文を使用することが出来ます。
テーブルとカラムの名前を引用符で囲む <a name="quoting-table-and-column-names"></a> * `[[カラム名]]`: 引用符で囲まれるカラム名を二重角括弧で包む。
------------------------------------ * `{{テーブル名}}`: 引用符で囲まれるテーブル名を二重波括弧で包む。
テーブルとカラムの名前をクエリの中で安全に使えるようにするために、Yii にそれらの名前を引用符で適切に囲ませることが出来ます。 Yii DAO は、SQL に含まれるこのような構文を、対応する適切な引用符で囲まれたカラム名とテーブル名に自動的に変換します。
例えば、
```php ```php
$sql = "SELECT COUNT([[$column]]) FROM {{table}}"; // MySQL では SELECT COUNT(`id`) FROM `employee` という SQL が実行される
$rowCount = $connection->createCommand($sql)->queryScalar(); $count = $db->createCommand("SELECT COUNT([[id]]) FROM {{employee}}")
->queryScalar();
``` ```
上記のコードにおいて、`[[$column]]` は引用符で適切に囲まれたカラム名に変換され、`{{table}}` は引用符で適切に囲まれたテーブル名に変換されます。 ### テーブルプレフィックスを使う <a name="using-table-prefix"></a>
この構文には、テーブル名に限定された特別な変種があります。 あなたの DB テーブルのほとんどが何か共通のプレフィックスを持っている場合は、Yii DAO によってサポートされているテーブルプレフィックスの機能を使うことが出来ます。
それは、`{{%Y}}` が、提供された値にアプリケーションのテーブル接頭辞を (テーブル接頭辞がセットされている場合は) 自動的に追加する、というものです。
```php 最初に、[[yii\db\Connection::tablePrefix]] プロパティによって、テーブルプレフィックスを指定します。
$sql = "SELECT COUNT([[$column]]) FROM {{%table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();
```
上記のコードは、テーブル接頭辞をそのように構成している場合は、`tbl_table` からセレクトする結果になります。
```php ```php
return [ return [
...@@ -234,106 +287,74 @@ return [ ...@@ -234,106 +287,74 @@ return [
]; ];
``` ```
もう一つの選択肢は、[[yii\db\Connection::quoteTableName()]] と [[yii\db\Connection::quoteColumnName()]] を使って、手作業でテーブル名とカラム名を引用符で囲むことです。 そして、あなたのコードの中で、そのテーブルプレフィックスを名前に持つテーブルを参照しなければならないときには、いつでも `{{%テーブル名}}` という構文を使います。
パーセント記号は DB 接続を構成したときに指定したテーブルプレフィックスに自動的に置き換えられます。
例えば、
```php ```php
$column = $connection->quoteColumnName($column); // MySQL では SELECT COUNT(`id`) FROM `tbl_employee` という SQL が実行される
$table = $connection->quoteTableName($table); $count = $db->createCommand("SELECT COUNT([[id]]) FROM {{%employee}}")
$sql = "SELECT COUNT($column) FROM $table"; ->queryScalar();
$rowCount = $connection->createCommand($sql)->queryScalar();
``` ```
プリペアドステートメントを使用する ## トランザクションを実行する <a name="performing-transactions"></a>
----------------------------------
クエリのパラメータを安全にクエリに渡すために、プリペアドステートメントを利用すべきです。 一続きになった複数の関連するクエリを実行するときは、データの整合性を一貫性を保証するために、一連のクエリをトランザクションで囲む必要がある場合があります。
最初に、クエリの中に (`:placeholder` という構文を使って) 名前付きのプレースホルダを作ります。 一連のクエリのどの一つが失敗した場合でも、データベースは、何一つクエリが実行されなかったかのような状態へとロールバックされます。
次に、プレースホルダに値をバインドしてクエリを実行します。
```php
$command = $connection->createCommand('SELECT * FROM post WHERE id=:id');
$command->bindValue(':id', $_GET['id']);
$post = $command->queryOne();
```
プリペアドステートメントを使うもう一つの目的は (セキュリティの向上のほかに)、一度だけ準備したクエリを複数回実行することが出来るという点にあります。 次のコードはトランザクションの典型的な使用方法を示すものです。
```php ```php
$command = $connection->createCommand('DELETE FROM post WHERE id=:id'); $db->transaction(function($db) {
$command->bindParam(':id', $id); $db->createCommand($sql1)->execute();
$db->createCommand($sql2)->execute();
$id = 1; // ... その他の SQL 文を実行 ...
$command->execute(); });
$id = 2;
$command->execute();
``` ```
クエリの実行の前にプレースホルダを変数にバインドすること、そして、後に続く各回の実行の前に変数の値を変更すること (たいていはループで行います) に着目してください。 上記のコードは、次のものと等価です。
このやり方でクエリを実行すると、各クエリを一つずつ実行するのに比べて、はるかに効率が良くなることがあります。
トランザクションを実行する
--------------------------
一続きになった複数の関連するクエリを実行するときは、データの整合性を保護するために、一連のクエリをトランザクションで囲む必要がある場合があります。
トランザクションを使うと、全て成功するか、さもなくば、いかなる結果ももたらさない、という動作をする一連のクエリを書くことが出来ます。
Yii はトランザクションを扱うシンプルなインタフェイスを提供して、単純な場合だけでなく、分離レベルを定義する必要があるような高度な用法にも対応しています。
次のコードが示しているシンプルなパターンは、クエリにトランザクションを使用する全てのコードが従うべきものです。
```php ```php
$transaction = $connection->beginTransaction(); $transaction = $db->beginTransaction();
try { try {
$connection->createCommand($sql1)->execute(); $db->createCommand($sql1)->execute();
$connection->createCommand($sql2)->execute(); $db->createCommand($sql2)->execute();
// ... その他の SQL 文を実行 ... // ... その他の SQL 文を実行 ...
$transaction->commit(); $transaction->commit();
} catch(\Exception $e) { } catch(\Exception $e) {
$transaction->rollBack(); $transaction->rollBack();
throw $e; throw $e;
} }
``` ```
最初の行で、データベース接続オブジェクトの [[yii\db\Connection::beginTransaction()|beginTransaction()]] メソッドを使って、新しいトランザクションを開始しています。 [[yii\db\Connection::beginTransaction()|beginTransaction()]] メソッドを呼んで、新しいトランザクションを開始します。
トランザクション自体は、`$transaction` に保存された [[yii\db\Transaction]] オブジェクトとして表現されています。 トランザクションは、変数 `$transaction` に保存された [[yii\db\Transaction]] オブジェクトとして表現されています。
エラー処理を可能にするために、全てのクエリを try-catch ブロックで囲みます。 そして、実行されるクエリを `try...catch...` ブロックで囲みます。
成功した場合には [[yii\db\Transaction::commit()|commit()]] を呼んでトランザクションをコミットし、エラーが発生した場合には [[yii\db\Transaction::rollBack()|rollBack()]] を呼びます。 全てのクエリの実行が成功した場合には [[yii\db\Transaction::commit()|commit()]] を呼んでトランザクションをコミットします。
`rollBack` は、トランザクションの内側で実行された全てのクエリの結果を取り消します。 そうでなければ、例外がトリガされてキャッチされ、[[yii\db\Transaction::rollBack()|rollBack()]] が呼ばれて、失敗したクエリに先行するクエリがトランザクションの中で行った変更がロールバックされます。
`throw $e` は、私たち自身ではエラーを処理することが出来ない場合に、例外を再スローするために用いられます。
これによって、他のコード、または Yii のエラーハンドラにエラー処理を委譲しています。
必要であれば、複数のトランザクションを入れ子にすることも可能です。
```php ### 分離レベルを指定する <a name="specifying-isolation-levels"></a>
// 外側のトランザクション
$transaction1 = $connection->beginTransaction();
try {
$connection->createCommand($sql1)->execute();
// 内側のトランザクション
$transaction2 = $connection->beginTransaction();
try {
$connection->createCommand($sql2)->execute();
$transaction2->commit();
} catch (Exception $e) {
$transaction2->rollBack();
}
$transaction1->commit(); Yii は、トランザクションの [分離レベル] の設定もサポートしています。
} catch (Exception $e) { デフォルトでは、新しいトランザクションを開始したときは、データベースシステムによって設定された分離レベルを使用します。
$transaction1->rollBack(); デフォルトの分離レベルは、次のようにしてオーバーライドすることが出来ます。
}
```
これが期待通りの動作をするためには、あなたの DBMS が SAVEPOINT をサポートしていなければならないことに注意してください。 ```php
上記のコードはどのような DBMS でも動きますが、トランザクションの安全性が保証されるのは、背後の DBMS がそれをサポートしている場合だけです。 $isolationLevel = \yii\db\Transaction::REPEATABLE_READ;
Yii は、また、トランザクションの [分離レベル] の設定もサポートしています。 $db->transaction(function ($db) {
トランザクションを開始したとき、トランザクションはデータベースによって設定されたデフォルトの分離レベルで実行されます。 ....
トランザクションを開始するときに、分離レベルを明示的に指定することが出来ます。 }, $isolationLevel);
// あるいは
```php $transaction = $db->beginTransaction($isolationLevel);
$transaction = $connection->beginTransaction(\yii\db\Transaction::REPEATABLE_READ);
``` ```
Yii は、最もよく使われる分離レベルのために、四つの定数を提供しています。 Yii は、最もよく使われる分離レベルのために、四つの定数を提供しています。
...@@ -342,14 +363,13 @@ Yii 縺ッ縲∵怙繧ゅh縺丈スソ繧上l繧句髮「繝ャ繝吶Ν縺ョ縺溘a縺ォ縲∝屁縺、縺ョ螳壽焚 ...@@ -342,14 +363,13 @@ Yii 縺ッ縲∵怙繧ゅh縺丈スソ繧上l繧句髮「繝ャ繝吶Ν縺ョ縺溘a縺ォ縲∝屁縺、縺ョ螳壽焚
- [[\yii\db\Transaction::READ_COMMITTED]] - ダーティーリードを回避。 - [[\yii\db\Transaction::READ_COMMITTED]] - ダーティーリードを回避。
- [[\yii\db\Transaction::REPEATABLE_READ]] - ダーティーリードと非再現リードを回避。 - [[\yii\db\Transaction::REPEATABLE_READ]] - ダーティーリードと非再現リードを回避。
- [[\yii\db\Transaction::SERIALIZABLE]] - 最も強いレベル。上記の問題を全て回避。 - [[\yii\db\Transaction::SERIALIZABLE]] - 最も強いレベル。上記の問題を全て回避。
分離レベルを指定するためには、上記の定数を使う以外に、あなたが使っている DBMS によってサポートされている有効な構文の文字列を使うことも出来ます。
上記の定数を使うことも出来ますが、あなたの DBMS で `SET TRANSACTION ISOLATION LEVEL` に続けて書くことが出来る、文法として有効な文字列を使うことも出来ます。 例えば、PostreSQL では、`SERIALIZABLE READ ONLY DEFERRABLE` を使うことが出来ます。
たとえば、postgres であれば、`SERIALIZABLE READ ONLY DEFERRABLE` を使っても構いません。
DBMS によっては、接続全体に対してのみ分離レベルの設定を許容しているものがあることに注意してください。 DBMS によっては、接続全体に対してのみ分離レベルの設定を許容しているものがあることに注意してください。
そのような DBMS の場合、いったん分離レベルを指定すると、後続のトランザクションは、指定しなくても、同じ分離レベルで実行されることになります。 その場合、すべての後続のトランザクションは、指定しなくても、同じ分離レベルで実行されます。
従って、この機能を使用するときは、相反する設定を避けるために、全てのトランザクションについて分離レベルを明示的に指定しなければなりません。 従って、この機能を使用するときは、相反する設定を避けるために、全てのトランザクションについて分離レベルを明示的に指定しなければなりません。
このチュートリアルを書いている時点では、これに該当する DBMS は MSSQL と SQLite です。 このチュートリアルを書いている時点では、これに該当する DBMS は MSSQL と SQLite だけです。
> Note|注意: SQLite は、二つの分離レベルしかサポートしていません。すなわち、`READ UNCOMMITTED` と `SERIALIZABLE` しか使えません。 > Note|注意: SQLite は、二つの分離レベルしかサポートしていません。すなわち、`READ UNCOMMITTED` と `SERIALIZABLE` しか使えません。
他のレベルを使おうとすると、例外が投げられます。 他のレベルを使おうとすると、例外が投げられます。
...@@ -360,9 +380,43 @@ DBMS 縺ォ繧医▲縺ヲ縺ッ縲∵磁邯壼菴薙↓蟇セ縺励※縺ョ縺ソ蛻屬繝ャ繝吶Ν縺ョ險ュ螳壹r ...@@ -360,9 +380,43 @@ DBMS 縺ォ繧医▲縺ヲ縺ッ縲∵磁邯壼菴薙↓蟇セ縺励※縺ョ縺ソ蛻屬繝ャ繝吶Ν縺ョ險ュ螳壹r
[分離レベル]: http://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%88%86%E9%9B%A2%E3%83%AC%E3%83%99%E3%83%AB [分離レベル]: http://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%88%86%E9%9B%A2%E3%83%AC%E3%83%99%E3%83%AB
### トランザクションを入れ子にする <a name="nesting-transactions"></a>
あなたの DBMS が Savepoint をサポートしている場合は、次のように、複数のトランザクションを入れ子にすることが出来ます。
レプリケーションと読み書きの分離 ```php
-------------------------------- $db->transaction(function ($db) {
// 外側のトランザクション
$db->transaction(function ($db) {
// 内側のトランザクション
});
});
```
あるいは、
```php
$outerTransaction = $db->beginTransaction();
try {
$db->createCommand($sql1)->execute();
$innerTransaction = $db->beginTransaction();
try {
$db->createCommand($sql2)->execute();
$innerTransaction->commit();
} catch (Exception $e) {
$innerTransaction->rollBack();
}
$outerTransaction->commit();
} catch (Exception $e) {
$outerTransaction->rollBack();
}
```
## レプリケーションと読み書きの分離 <a name="read-write-splitting"></a>
多くの DBMS は、データベースの可用性とサーバのレスポンスタイムを向上させるために、[データベースレプリケーション](http://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3#.E3.83.87.E3.83.BC.E3.82.BF.E3.83.99.E3.83.BC.E3.82.B9) をサポートしています。 多くの DBMS は、データベースの可用性とサーバのレスポンスタイムを向上させるために、[データベースレプリケーション](http://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3#.E3.83.87.E3.83.BC.E3.82.BF.E3.83.99.E3.83.BC.E3.82.B9) をサポートしています。
データベースレプリケーションによって、データはいわゆる *マスタサーバ* から *スレーブサーバ* に複製されます。 データベースレプリケーションによって、データはいわゆる *マスタサーバ* から *スレーブサーバ* に複製されます。
...@@ -375,7 +429,7 @@ DBMS 縺ォ繧医▲縺ヲ縺ッ縲∵磁邯壼菴薙↓蟇セ縺励※縺ョ縺ソ蛻屬繝ャ繝吶Ν縺ョ險ュ螳壹r ...@@ -375,7 +429,7 @@ DBMS 縺ォ繧医▲縺ヲ縺ッ縲∵磁邯壼菴薙↓蟇セ縺励※縺ョ縺ソ蛻屬繝ャ繝吶Ν縺ョ險ュ螳壹r
'class' => 'yii\db\Connection', 'class' => 'yii\db\Connection',
// マスタの構成 // マスタの構成
'dsn' => 'dsn for master server', 'dsn' => 'マスタサーバの DSN',
'username' => 'master', 'username' => 'master',
'password' => '', 'password' => '',
...@@ -513,44 +567,43 @@ $rows = $db->useMaster(function ($db) { ...@@ -513,44 +567,43 @@ $rows = $db->useMaster(function ($db) {
直接に `$db->enableSlaves` を false に設定して、全てのクエリをマスタ接続に向けることも出来ます。 直接に `$db->enableSlaves` を false に設定して、全てのクエリをマスタ接続に向けることも出来ます。
データベーススキーマを扱う ## データベーススキーマを扱う <a name="database-schema"></a>
--------------------------
### スキーマ情報を取得する
[[yii\db\Schema]] のインスタンスを次のようにして取得することが出来ます。 Yii DAO は、新しいテーブルを作ったり、テーブルからカラムを削除したりなど、データベーススキーマを操作することを可能にする一揃いのメソッドを提供しています。
以下がそのソッドのリストです。
```php * [[yii\db\Command::createTable()|createTable()]]: テーブルを作成する
$schema = $connection->getSchema(); * [[yii\db\Command::renameTable()|renameTable()]]: テーブルの名前を変更する
``` * [[yii\db\Command::dropTable()|dropTable()]]: テーブルを削除する
* [[yii\db\Command::truncateTable()|truncateTable()]]: テーブルの全ての行を削除する
* [[yii\db\Command::addColumn()|addColumn()]]: カラムを追加する
* [[yii\db\Command::renameColumn()|renameColumn()]]: カラムの名前を変更する
* [[yii\db\Command::dropColumn()|dropColumn()]]: カラムを削除する
* [[yii\db\Command::alterColumn()|alterColumn()]]: カラムを変更する
* [[yii\db\Command::addPrimaryKey()|addPrimaryKey()]]: プライマリキーを追加する
* [[yii\db\Command::dropPrimaryKey()|dropPrimaryKey()]]: プライマリキーを削除する
* [[yii\db\Command::addForeignKey()|addForeignKey()]]: 外部キーを追加する
* [[yii\db\Command::dropForeignKey()|dropForeignKey()]]: 外部キーを削除する
* [[yii\db\Command::createIndex()|createIndex()]]: インデックスを作成する
* [[yii\db\Command::dropIndex()|dropIndex()]]: インデックスを削除する
このオブジェクトが、データベースについてのいろいろなスキーマ情報を読み取ることを可能にする一連のメソッドを持っています。 これらのメソッドは次のようにして使うことが出来ます。
```php ```php
$tables = $schema->getTableNames(); // CREATE TABLE
``` $db->createCommand()->createTable('post', [
完全なリファレンスとしては、[[yii\db\Schema]] を参照してください。
### スキーマを修正する
基本的な SQL クエリに加えて、[[yii\db\Command]] はデータベーススキーマの修正を可能にする一連のメソッドを持っています。
- createTable, renameTable, dropTable, truncateTable
- addColumn, renameColumn, dropColumn, alterColumn
- addPrimaryKey, dropPrimaryKey
- addForeignKey, dropForeignKey
- createIndex, dropIndex
これらは、次のようにして使用することが出来ます。
```php
// TABLE を作成する
$connection->createCommand()->createTable('post', [
'id' => 'pk', 'id' => 'pk',
'title' => 'string', 'title' => 'string',
'text' => 'text', 'text' => 'text',
]); ]);
``` ```
完全なリファレンスとしては、[[yii\db\Command]] を参照してください。 テーブルに関する定義情報を DB 接続の [[yii\db\Connection::getTableSchema()|getTableSchema()]] メソッドによって取得することも出来ます。
例えば、
```php
$table = $db->getTableSchema('post');
```
このメソッドは、テーブルのカラム、プライマリキー、外部キーなどの情報を含む [[yii\db\TableSchema]] オブジェクトを返します。
これらの情報は、主として [クエリビルダ](db-query-builder.md)[アクティブレコード](db-active-record.md) によって利用されて、特定のデータベースに依存しないコードを書くことを助けてくれます。
...@@ -187,6 +187,12 @@ $query->where(['status' => null]); ...@@ -187,6 +187,12 @@ $query->where(['status' => null]);
WHERE (`status` IS NULL) WHERE (`status` IS NULL)
``` ```
`IS NOT NULL` が必要なときは次のように書くことが出来ます。
```php
$query->where(['not', ['col' => null]]);
```
次のように `Query` オブジェクトを使ってサブクエリを作ることも出来ます。 次のように `Query` オブジェクトを使ってサブクエリを作ることも出来ます。
```php ```php
...@@ -443,7 +449,7 @@ foreach ($query->each() as $user) { ...@@ -443,7 +449,7 @@ foreach ($query->each() as $user) {
`batch()` または `each()` メソッドに最初のパラメータを渡すことによって、バッチサイズを変更することが出来ます。 `batch()` または `each()` メソッドに最初のパラメータを渡すことによって、バッチサイズを変更することが出来ます。
[[yii\db\Query::all()]] とは対照的に、バッチクエリは一度に 100 行のデータしかメモリに読み込みません。 [[yii\db\Query::all()]] とは対照的に、バッチクエリは一度に 100 行のデータしかメモリに読み込みません。
データを処理した後、すぐにデータを破棄するようにすれば、バッチクエリの助けを借りてメモリ消費量を限度以下に抑えることが出来ます。 データを処理した後、すぐにデータを破棄するようにすれば、バッチクエリの助けを借りてメモリ消費量を削減することが出来ます。
[[yii\db\Query::indexBy()]] によってクエリ結果をあるカラムでインデックスするように指定している場合でも、バッチクエリは正しいインデックスを保持します。 [[yii\db\Query::indexBy()]] によってクエリ結果をあるカラムでインデックスするように指定している場合でも、バッチクエリは正しいインデックスを保持します。
例えば、 例えば、
......
...@@ -31,7 +31,7 @@ ID を指定しない場合は、JS コードそれ自身が ID として扱わ ...@@ -31,7 +31,7 @@ ID を指定しない場合は、JS コードそれ自身が ID として扱わ
外部スクリプトは次のようにして追加することが出来ます。 外部スクリプトは次のようにして追加することが出来ます。
```php ```php
$this->registerJsFile('http://example.com/js/main.js', ['depends' => [JqueryAsset::className()]]); $this->registerJsFile('http://example.com/js/main.js', ['depends' => [\yii\web\JqueryAsset::className()]]);
``` ```
[[yii\web\View::registerJsFile()|registerJsFile()]] の引数は [[yii\web\View::registerCssFile()|registerCssFile()]] のそれと同じです。 [[yii\web\View::registerJsFile()|registerJsFile()]] の引数は [[yii\web\View::registerCssFile()|registerCssFile()]] のそれと同じです。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment