|
|
|
@ -1,31 +1,38 @@
|
|
|
|
|
データベースアクセスオブジェクト |
|
|
|
|
================================ |
|
|
|
|
|
|
|
|
|
> 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) の上に構築されたデータベースアクセスレイヤを含んでいます。 |
|
|
|
|
データベースアクセスオブジェクト (DAO) のインタフェイスは、統一された API を提供し、さまざまなデータベース製品間に存在する不統一のいくらかを解決します。 |
|
|
|
|
アクティブレコードは、モデルを通じてのデータベースとの相互作用を提供し、クエリビルダは、動的なクエリの作成を支援します。 |
|
|
|
|
一方、DAO はデータベースに対して直接に SQL を実行する単純で効率的な方法を提供します。 |
|
|
|
|
実行すべきクエリが高価なものである場合、かつ/または、アプリケーションモデル (および対応するビジネスロジック) が必要でない場合に、あなたは DAO を使いたいと思うでしょう。 |
|
|
|
|
Yii DAO を使うときは、主として素の SQL と PHP 配列を扱う必要があります。 |
|
|
|
|
結果として、Yii DAO はデータベースにアクセスする方法としては最も効率的なものになります。 |
|
|
|
|
しかし、SQL の構文はデータベースによってさまざまに異なる場合がありますので、Yii DAO を使用するということは、特定のデータベースに依存しないアプリケーションを作るためには追加の労力が必要になる、ということをも同時に意味します。 |
|
|
|
|
|
|
|
|
|
Yii はデフォルトで下記の DBMS をサポートしています。 |
|
|
|
|
Yii は下記の DBMS のサポートを内蔵しています。 |
|
|
|
|
|
|
|
|
|
- [MySQL](http://www.mysql.com/) |
|
|
|
|
- [MariaDB](https://mariadb.com/) |
|
|
|
|
- [SQLite](http://sqlite.org/) |
|
|
|
|
- [PostgreSQL](http://www.postgresql.org/) |
|
|
|
|
- [CUBRID](http://www.cubrid.org/): バージョン 9.3 以上。(cubrid PDO 拡張の [バグ](http://jira.cubrid.org/browse/APIS-658) |
|
|
|
|
のために、値を引用符で囲む機能が動作しません。そのため、サーバだけでなくクライアントも CUBRID 9.3 が必要になります) |
|
|
|
|
- [CUBRID](http://www.cubrid.org/): バージョン 9.3 以上。 |
|
|
|
|
- [Oracle](http://www.oracle.com/us/products/database/overview/index.html) |
|
|
|
|
- [MSSQL](https://www.microsoft.com/en-us/sqlserver/default.aspx): バージョン 2008 以上。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
構成 |
|
|
|
|
---- |
|
|
|
|
## DB 接続を作成する <a name="creating-db-connections"></a> |
|
|
|
|
|
|
|
|
|
データベースとの相互作用を開始するためには (DAO を使うにしろ使わないにしろ)、アプリケーションのデータベース接続コンポーネントを構成する必要があります。 |
|
|
|
|
データソース名 (DSN) が、どのデータベース製品のどの特定のデータベースにアプリケーションが接続すべきかを構成します。 |
|
|
|
|
データベースにアクセスするために、まずは、データベースに接続するために [[yii\db\Connection]] のインスタンスを作成する必要があります。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$db = new yii\db\Connection([ |
|
|
|
|
'dsn' => 'mysql:host=localhost;dbname=example', |
|
|
|
|
'username' => 'root', |
|
|
|
|
'password' => '', |
|
|
|
|
'charset' => 'utf8', |
|
|
|
|
]); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
DB 接続は、たいていは、さまざまな場所でアクセスする必要がありますので、次のように、[アプリケーションコンポーネント](structure-application-components.md) の形式で構成するのが通例です。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
return [ |
|
|
|
@ -34,14 +41,7 @@ return [
|
|
|
|
|
// ... |
|
|
|
|
'db' => [ |
|
|
|
|
'class' => 'yii\db\Connection', |
|
|
|
|
'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB |
|
|
|
|
//'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 |
|
|
|
|
'dsn' => 'mysql:host=localhost;dbname=example', |
|
|
|
|
'username' => 'root', |
|
|
|
|
'password' => '', |
|
|
|
|
'charset' => 'utf8', |
|
|
|
@ -51,8 +51,23 @@ return [
|
|
|
|
|
]; |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
DSN 文字列のフォーマットに関する詳細は、[PHP マニュアル](http://www.php.net/manual/ja/function.PDO-construct.php) を参照してください。 |
|
|
|
|
このクラスで構成可能なプロパティの全てのリストについては、[[yii\db\Connection]] を参照してください。 |
|
|
|
|
こうすると `Yii::$app->db` という式で DB 接続にアクセスすることが出来るようになります。 |
|
|
|
|
|
|
|
|
|
> 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 に実際のデータベースのタイプを知らせなければならないことに注意してください。 |
|
|
|
|
例えば、 |
|
|
|
@ -67,50 +82,50 @@ ODBC 経由でデータベースに接続しようとする場合は、[[yii\db\
|
|
|
|
|
], |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
主たる `db` 接続には、`\Yii::$app->db` という式によってアクセスすることが出来ます。 |
|
|
|
|
一つのアプリケーションで複数の DB 接続を構成することも出来ます。 |
|
|
|
|
アプリケーションの構成情報において、それらに別々の ID を割り当てるだけのことです。 |
|
|
|
|
[[yii\db\Connection::dsn|dsn]] プロパティに加えて、たいていは [[yii\db\Connection::username|username]] と [[yii\db\Connection::password|password]] も構成しなければなりません。 |
|
|
|
|
構成可能なプロパティの全てのリストは [[yii\db\Connection]] を参照して下さい。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
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', |
|
|
|
|
], |
|
|
|
|
], |
|
|
|
|
// ... |
|
|
|
|
]; |
|
|
|
|
``` |
|
|
|
|
> Info|情報: DB 接続のインスタンスを作成するとき、実際のデータベース接続は、最初の SQL を実行するか、[[yii\db\Connection::open()|open()]] メソッドを明示的に呼ぶかするまでは確立されません。 |
|
|
|
|
|
|
|
|
|
これで、必要に応じて両方のデータベース接続を同時に使用することが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$primaryConnection = \Yii::$app->db; |
|
|
|
|
$secondaryConnection = \Yii::$app->secondDb; |
|
|
|
|
``` |
|
|
|
|
## SQL クエリを実行する <a name="executing-sql-queries"></a> |
|
|
|
|
|
|
|
|
|
いったんデータベース接続のインスタンスを得てしまえば、次の手順に従って SQL クエリを実行することが出来ます。 |
|
|
|
|
|
|
|
|
|
1. 素の SQL で [[yii\db\Command]] を作成する。 |
|
|
|
|
2. パラメータをバインドする (オプション)。 |
|
|
|
|
3. [[yii\db\Command]] の SQL 実行メソッドの一つを呼ぶ。 |
|
|
|
|
|
|
|
|
|
DB 接続を [アプリケーションコンポーネント](structure-application-components.md) として定義したくない場合は、インスタンスを直接に作成することも出来ます。 |
|
|
|
|
次に、データベースからデータを読み出すさまざまな方法を例示します。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$connection = new \yii\db\Connection([ |
|
|
|
|
'dsn' => $dsn, |
|
|
|
|
'username' => $username, |
|
|
|
|
'password' => $password, |
|
|
|
|
]); |
|
|
|
|
$connection->open(); |
|
|
|
|
$db = new yii\db\Connection(...); |
|
|
|
|
|
|
|
|
|
// 行のセットを返す。各行は、カラム名と値の連想配列。 |
|
|
|
|
// 結果が無い場合は空の配列が返される。 |
|
|
|
|
$posts = $db->createCommand('SELECT * FROM post') |
|
|
|
|
->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 |
|
|
|
|
return [ |
|
|
|
@ -121,6 +136,7 @@ return [
|
|
|
|
|
'class' => 'yii\db\Connection', |
|
|
|
|
// ... |
|
|
|
|
'on afterOpen' => function($event) { |
|
|
|
|
// $event->sender は DB 接続を指す |
|
|
|
|
$event->sender->createCommand("SET time_zone = 'UTC'")->execute(); |
|
|
|
|
} |
|
|
|
|
], |
|
|
|
@ -129,97 +145,134 @@ return [
|
|
|
|
|
]; |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
基本的な SQL クエリを実行する |
|
|
|
|
----------------------------- |
|
|
|
|
|
|
|
|
|
いったんデータベース接続のインスタンスを得てしまえば、[[yii\db\Command]] を使って SQL クエリを実行することが出来るようになります。 |
|
|
|
|
### パラメータをバインドする <a name="binding-parameters"></a> |
|
|
|
|
|
|
|
|
|
### SELECT クエリを実行する |
|
|
|
|
|
|
|
|
|
実行されるクエリが行のセットを返す場合は、`queryAll` を使います。 |
|
|
|
|
パラメータを持つ SQL から DB コマンドを作成するときは、SQL インジェクション攻撃を防止するために、ほとんど全ての場合においてパラメータをバインドする手法を用いるべきです。 |
|
|
|
|
例えば、 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$command = $connection->createCommand('SELECT * FROM post'); |
|
|
|
|
$posts = $command->queryAll(); |
|
|
|
|
$post = $db->createCommand('SELECT * FROM post WHERE id=:id AND status=:status') |
|
|
|
|
->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 |
|
|
|
|
$command = $connection->createCommand('SELECT * FROM post WHERE id=1'); |
|
|
|
|
$post = $command->queryOne(); |
|
|
|
|
$params = [':id' => $_GET['id'], ':status' => 1]; |
|
|
|
|
|
|
|
|
|
$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 |
|
|
|
|
$command = $connection->createCommand('SELECT title FROM post'); |
|
|
|
|
$titles = $command->queryColumn(); |
|
|
|
|
$command = $db->createCommand('SELECT * FROM post WHERE id=:id'); |
|
|
|
|
|
|
|
|
|
$post1 = $command->bindValue(':id', 1)->queryOne(); |
|
|
|
|
$post2 = $command->bindValue(':id', 2)->queryOne(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
クエリがスカラ値だけを返す場合は、`queryScalar` を使います。 |
|
|
|
|
[[yii\db\Command::bindParam()|bindParam()]] はパラメータを参照渡しでバインドすることをサポートしていますので、上記のコードは次のように書くことも出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$command = $connection->createCommand('SELECT COUNT(*) FROM post'); |
|
|
|
|
$postCount = $command->queryScalar(); |
|
|
|
|
$command = $db->createCommand('SELECT * FROM post WHERE id=:id') |
|
|
|
|
->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 |
|
|
|
|
$command = $connection->createCommand('UPDATE post SET status=1 WHERE id=1'); |
|
|
|
|
$command->execute(); |
|
|
|
|
$db->createCommand('UPDATE post SET status=1 WHERE id=1') |
|
|
|
|
->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 |
|
|
|
|
// INSERT |
|
|
|
|
$connection->createCommand()->insert('user', [ |
|
|
|
|
// INSERT (テーブル名, カラムの値) |
|
|
|
|
$db->createCommand()->insert('user', [ |
|
|
|
|
'name' => 'Sam', |
|
|
|
|
'age' => 30, |
|
|
|
|
])->execute(); |
|
|
|
|
|
|
|
|
|
// INSERT 複数行を一度に |
|
|
|
|
$connection->createCommand()->batchInsert('user', ['name', 'age'], [ |
|
|
|
|
// UPDATE (テーブル名, カラムの値, 条件) |
|
|
|
|
$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], |
|
|
|
|
['Jane', 20], |
|
|
|
|
['Linda', 25], |
|
|
|
|
])->execute(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
// UPDATE |
|
|
|
|
$connection->createCommand()->update('user', ['status' => 1], 'age > 30')->execute(); |
|
|
|
|
## テーブルとカラムの名前を引用符で囲む <a name="quoting-table-and-column-names"></a> |
|
|
|
|
|
|
|
|
|
// DELETE |
|
|
|
|
$connection->createCommand()->delete('user', 'status = 0')->execute(); |
|
|
|
|
``` |
|
|
|
|
特定のデータベースに依存しないコードを書くときには、テーブルとカラムの名前を適切に引用符で囲むことが、たいてい、頭痛の種になります。 |
|
|
|
|
データベースによって名前を引用符で囲む規則がさまざまに異なるからです。 |
|
|
|
|
この問題を克服するために、次のように、Yii によって導入された引用符の構文を使用することが出来ます。 |
|
|
|
|
|
|
|
|
|
テーブルとカラムの名前を引用符で囲む <a name="quoting-table-and-column-names"></a> |
|
|
|
|
------------------------------------ |
|
|
|
|
* `[[カラム名]]`: 引用符で囲まれるカラム名を二重角括弧で包む。 |
|
|
|
|
* `{{テーブル名}}`: 引用符で囲まれるテーブル名を二重波括弧で包む。 |
|
|
|
|
|
|
|
|
|
テーブルとカラムの名前をクエリの中で安全に使えるようにするために、Yii にそれらの名前を引用符で適切に囲ませることが出来ます。 |
|
|
|
|
Yii DAO は、SQL に含まれるこのような構文を、対応する適切な引用符で囲まれたカラム名とテーブル名に自動的に変換します。 |
|
|
|
|
例えば、 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$sql = "SELECT COUNT([[$column]]) FROM {{table}}"; |
|
|
|
|
$rowCount = $connection->createCommand($sql)->queryScalar(); |
|
|
|
|
// MySQL では SELECT COUNT(`id`) FROM `employee` という SQL が実行される |
|
|
|
|
$count = $db->createCommand("SELECT COUNT([[id]]) FROM {{employee}}") |
|
|
|
|
->queryScalar(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
上記のコードにおいて、`[[$column]]` は引用符で適切に囲まれたカラム名に変換され、`{{table}}` は引用符で適切に囲まれたテーブル名に変換されます。 |
|
|
|
|
### テーブルプレフィックスを使う <a name="using-table-prefix"></a> |
|
|
|
|
|
|
|
|
|
この構文には、テーブル名に限定された特別な変種があります。 |
|
|
|
|
それは、`{{%Y}}` が、提供された値にアプリケーションのテーブル接頭辞を (テーブル接頭辞がセットされている場合は) 自動的に追加する、というものです。 |
|
|
|
|
あなたの DB テーブルのほとんどが何か共通のプレフィックスを持っている場合は、Yii DAO によってサポートされているテーブルプレフィックスの機能を使うことが出来ます。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$sql = "SELECT COUNT([[$column]]) FROM {{%table}}"; |
|
|
|
|
$rowCount = $connection->createCommand($sql)->queryScalar(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
上記のコードは、テーブル接頭辞をそのように構成している場合は、`tbl_table` からセレクトする結果になります。 |
|
|
|
|
最初に、[[yii\db\Connection::tablePrefix]] プロパティによって、テーブルプレフィックスを指定します。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
return [ |
|
|
|
@ -234,106 +287,74 @@ return [
|
|
|
|
|
]; |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
もう一つの選択肢は、[[yii\db\Connection::quoteTableName()]] と [[yii\db\Connection::quoteColumnName()]] を使って、手作業でテーブル名とカラム名を引用符で囲むことです。 |
|
|
|
|
そして、あなたのコードの中で、そのテーブルプレフィックスを名前に持つテーブルを参照しなければならないときには、いつでも `{{%テーブル名}}` という構文を使います。 |
|
|
|
|
パーセント記号は DB 接続を構成したときに指定したテーブルプレフィックスに自動的に置き換えられます。 |
|
|
|
|
例えば、 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$column = $connection->quoteColumnName($column); |
|
|
|
|
$table = $connection->quoteTableName($table); |
|
|
|
|
$sql = "SELECT COUNT($column) FROM $table"; |
|
|
|
|
$rowCount = $connection->createCommand($sql)->queryScalar(); |
|
|
|
|
// MySQL では SELECT COUNT(`id`) FROM `tbl_employee` という SQL が実行される |
|
|
|
|
$count = $db->createCommand("SELECT COUNT([[id]]) FROM {{%employee}}") |
|
|
|
|
->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 |
|
|
|
|
$command = $connection->createCommand('DELETE FROM post WHERE id=:id'); |
|
|
|
|
$command->bindParam(':id', $id); |
|
|
|
|
|
|
|
|
|
$id = 1; |
|
|
|
|
$command->execute(); |
|
|
|
|
|
|
|
|
|
$id = 2; |
|
|
|
|
$command->execute(); |
|
|
|
|
$db->transaction(function($db) { |
|
|
|
|
$db->createCommand($sql1)->execute(); |
|
|
|
|
$db->createCommand($sql2)->execute(); |
|
|
|
|
// ... その他の SQL 文を実行 ... |
|
|
|
|
}); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
クエリの実行の前にプレースホルダを変数にバインドすること、そして、後に続く各回の実行の前に変数の値を変更すること (たいていはループで行います) に着目してください。 |
|
|
|
|
このやり方でクエリを実行すると、各クエリを一つずつ実行するのに比べて、はるかに効率が良くなることがあります。 |
|
|
|
|
|
|
|
|
|
トランザクションを実行する |
|
|
|
|
-------------------------- |
|
|
|
|
|
|
|
|
|
一続きになった複数の関連するクエリを実行するときは、データの整合性を保護するために、一連のクエリをトランザクションで囲む必要がある場合があります。 |
|
|
|
|
トランザクションを使うと、全て成功するか、さもなくば、いかなる結果ももたらさない、という動作をする一連のクエリを書くことが出来ます。 |
|
|
|
|
Yii はトランザクションを扱うシンプルなインタフェイスを提供して、単純な場合だけでなく、分離レベルを定義する必要があるような高度な用法にも対応しています。 |
|
|
|
|
|
|
|
|
|
次のコードが示しているシンプルなパターンは、クエリにトランザクションを使用する全てのコードが従うべきものです。 |
|
|
|
|
上記のコードは、次のものと等価です。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$transaction = $connection->beginTransaction(); |
|
|
|
|
$transaction = $db->beginTransaction(); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
$connection->createCommand($sql1)->execute(); |
|
|
|
|
$connection->createCommand($sql2)->execute(); |
|
|
|
|
$db->createCommand($sql1)->execute(); |
|
|
|
|
$db->createCommand($sql2)->execute(); |
|
|
|
|
// ... その他の SQL 文を実行 ... |
|
|
|
|
|
|
|
|
|
$transaction->commit(); |
|
|
|
|
|
|
|
|
|
} catch(\Exception $e) { |
|
|
|
|
|
|
|
|
|
$transaction->rollBack(); |
|
|
|
|
|
|
|
|
|
throw $e; |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
最初の行で、データベース接続オブジェクトの [[yii\db\Connection::beginTransaction()|beginTransaction()]] メソッドを使って、新しいトランザクションを開始しています。 |
|
|
|
|
トランザクション自体は、`$transaction` に保存された [[yii\db\Transaction]] オブジェクトとして表現されています。 |
|
|
|
|
エラー処理を可能にするために、全てのクエリを try-catch ブロックで囲みます。 |
|
|
|
|
成功した場合には [[yii\db\Transaction::commit()|commit()]] を呼んでトランザクションをコミットし、エラーが発生した場合には [[yii\db\Transaction::rollBack()|rollBack()]] を呼びます。 |
|
|
|
|
`rollBack` は、トランザクションの内側で実行された全てのクエリの結果を取り消します。 |
|
|
|
|
`throw $e` は、私たち自身ではエラーを処理することが出来ない場合に、例外を再スローするために用いられます。 |
|
|
|
|
これによって、他のコード、または Yii のエラーハンドラにエラー処理を委譲しています。 |
|
|
|
|
[[yii\db\Connection::beginTransaction()|beginTransaction()]] メソッドを呼んで、新しいトランザクションを開始します。 |
|
|
|
|
トランザクションは、変数 `$transaction` に保存された [[yii\db\Transaction]] オブジェクトとして表現されています。 |
|
|
|
|
そして、実行されるクエリを `try...catch...` ブロックで囲みます。 |
|
|
|
|
全てのクエリの実行が成功した場合には [[yii\db\Transaction::commit()|commit()]] を呼んでトランザクションをコミットします。 |
|
|
|
|
そうでなければ、例外がトリガされてキャッチされ、[[yii\db\Transaction::rollBack()|rollBack()]] が呼ばれて、失敗したクエリに先行するクエリがトランザクションの中で行った変更がロールバックされます。 |
|
|
|
|
|
|
|
|
|
必要であれば、複数のトランザクションを入れ子にすることも可能です。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
// 外側のトランザクション |
|
|
|
|
$transaction1 = $connection->beginTransaction(); |
|
|
|
|
try { |
|
|
|
|
$connection->createCommand($sql1)->execute(); |
|
|
|
|
|
|
|
|
|
// 内側のトランザクション |
|
|
|
|
$transaction2 = $connection->beginTransaction(); |
|
|
|
|
try { |
|
|
|
|
$connection->createCommand($sql2)->execute(); |
|
|
|
|
$transaction2->commit(); |
|
|
|
|
} catch (Exception $e) { |
|
|
|
|
$transaction2->rollBack(); |
|
|
|
|
} |
|
|
|
|
### 分離レベルを指定する <a name="specifying-isolation-levels"></a> |
|
|
|
|
|
|
|
|
|
$transaction1->commit(); |
|
|
|
|
} catch (Exception $e) { |
|
|
|
|
$transaction1->rollBack(); |
|
|
|
|
} |
|
|
|
|
``` |
|
|
|
|
Yii は、トランザクションの [分離レベル] の設定もサポートしています。 |
|
|
|
|
デフォルトでは、新しいトランザクションを開始したときは、データベースシステムによって設定された分離レベルを使用します。 |
|
|
|
|
デフォルトの分離レベルは、次のようにしてオーバーライドすることが出来ます。 |
|
|
|
|
|
|
|
|
|
これが期待通りの動作をするためには、あなたの DBMS が SAVEPOINT をサポートしていなければならないことに注意してください。 |
|
|
|
|
上記のコードはどのような DBMS でも動きますが、トランザクションの安全性が保証されるのは、背後の DBMS がそれをサポートしている場合だけです。 |
|
|
|
|
```php |
|
|
|
|
$isolationLevel = \yii\db\Transaction::REPEATABLE_READ; |
|
|
|
|
|
|
|
|
|
Yii は、また、トランザクションの [分離レベル] の設定もサポートしています。 |
|
|
|
|
トランザクションを開始したとき、トランザクションはデータベースによって設定されたデフォルトの分離レベルで実行されます。 |
|
|
|
|
トランザクションを開始するときに、分離レベルを明示的に指定することが出来ます。 |
|
|
|
|
$db->transaction(function ($db) { |
|
|
|
|
.... |
|
|
|
|
}, $isolationLevel); |
|
|
|
|
|
|
|
|
|
// あるいは |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$transaction = $connection->beginTransaction(\yii\db\Transaction::REPEATABLE_READ); |
|
|
|
|
$transaction = $db->beginTransaction($isolationLevel); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
Yii は、最もよく使われる分離レベルのために、四つの定数を提供しています。 |
|
|
|
@ -342,14 +363,13 @@ Yii は、最もよく使われる分離レベルのために、四つの定数
|
|
|
|
|
- [[\yii\db\Transaction::READ_COMMITTED]] - ダーティーリードを回避。 |
|
|
|
|
- [[\yii\db\Transaction::REPEATABLE_READ]] - ダーティーリードと非再現リードを回避。 |
|
|
|
|
- [[\yii\db\Transaction::SERIALIZABLE]] - 最も強いレベル。上記の問題を全て回避。 |
|
|
|
|
|
|
|
|
|
上記の定数を使うことも出来ますが、あなたの DBMS で `SET TRANSACTION ISOLATION LEVEL` に続けて書くことが出来る、文法として有効な文字列を使うことも出来ます。 |
|
|
|
|
たとえば、postgres であれば、`SERIALIZABLE READ ONLY DEFERRABLE` を使っても構いません。 |
|
|
|
|
分離レベルを指定するためには、上記の定数を使う以外に、あなたが使っている DBMS によってサポートされている有効な構文の文字列を使うことも出来ます。 |
|
|
|
|
例えば、PostreSQL では、`SERIALIZABLE READ ONLY DEFERRABLE` を使うことが出来ます。 |
|
|
|
|
|
|
|
|
|
DBMS によっては、接続全体に対してのみ分離レベルの設定を許容しているものがあることに注意してください。 |
|
|
|
|
そのような DBMS の場合、いったん分離レベルを指定すると、後続のトランザクションは、指定しなくても、同じ分離レベルで実行されることになります。 |
|
|
|
|
その場合、すべての後続のトランザクションは、指定しなくても、同じ分離レベルで実行されます。 |
|
|
|
|
従って、この機能を使用するときは、相反する設定を避けるために、全てのトランザクションについて分離レベルを明示的に指定しなければなりません。 |
|
|
|
|
このチュートリアルを書いている時点では、これに該当する DBMS は MSSQL と SQLite です。 |
|
|
|
|
このチュートリアルを書いている時点では、これに該当する DBMS は MSSQL と SQLite だけです。 |
|
|
|
|
|
|
|
|
|
> Note|注意: SQLite は、二つの分離レベルしかサポートしていません。すなわち、`READ UNCOMMITTED` と `SERIALIZABLE` しか使えません。 |
|
|
|
|
他のレベルを使おうとすると、例外が投げられます。 |
|
|
|
@ -360,9 +380,43 @@ DBMS によっては、接続全体に対してのみ分離レベルの設定を
|
|
|
|
|
|
|
|
|
|
[分離レベル]: 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) をサポートしています。 |
|
|
|
|
データベースレプリケーションによって、データはいわゆる *マスタサーバ* から *スレーブサーバ* に複製されます。 |
|
|
|
@ -375,7 +429,7 @@ DBMS によっては、接続全体に対してのみ分離レベルの設定を
|
|
|
|
|
'class' => 'yii\db\Connection', |
|
|
|
|
|
|
|
|
|
// マスタの構成 |
|
|
|
|
'dsn' => 'dsn for master server', |
|
|
|
|
'dsn' => 'マスタサーバの DSN', |
|
|
|
|
'username' => 'master', |
|
|
|
|
'password' => '', |
|
|
|
|
|
|
|
|
@ -513,44 +567,43 @@ $rows = $db->useMaster(function ($db) {
|
|
|
|
|
直接に `$db->enableSlaves` を false に設定して、全てのクエリをマスタ接続に向けることも出来ます。 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
データベーススキーマを扱う |
|
|
|
|
-------------------------- |
|
|
|
|
|
|
|
|
|
### スキーマ情報を取得する |
|
|
|
|
## データベーススキーマを扱う <a name="database-schema"></a> |
|
|
|
|
|
|
|
|
|
[[yii\db\Schema]] のインスタンスを次のようにして取得することが出来ます。 |
|
|
|
|
Yii DAO は、新しいテーブルを作ったり、テーブルからカラムを削除したりなど、データベーススキーマを操作することを可能にする一揃いのメソッドを提供しています。 |
|
|
|
|
以下がそのソッドのリストです。 |
|
|
|
|
|
|
|
|
|
```php |
|
|
|
|
$schema = $connection->getSchema(); |
|
|
|
|
``` |
|
|
|
|
* [[yii\db\Command::createTable()|createTable()]]: テーブルを作成する |
|
|
|
|
* [[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 |
|
|
|
|
$tables = $schema->getTableNames(); |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
完全なリファレンスとしては、[[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', [ |
|
|
|
|
// CREATE TABLE |
|
|
|
|
$db->createCommand()->createTable('post', [ |
|
|
|
|
'id' => 'pk', |
|
|
|
|
'title' => 'string', |
|
|
|
|
'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) によって利用されて、特定のデータベースに依存しないコードを書くことを助けてくれます。 |
|
|
|
|