1の追加、削除、変更、確認を実現します
。いい
DAO は PHP データ オブジェクト (PDO) に基づいて構築されています。これは、
を含む多くの人気のある DBMS に統合されたデータ アクセスを提供する拡張機能です。MySQL、PostgreSQL など。したがって、Yii DAO、PDO拡張機能、および特定のPDOデータベースドライバー(PDO_MYSQLなど)を使用するには
インストールする必要があります。
Yii DAO には主に次の 4 つのカテゴリが含まれます:
CDbConnection: データベース接続を表します。
CDbCommand: データベースを通じて実行される SQL ステートメントを表します。
CDbDataReader: クエリ結果セットからの行の前方専用ストリームを表します。
CDbTransaction: データベース トランザクションを表します。
1. データベース接続を確立します
欲しい
データベース接続を確立し、CDbConnectionを作成します
インスタンス化してアクティブ化します。データベースに接続するには、接続情報を指定するデータ ソース名 (DSN) が必要です。ユーザー名とパスワードも使用できます。データベース接続時にエラーが発生した場合
(例: 間違った DSN または無効なユーザー名/パスワード)、例外がスローされます。
$connection=新しい CDbConnection($dsn,$ユーザー名,$パスワード);
// 接続を確立します。 try...catch を使用すると、スローされる可能性のある例外をキャッチできます
$connection->active=true;
......
$connection->active=false; // 接続を閉じます
DSN の形式は、使用される PDO データベース ドライバーによって異なります。通常、DSN には PDO ドライバーの名前、コロン、ドライバー固有の接続構文が含まれます。詳細については、PDO ドキュメントを参照してください。以下は、一般的に使用される DSN 形式のリストです。
* SQLite: sqlite:/path/to/dbfile
* MySQL: mysql:host=localhost;dbname=testdb
* PostgreSQL: pgsql:host=localhost;port=5432;dbname=testdb
* SQL サーバー: mssql:host=localhost;dbname=testdb
* Oracle: oci:dbname=//localhost:1521/testdb
CDbConnectionはCApplicationComponentを継承しているので、アプリケーションコンポーネントとしても利用できます。これを行うには、アプリケーション構成で db (または他の名前) アプリケーション コンポーネントを次のように構成します。
配列(......
'コンポーネント'=>配列(
......
'db'=>array(
'クラス'=>'CDbConnection',
'connectionString'=>'mysql:host=localhost;dbname=testdb',
'ユーザー名'=>'root',
'パスワード'=>'パスワード',
'emulatePrepare'=>true, // 一部の MySQL インストールで必要です
)、
)、
)
これで、Yii::app()->db を通じてデータベース接続にアクセスできるようになります。 CDbConnection::autoConnect を false に特別に設定しない限り、これは自動的にアクティブ化されます。こうすることで、この単一の DB 接続をコード内の多くの場所で共有できます。
2. SQLステートメントを実行します
データベース接続が確立されたら、CDbCommand を使用して SQL ステートメントを実行できます。 CDbCommand インスタンスを作成するには、指定した SQL ステートメントを引数として CDbConnection::createCommand() を呼び出します。
$connection=Yii::app()->db; // 「db」接続が確立されていると仮定します
// そうでない場合は、明示的に接続を確立する必要がある場合があります:
// $connection=new CDbConnection($dsn,$username,$password);
$command=$connection->createCommand($sql);
// 必要に応じて、この SQL ステートメントは次のように変更できます:
// $command->text=$newSQL;
SQL ステートメントは、次の 2 つの方法で CDbCommand を通じて実行されます。
execute(): INSERT、UPDATE、DELETE などの非クエリ SQL ステートメントを実行します。成功すると、この実行によって影響を受けた行数が返されます。
query(): SELECT など、複数行のデータを返す SQL ステートメントを実行します。成功すると、結果のデータ行を反復処理できる CDbDataReader インスタンスが返されます。簡単にするために、(Yii) はクエリ結果を直接返す一連の queryXXX() メソッドも実装します。
SQL ステートメントの実行時にエラーが発生した場合、例外がスローされます。
$rowCount=$command->execute() // クエリなしの SQL を実行します
;$dataReader=$command->query() // SQL クエリを実行します
;$rows=$command->queryAll(); // クエリを実行し、結果のすべての行を返します
$row=$command->queryRow(); // クエリを実行し、結果の最初の行を返します
$column=$command->queryColumn(); // クエリを実行し、結果の最初の列を返します
$value=$command->queryScalar(); // クエリを実行し、結果の最初の行の最初のフィールドを返します
3. クエリ結果を取得する
で
CDbCommand::query() が CDbDataReader インスタンスを生成した後、それを繰り返し呼び出すことができます
CDbDataReader::read() 結果の行を取得します。 PHP の foreach 言語構造でも使用できます
CDbDataReader はデータを 1 行ずつ取得します。
$dataReader=$command->query();
// false が返されるまで read() を繰り返し呼び出します
while(($row=$dataReader->read())!==false) { ... }
// foreach を使用してデータの各行を反復処理します
foreach($dataReader as $row) { ... }
//すべての行を一度に配列に抽出します
$rows=$dataReader->readAll();
注: query() とは異なり、すべての queryXXX() メソッドはデータを直接返します。たとえば、queryRow() は、クエリ結果の最初の行を表す配列を返します。
4. トランザクションを使用する
Yii では CDbTransaction インスタンスとして表されるトランザクションは、次の状況で開始される可能性があります:
* 取引を開始します。
* クエリを 1 つずつ実行します。データベースへの更新は外部からは見えません。
* トランザクションを送信します。トランザクションが成功すると、更新が表示されます。
* クエリの 1 つが失敗すると、トランザクション全体がロールバックされます。
上記のワークフローは、次のコードを通じて実装できます:
$transaction=$connection->beginTransaction();
試してみる
{
$connection->createCommand($sql1)->execute();
$connection->createCommand($sql2)->execute();
//....その他の SQL 実行
$transaction->commit();
}
catch(Exception $e) // クエリが失敗した場合、例外がスローされます
{
$transaction->rollBack();
}
5. バインディングパラメータ
SQL インジェクション攻撃を回避し、繰り返し実行される SQL ステートメントの効率を向上させるために、オプションのパラメーター プレースホルダーを使用して SQL ステートメントを「準備」できます。パラメーターがバインドされると、これらのプレースホルダーは実際のパラメーターに置き換えられます。高麗人参
数値プレースホルダーは、名前を付けることも (一意のトークンとして表示される)、名前を付けないこともできます (疑問符として表示されます)。 CDbCommand::bindParam() または
を呼び出します。
CDbCommand::bindValue()これらのプレースホルダーを実際のパラメーターに置き換えます。これらのパラメータを引用符で囲む必要はありません。基礎となるデータベース ドライバがこれを処理します。パラメータのバインドは、SQL ステートメントが実行される前に完了する必要があります。
// 2 つのプレースホルダー「:username」と「:email」を含む SQL
$sql="INSERT INTO tbl_user (ユーザー名, メールアドレス) VALUES(:ユーザー名,:メールアドレス)";
$command=$connection->createCommand($sql);
// プレースホルダー「:username」を実際のユーザー名に置き換えます
$command->bindParam(":ユーザー名",$ユーザー名,PDO::PARAM_STR);
// プレースホルダー「:email」を実際のメールに置き換えます
$command->bindParam(":email",$email,PDO::PARAM_STR);
$command->execute();
// 新しいパラメータセットを含む別の行を挿入します
$command->bindParam(":username",$username2,PDO::PARAM_STR);
$command->bindParam(":email",$email2,PDO::PARAM_STR);
$command->execute();
メソッドbindParam()とbindValue()は非常に似ています。唯一の違いは、前者はパラメータのバインドに PHP 変数を使用するのに対し、後者は値を使用することです。メモリ内に大きなデータ ブロックを持つパラメータの場合、パフォーマンス上の理由から、前者を最初に使用する必要があります。
6. バインディングコラム
クエリ結果を取得するときに、PHP 変数を使用して列をバインドすることもできます。これにより、クエリ結果の行が取得されるたびに最新の値が自動的に入力されます。
$sql="ユーザー名、メールアドレスを tbl_user から選択";
$dataReader=$connection->createCommand($sql)->query();
// $username 変数を使用して最初の列 (ユーザー名) をバインドします
$dataReader->bindColumn(1,$username);
// $email 変数を使用して 2 番目の列 (電子メール) をバインドします
$dataReader->bindColumn(2,$email);
while($dataReader->read()!==false)
{
// $username と $email には、現在の行のユーザー名とメールアドレスが含まれます
}
7. テーブル接頭語を使用する
欲しい
テーブル プレフィックスを使用し、CDbConnection::tablePrefix プロパティを目的のテーブル プレフィックスに設定します。次に、SQL ステートメントで
を使用します。
{{TableName}} はテーブルの名前を表します。TableName はプレフィックスのないテーブル名を指します。たとえば、データベースに tbl_userという名前のデータベースが含まれているとします。
テーブル、および tbl_ がテーブル プレフィックスとして構成されている場合、次のコードを使用してユーザー関連のクエリを実行できます。$sql='SELECT * FROM {{user}}';
$users=$connection->createCommand($sql)->queryAll();
2. アクティブな記録
だけど
Yii DAO はデータベース関連のほぼすべてのタスクを処理できますが、通常の CRUD (作成、読み取り、
) を実行するいくつかのルーチンの作成に時間の 90% を費やす可能性があります。更新および削除の SQL ステートメント)。また、コードに SQL ステートメントが混在すると、メンテナンスが困難になります。これらの問題を解決するには、Active
を使用できます記録。
アクティブ レコード (AR) は、人気のあるオブジェクト リレーショナル マッピング (ORM) テクノロジーです。すべてのAR
クラスはデータ テーブル (またはビュー) を表し、AR インスタンスはクラスの属性として AR クラスに反映されます。 AR としての一般的な CRUD 操作
メソッドの実装。したがって、よりオブジェクト指向的な方法でデータにアクセスできます。たとえば、次のコードを使用して、tbl_post テーブルに新しい行を挿入できます。
$post=新しい投稿;
$post->title='サンプル投稿';
$post->content='投稿本文のコンテンツ';
$post->save();
注: AR は、データベース関連のすべてのタスクを解決することを目的としたものではありません。その最適なアプリケーションは、データ テーブルを PHP 構造にモデル化し、複雑な SQL ステートメントを含まないクエリを実行することです。 複雑なクエリシナリオの場合は、Yii DAO を使用する必要があります。
1. データベース接続を確立します
AR はデータベース接続に依存してデータベース関連の操作を実行します。デフォルトでは、db アプリケーション コンポーネントが必要な CDbConnection データベース接続インスタンスを提供すると想定します。次のアプリケーション構成は例を示しています。
配列を返す('コンポーネント'=>配列(
'db'=>array(
'クラス'=>'system.db.CDbConnection',
'connectionString'=>'sqlite:path/to/dbfile',
// パフォーマンスを向上させるためにスキーマ キャッシュをオンにします
// 'schemaCachingDuration'=>3600,
)、
)、
);
言及
説明: Active Record はテーブルのメタデータに依存して列情報を決定するため、メタデータの読み取りと解析には時間がかかります。
データベースのテーブル構造がほとんど変更されない場合は、CDbConnection::schemaCachingDuration 属性をゼロより大きい値に構成して有効にする必要があります
テーブル構造キャッシュ。
データベースではないアプリケーション コンポーネントを使用する場合、または AR を使用して複数のデータベースを処理する場合は、CActiveRecord::getDbConnection() をオーバーライドする必要があります。 CActiveRecord クラスは、すべての AR クラスの基本クラスです。
ヒント: AR を通じて複数のデータベースを使用するには 2 つの方法があります。データベースの構造が異なる場合は、異なる AR 基本クラスを作成して、異なる getDbConnection() を実装できます。それ以外の場合は、静的変数 CActiveRecord::db を動的に変更することをお勧めします。
2. ARクラスを定義する
データテーブルにアクセスするには、まず CActiveRecord を統合して AR クラスを定義する必要があります。各 AR クラスは個別のデータ テーブルを表し、AR インスタンスはそのテーブル内の行を表します。
次の例は、tbl_post テーブルを表す AR クラスの最も単純なコードを示しています。
クラス Post は CActiveRecord を拡張します
{
パブリック静的関数モデル($className=__CLASS__)
{
親を返す::モデル($className);
}
パブリック関数 tableName()
{
「tbl_post」を返す;
}
}
ヒント: AR クラスは複数の場所で参照されることが多いため、AR クラスを 1 つずつインポートする代わりに、AR クラスを含むディレクトリ全体をインポートできます。 たとえば、すべての AR クラス ファイルが protected/models ディレクトリにある場合、アプリケーションを次のように構成できます:
配列を返す(
'import'=>array(
'application.models.*',
)、
);
デフォルトでは、AR クラスの名前はデータテーブルの名前と同じです。異なる場合は、tableName() メソッドをオーバーライドします。
テーブルプレフィックス関数を使用するには、ARクラスのtableName()メソッドを次のようにオーバーライドできます
パブリック関数 tableName()
{
「{{post}}」を返す;
}
これは、Yii が自動的に接頭辞を追加して完全なテーブル名を返すことができるように、接頭辞のないテーブル名を二重中括弧で囲むことを意味します。
データテーブル行の列の値は、対応する AR インスタンスのプロパティとしてアクセスできます。たとえば、次のコードはタイトル列 (属性) を設定します。
$post=新しい投稿;$post->title='投稿のサンプル';
だけど
Post クラスで属性 title を明示的に定義したことはありませんが、上記のコードを通じてアクセスできます。これは、title が tbl_post テーブル内の 1 つであるためです
Column、CActiveRecord は、PHP の __get() マジック メソッドを通じてアクセス可能なプロパティになります。同じ方法で存在しない列にアクセスしようとすると、
がスローされます。
例外。テーブルに主キーがない場合は、次のように対応する AR クラスの PrimaryKey() メソッドをオーバーライドして、どの列を主キーとして使用するかを指定する必要があります。
パブリック関数primaryKey()
{
「id」を返す;
// 複合主キーの場合は、次のような配列を返します
// 配列を返します('pk1', 'pk2');
}
3. 記録を作成する
データ テーブルに新しい行を挿入するには、対応する AR クラスのインスタンスを作成し、テーブルの列に関連するそのプロパティを設定してから、save() メソッドを呼び出して挿入を完了する必要があります。
$post=新しい投稿;
$post->title='サンプル投稿';
$post->content='サンプル投稿のコンテンツ';
$post->create_time=time();
$post->save();
テーブルの主キーが自動インクリメントの場合、挿入が完了すると、AR インスタンスには更新された主キーが含まれます。上記の例では、明示的に変更していないにもかかわらず、 id 属性は新しく挿入された投稿の主キー値を反映します。
テーブル構造内で列が静的なデフォルト値 (文字列、数値など) で定義されている場合。その後、AR インスタンスの対応する属性には、インスタンスの作成時にこのデフォルト値が自動的に含まれます。このデフォルト値を変更する 1 つの方法は、AR クラスでこの属性を明示的に定義することです:
クラス Post は CActiveRecord を拡張します
{
public $title='タイトルを入力してください';
......
}
$post=新しい投稿;
echo $post->title; // ここに表示されます: タイトルを入力してください
レコードがデータベースに保存 (挿入または更新) される前に、そのプロパティを CDbExpression タイプに割り当てることができます。たとえば、MySQL の NOW() 関数によって返されたタイムスタンプを保存するには、次のコードを使用できます:
$post=新しい投稿;
$post->create_time=new CDbExpression('NOW()'); //CDbExpression クラスはデータベース式の値を計算します
// $post->create_time='NOW()' は機能しません。
// 'NOW()' は文字列として処理されます。$post->save();
言及
ヒント: AR を使用すると、多くの SQL ステートメントを記述せずにデータベース操作を実行できるため、
AR が舞台裏でどのような SQL ステートメントを実行しているのか疑問に思うことがよくあります。これは、Yii のロギング機能を有効にすることで実現できます。たとえば、アプリケーション構成で CWebLogRoute を有効にしました。
各 Web ページの最後に、実行された SQL ステートメントが表示されます。
アプリケーション構成で CDbConnection::enableParamLogging を true に設定して、SQL ステートメントにバインドされたパラメーター値も記録されるようにすることもできます
記録。
4. 記録を読む
データテーブル内のデータを読み取るには、次のように find series メソッドの 1 つを呼び出すことができます:
//指定された条件を満たす結果の最初の行を検索します
$post=Post::model()->find($condition,$params);
//指定された主キー値を持つ行を検索します
$post=Post::model()->findByPk($postID,$condition,$params);
// 指定された属性値を持つ行を検索します
$post=Post::model()->findByAttributes($attributes,$condition,$params);
// 指定された SQL ステートメントを通じて結果の最初の行を検索します
$post=Post::model()->findBySql($sql,$params);
上記のように、Post::model() を通じて find メソッドを呼び出します。静的メソッド model() はすべての AR クラスに必要であることに注意してください。このメソッドは、クラスレベルのメソッド (静的クラス メソッドのようなもの) にアクセスするためのオブジェクト コンテキスト内の AR インスタンスを返します。
find メソッドがクエリ条件を満たす行を見つけた場合、属性にデータ テーブル行の対応する列の値が含まれる Post インスタンスを返します。その後、通常のオブジェクト プロパティと同様に、ロードされた値を読み取ることができます (例: echo $post->title;)。
指定されたクエリ基準を使用してデータベース内で何も見つからない場合、find メソッドは null を返します。
find を呼び出すときは、$condition と $params を使用してクエリ条件を指定します。ここで、$condition は SQL ステートメント内の WHERE 文字列にすることができ、$params は $condation 内のプレースホルダーに値をバインドする必要があるパラメーターの配列です。例:
// postID=10 の行を検索します
$post=Post::model()->find('postID=:postID', array(':postID'=>10));
注: 上記の例では、特定の DBMS で postID 列参照をエスケープする必要がある場合があります。 たとえば、PostgreSQL を使用している場合、PostgreSQL はデフォルトで列名の大文字と小文字が区別されないため、この式を「postID」=:postID として記述する必要があります。
$condition を使用して、より複雑なクエリ条件を指定することもできます。文字列を使用する代わりに、 $condition を CDbCriteria のインスタンスにすることができます。これにより、WHERE に限定されない条件を指定できます。例:
$criteria=新しい CDbCriteria;
$criteria->select='title' // 'title' 列のみを選択します
$criteria->condition='postID=:postID';
$criteria->params=array(':postID'=>10);
$post=Post::model()->find($criteria) // $params は不要になりました
CDbCriteria をクエリ条件として使用する場合、$params パラメーターは上記と同様に CDbCriteria で指定できるため、必要なくなることに注意してください。
CDbCriteria の代わりに、配列を find メソッドに渡すこともできます。配列のキーと値はそれぞれ、条件の属性名と値に対応します。上記の例は次のように書き換えることができます。
$post=Post::model()->find(array()
'選択'=>'タイトル','条件'=>'投稿ID=:投稿ID',
'params'=>array(':postID'=>10),
));
ダン
クエリ条件が指定された値による複数の列の一致に関するものである場合、findByAttributes() を使用できます。 $attributes を使用します
パラメータは、列名でインデックス付けされた値の配列です。一部のフレームワークでは、このタスクは findByNameAndTitle のようなものを呼び出すことで実行できます
メソッドの実装。このアプローチは魅力的に思えますが、多くの場合、混乱、競合、および列名の大文字と小文字の区別などの問題が発生します。
指定されたクエリ条件に一致するデータ行が複数ある場合、以下の findAll メソッドを使用してそれらをすべて戻すことができます。すでに説明したように、それぞれに独自の検索メソッドがあります。
// 指定された条件を満たすすべての行を検索します
$posts=Post::model()->findAll($condition,$params);
// 指定された主キーを持つすべての行を検索します
$posts=Post::model()->findAllByPk($postIDs,$condition,$params);
// 指定された属性値を持つすべての行を検索します
$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
// 指定された SQL ステートメントを通じてすべての行を検索します
$posts=Post::model()->findAllBySql($sql,$params);
クエリ条件に一致するものがない場合、findAll は空の配列を返します。これは、何も見つからない場合に null を返す find とは異なります。
上記の find メソッドと findAll メソッドに加えて、便宜上、(Yii) は次のメソッドも提供します。
// 指定された条件を満たす行数を取得します$n=Post::model()->count($condition,$params);
// 指定された SQL を通じて結果の行数を取得します
$n=Post::model()->countBySql($sql,$params);
// 指定した条件を組み合わせた行が 1 つ以上あるかどうかを確認します
$exists=Post::model()->exists($condition,$params);
5. 記録を更新します
AR インスタンスに列の値が入力されたら、それらを変更してデータ テーブルに保存し直すことができます。
$post=Post::model()->findByPk(10);
$post->title='新しい投稿のタイトル';
$post->save(); // 変更をデータベースに保存します
ポジティブ
ご覧のとおり、同じ save() メソッドを使用して挿入操作と更新操作を実行します。 new 演算子を使用して AR インスタンスが作成された場合は、save() を呼び出します
データの新しい行がデータ テーブルに挿入されます。AR インスタンスが find または findAll メソッドの結果である場合は、save() を呼び出します
テーブル内の既存の行を更新します。実際、CActiveRecord::isNewRecord を使用して、AR インスタンスが新しいかどうかを示します。
最初にデータテーブルをロードせずに、データテーブル内の 1 つ以上の行を直接更新することもできます。 AR は、この目的を達成するために、次の便利なクラスレベルのメソッドを提供します:
//指定された条件を満たす行を更新します
Post::model()->updateAll($attributes,$condition,$params);
//指定された条件と主キーに一致する行を更新します
Post::model()->updateByPk($pk,$attributes,$condition,$params);
//指定された条件を満たす行の count 列を更新します
Post::model()->updateCounters($counters,$condition,$params);
上記のコードでは、$attributes は列名でインデックス付けされた列値を含む配列であり、$counters は列名でインデックス付けされた増分可能な値の配列であり、$condition と $params は前の段落で説明済みです。
6. 記録を削除する
AR インスタンスにデータ行が入力されている場合は、このデータ行を削除することもできます。
$post=Post::model()->findByPk(10); // ID が 10 の投稿があるとします
;
$post->delete(); // データテーブルからこの行を削除します削除後、AR インスタンスは変更されませんが、データ テーブル内の対応する行が失われていることに注意してください。
以下のクラスレベルのコードを使用すると、最初に行をロードせずに行を削除できます。
//指定された条件を満たす行を削除します
Post::model()->deleteAll($condition,$params);
//指定された条件と主キーに一致する行を削除します
Post::model()->deleteByPk($pk,$condition,$params);
7. データの検証
行を挿入または更新するとき、多くの場合、列の値が対応するルールに準拠しているかどうかを確認する必要があります。列の値がエンドユーザーによって提供される場合、これはさらに重要になります。全体として、クライアントから送信されるデータは決して信頼できません。
save() を呼び出すと、AR は自動的にデータ検証を実行します。検証は、AR クラスの rules() メソッドで指定されたルールに基づいて行われます。検証ルールの詳細については、「検証ルールの宣言」セクションを参照してください。以下は、レコードを保存するときに必要な一般的なワークフローです。
if($post->save())
{
//データは有効で、正常に挿入/更新されました
}
その他
{
//データが無効です。getErrors() を呼び出してエラー情報を抽出します
}
挿入または更新されるデータがエンドユーザーによって HTML フォームで送信される場合、それを対応する AR 属性に割り当てる必要があります。これは次のような方法で実現できます:
$post->title=$_POST['title'];
$post->content=$_POST['content'];
$post->save();
列が多い場合、この種のコピーの非常に長いリストが表示されることがあります。これは、次に示すように属性プロパティを使用することで簡素化できます。詳細については、「安全な属性の割り当て」セクションと「アクションの作成」セクションを参照してください。
// $_POST['Post'] が値として列名インデックス列値を持つ配列であると仮定します
$post->attributes=$_POST['投稿'];
$post->save();
8. 記録を比較する
テーブル レコードと同様に、AR インスタンスは主キー値によって識別されます。したがって、2 つの AR インスタンスを比較するには、それらが同じ AR クラスに属していると仮定して、主キー値を比較するだけで済みます。ただし、より簡単な方法は、CActiveRecord::equals() を呼び出すことです。
他のフレームワークの AR 実装とは異なり、Yii は AR で複数の主キーをサポートします。複合主キーは 2 つ以上のフィールドで構成されます。同様に、主キーの値は Yii では配列として表されます。 PrimaryKey 属性は、AR インスタンスの主キー値を与えます。
9.カスタマイズ
CActiveRecord は、ワークフローをカスタマイズするためにサブクラスでオーバーライドできるいくつかのプレースホルダー メソッドを提供します。
beforeva lidate と afterValidate: これら 2 つは、データの有効性を検証する前と後に呼び出されます。
beforeSave と afterSave: これら 2 つは、AR インスタンスを保存する前と後に呼び出されます。
beforeDelete と afterDelete: これら 2 つは、AR インスタンスが削除される前後に呼び出されます。
afterConstruct: これは、new 演算子を使用して各 AR インスタンスが作成された後に呼び出されます。
beforeFind: これは、AR ファインダーがクエリ (find()、findAll() など) を実行するために使用される前に呼び出されます。
afterFind: これは、クエリの結果として各 AR インスタンスが作成されるときに呼び出されます。
10. AR を使用して業務を処理する
各 AR インスタンスには、CDbConnection のインスタンスである dbConnection という名前のプロパティが含まれているため、必要に応じて AR で Yii DAO が提供するトランザクション関数を使用できます。
$model=Post::model();$transaction=$model->dbConnection->beginTransaction();
試してみる
{
// 検索と保存は 2 つのステップであり、別のリクエストが介入する可能性があります
// このようにしてトランザクションを使用して、その一貫性と整合性を確保します
$post=$model->findByPk(10);
$post->title='新しい投稿のタイトル';
$post->save();
$transaction->commit();
}
キャッチ(例外 $e)
{
$transaction->rollBack();
}
11. 命名範囲
名前付きスコープは名前付きクエリ ルールを表し、他の名前付きスコープと組み合わせて使用したり、アクティブ レコード クエリに適用したりできます。
名前付きスコープは、主に CActiveRecord::scopes() メソッドの名前とルールのペアの形式で宣言されます。次のコードは、Post モデル クラスで 2 つの名前付き範囲 (公開済みおよび最近) を宣言します。
クラス Post は CActiveRecord を拡張します
{
......
パブリック関数scopes()
{
配列を返す(
'公開済み'=>array(
'条件'=>'ステータス=1',
)、
'最近'=>array(
'order'=>'create_time DESC',
'制限'=>5,
)、
);
}
}
各名前付き範囲は、CDbCriteria インスタンスの初期化に使用できる配列として宣言されます。たとえば、最近名前を付けた範囲は、order 属性が create_time DESC、limit 属性が 5 であることを指定します。クエリ ルールに変換された後、最後の 5 つの投稿が返されます。
名前付き範囲は主に、find メソッド呼び出しの修飾子として使用されます。複数の名前付き範囲を連結して、より限定的なクエリ結果セットを形成できます。たとえば、最近の投稿を見つけるには、次のようなコードを使用できます:
$posts=Post::model()->published()->recently()->findAll();
一般に、名前付き範囲は find メソッド呼び出しの左側に表示される必要があります。それぞれがクエリ ルールを提供し、find メソッド呼び出しに渡されるルールを含む他のルールと結合されます。最終的な結果は、クエリに一連のフィルターを追加するようなものです。
名前付き範囲は、更新メソッドと削除メソッドでも使用できます。たとえば、次のコードは最近の投稿をすべて削除します:
投稿::モデル()->公開()->最近()->削除();
注: 名前付きスコープはクラスレベルのメソッドにのみ使用できます。つまり、このメソッドは ClassName::model() を使用して呼び出す必要があります。
12. パラメータ化された命名範囲
名前付き範囲はパラメータ化できます。たとえば、最近名前を付けたスコープで指定された投稿の数をカスタマイズしたいとします。これを実現するには、CActiveRecord::scopes メソッドで名前付きスコープを宣言する代わりに、この名前付きスコープと同じ名前のメソッドを定義する必要があります。 :
最近の公開関数($limit=5)
{
$this->getDbCriteria()->mergeWith(array(
)
'order'=>'create_time DESC','制限'=>$制限,
));
$this を返す;
}
次に、次のステートメントを使用して、最新の 3 つの投稿を取得できます。
$posts=Post::model()->published()->最近(3)->findAll();
上記のコードでパラメータ 3 を指定しない場合、デフォルトで最新の 5 件の投稿が取得されます。
13. デフォルトの名前付け範囲
モデル
型クラスは、すべて (関連するものを含む) に適用されるデフォルトの名前付きスコープを持つことができます
このモデルについての問い合わせ。たとえば、複数の言語をサポートする Web サイトでは、現在のユーザーが指定した言語でのみコンテンツが表示される場合があります。この Web サイトのコンテンツに関するクエリが多数ある可能性があるため、デフォルトを定義できます
この問題を解決するための名前付きスコープ。これを実現するには、次のように CActiveRecord::defaultScope メソッドをオーバーライドします。
クラスコンテンツはCActiveRecordを拡張します
{
パブリック関数defaultScope()
{
配列を返す(
'条件'=>"言語='".Yii::app()->言語."'",
);
}
}
ここで、次のメソッドが呼び出されると、上で定義したクエリ ルールが自動的に使用されます。
$contents=Content::model()->findAll();デフォルトの名前付き範囲は SELECT クエリにのみ適用されることに注意してください。 INSERT、UPDATE、および DELETE クエリは無視されます。
3. リレーショナルアクティブレコード (関連クエリ)
アクティブ レコード (AR) を介して単一のデータ テーブルからデータを取得する方法はすでに理解しています。このセクションでは、AR を使用して関連するデータ テーブルに接続してデータを取得する方法を紹介します。
関連付けられた AR を使用する前に、まずデータベース内の関連付けられたデータ テーブル間の主キーと外部キーの関連付けを確立する必要があります。AR は、データベース内のデータ テーブルの関連付けを定義するメタ情報を分析して、データを接続する方法を決定する必要があります。
1. アソシエーションの宣言方法
AR を使用して関連クエリを実行する前に、さまざまな AR クラスがどのように関連しているかを AR に伝える必要があります。
AR クラス間の関連付けは、データベース内のこのクラスによって表されるデータ テーブル間の関連付けを直接反映します。リレーショナル データベースの観点から見ると、2 つのデータ テーブル A と B の間には、1 対多、1 対 1、および多対多の 3 つの関連付けが考えられます。 AR には 4 種類の関連付けがあります:
BELONGS_TO: データテーブル A と B の間の関係が 1 対多である場合、B は A に属すると言います。
HAS_MANY: データテーブル A と B の間の関係が多対 1 である場合、B には多くの A がある (B には多くの A がある) と言います。
HAS_ONE: これは、「HAS_MANY」関係の特殊なケースです。A が 1 つしか持たない場合、B には 1 つの A がある (B には 1 つの A がある) と言います。
MANY_MANY:
これは、リレーショナル データベースにおける多対多の関係に相当します。ほとんどのリレーショナル データベースは多対多の関係を直接サポートしていないため、通常、多対多の関係を 2 つの 1 対 1 の関係に分解するには別の関連付けテーブルが必要です
たくさんの関係。 AR 用語で理解すると、MANY_MANY 関係は BELONGS_TO と HAS_MANY で構成されていると考えることができます。
AR でのリレーションシップの宣言は、親クラス CActiveRecord の relationship() メソッドをオーバーライドすることで実現されます。このメソッドは、関係定義を含む配列を返します。配列内のキー値の各セットは関連付けを表します。
'VarName'=>array('RelationType', 'ClassName', 'ForeignKey', ...追加オプション)これ
の VarName はこの関連付けの名前です。RelationType はこの関連付けのタイプを指定します。
の 4 つのタイプを表す 4 つの定数があります。
タイプ: self::BELONGS_TO、self::HAS_ONE、self::HAS_MANY、self::MANY_MANY;ClassName は、この関係に関連付けられた AR クラスのクラス名です。ForeignKey は、この関係がどの外部キーを介して接続されているかを指定します。後ろに追加
オプションではいくつかの追加設定を追加できます。これについては後で紹介します。
以下のコードは、ユーザーと投稿の間の関連付けを定義する方法を示しています。
クラス Post extends CActiveRecord {
パブリック関数 relationship() {
配列を返す(
'作成者'=>array(
自分::BELONGS_TO,
「ユーザー」、
「著者ID」
)、
'カテゴリ'=>配列(
自分::MANY_MANY、
「カテゴリ」、
'投稿カテゴリー(投稿ID, カテゴリID)'
)、
);
}
}
クラス ユーザーは CActiveRecord を拡張します {
パブリック関数 relationship() {
配列を返す(
'投稿'=>array(
自分::HAS_MANY、
「投稿」、
「著者ID」
)、
'プロファイル'=>配列(
自分::HAS_ONE、
「プロフィール」、
「オーナーID」
)、
);
}
}
言います
明: 外部キーが 2 つ以上のフィールドで構成される場合があります。その場合、複数のフィールド名はカンマまたはスペースで区切ることができます。
ここに一緒に書いてください。多対多のリレーションシップの場合、関連テーブルは外部キー (たとえば、Post クラスのカテゴリ) に記録される必要があります
関連付けでは、外部キーをPostCategory(postID, categoryID)として記述する必要があります。
AR クラスでアソシエーションを宣言すると、各アソシエーションが属性として AR クラスに追加され、属性名がアソシエーションの名前になります。関連するクエリを実行すると、これらのプロパティは関連する AR クラスのインスタンスに設定されます。たとえば、Post インスタンスを取得するためにクエリを実行する場合、その $author 属性は Post 作成者を表す User クラスのインスタンスです。
2. 関連するクエリ
入力
関連クエリを実行する最も簡単な方法は、関連 AR オブジェクトの関連属性にアクセスすることです。このプロパティに以前にアクセスしたことがない場合は、関連するクエリが開始され、現在の AR オブジェクトの主キーを介して接続されます
関連テーブルを使用して、関連付けられたオブジェクトの値を取得し、これらのデータをオブジェクトのプロパティに保存します。この方法は「遅延読み込み」と呼ばれ、特定の属性にアクセスした場合にのみ、実際にデータベースに読み込まれることになります
関連データを取得します。次の例は、遅延読み込みプロセスを説明しています:
// ID が 10 の投稿を取得します
$post=Post::model()->findByPk(10);
// 投稿の作成者を取得します: ここでリレーショナル クエリが実行されます
$author=$post->著者;
異なる関連付け状況では、結果が見つからない場合、戻り値も異なります。BELONGS_TO および HAS_ONE 関連付けは、結果がない場合に null を返し、HAS_MANY および MANY_MANY は、結果がない場合に空の配列を返します。
遅延読み込み方法は非常に便利ですが、場合によっては効率的ではありません。たとえば、N 件の投稿の作成者情報を取得したい場合、遅延メソッドを使用すると、N 個の接続クエリが実行されます。この時点では、いわゆる積極的な読み込み方法を使用する必要があります。
積極的な読み込みメソッドは、メインの AR インスタンスとそれに関連する AR インスタンスを取得します。これは、with() メソッドと find または findAll メソッドを使用して行われます。
完了です。たとえば、$posts=Post::model()->with('author')->findAll();
アップ
上記のコードは Post インスタンスの配列を返します。遅延読み込みメソッドとは異なり、各 Post インスタンスの author プロパティはこのプロパティにアクセスする前に関連付けられています。
ユーザー インスタンスの母集団。投稿ごとに結合クエリを実行する代わりに、熱心な読み込みメソッドにより、単一の結合クエリですべての投稿とその投稿者が取得されます。
with() メソッドで複数の関連付け名を指定できます。たとえば、次のコードは投稿を投稿者とカテゴリとともに取得します。$posts=Post::model()->with('author','categories')->findAll();
ネストされた熱心な読み込みも使用できます。アソシエーション名のリストを使用する代わりに、次のように階層的な方法でアソシエーション名を with() メソッドに渡します。
$posts=Post::model()->with(
'著者.プロフィール',
'著者.投稿',
'カテゴリ')->すべて検索();
上記のコードは、投稿者とカテゴリとともにすべての投稿を取得します。各著者のプロフィールと投稿も表示されます。
次のように CDbCriteria::with 属性を指定して、積極的な読み込みを実行することもできます:
$criteria=新しい CDbCriteria;
$criteria->with=array(
'著者.プロフィール',
'著者.投稿',
「カテゴリー」、
);
$posts=Post::model()->findAll($criteria);
または
$posts=Post::model()->findAll(array(
)'with'=>array(
'著者.プロフィール',
'著者.投稿',
「カテゴリー」、
)
);
3. 関連するクエリのオプション
関連付け宣言で追加のパラメーターを指定できることを前述しました。これらのオプションは、名前と値のペアとして指定され、相関クエリをカスタマイズするために使用されます。それらの概要を以下に示します:
select: 関連する AR クラスをクエリするフィールドのリスト。デフォルトは「*」で、すべてのフィールドを意味します。クエリ フィールド名は、エイリアス式 (例: COUNT(??.name) AS nameCount) を使用して曖昧さを解消できます。
条件: WHERE サブステートメント。デフォルトは空です。列はエイリアスを使用して参照する必要があることに注意してください (例: ??.id=10)。
params: SQL ステートメントにバインドされたパラメーターは、名前と値のペアで構成される配列 () である必要があります。
on: ON サブステートメント。ここで指定した条件は、and 演算子を使用して結合条件に追加されます。このオプションのフィールド名は曖昧さをなくす必要があります。このオプションは MANY_MANY 関連付けには適用されません。
order: ORDER BY サブステートメント。デフォルトは空です。列はエイリアスを使用して参照する必要があることに注意してください (例: ??.age DESC)。
with: このオブジェクトと一緒にロードされる子関連オブジェクトのリスト。不適切に使用すると、無限の関連付けループが形成される可能性があることに注意してください。
joinType: この関連付けの結合タイプ。デフォルトは LEFT OUTER JOIN です。
aliasToken: 列プレフィックスのプレースホルダー。デフォルトは「??.」です。
エイリアス: 関連付けられたデータテーブルのエイリアス。デフォルトは null で、テーブルの別名と関連付け名が同じであることを意味します。
together: 関連するデータ テーブルをメイン テーブルおよび他のテーブルに強制的に接続するかどうか。このオプションは、HAS_MANY および MANY_MANY 関連付けに対してのみ意味があります。このオプションが false に設定されている場合、...(ここの元のテキストは間違っています!) デフォルトは空です。このオプションのフィールド名は明確化されています。
having: HAVING サブステートメント。デフォルトは空です。列は別名を使用して参照されることに注意してください。
index: 返された配列のインデックス タイプ。返された配列がキーワードインデックス付きの配列であるか、数値インデックス付きの配列であるかを決定します。このオプションを設定しないと、配列には数値のインデックスが付けられます。このオプションは、HAS_MANY と MANY_MANY に対してのみ意味を持ちます
さらに、特定の関連付けの遅延読み込みでは次のオプションが利用可能です:
グループ: GROUP BY 句。デフォルトは空です。列はエイリアス (例: ??.age) を使用して参照する必要があることに注意してください。 このオプションは、HAS_MANY および MANY_MANY 関連付けにのみ適用されます。
having: HAVING 句。デフォルトは空です。列はエイリアス (例: ??.age) を使用して参照する必要があることに注意してください。このオプションは、HAS_MANY および MANY_MANY 関連付けにのみ適用されます。
limit: クエリされる行数を制限します。このオプションは、BELONGS_TO 関連付けには使用できません。
オフセット: オフセット。このオプションは、BELONGS_TO 関連付けには使用できません。
次に、上記のオプションのいくつかを使用して、User の投稿の関連付け宣言を変更します。
クラス ユーザーは CActiveRecord を拡張します
{
パブリック関数関係()
{
配列を返す(
'posts'=>array(self::HAS_MANY, '投稿', 'author_id',
'order'=>'posts.create_time DESC',
'with'=>'カテゴリ')、
'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
);
}
}
$author->posts にアクセスすると、ユーザーの投稿が公開時刻の降順で並べ替えられ、各投稿インスタンスもそのカテゴリとともに読み込まれます。