2

すべての読み取りは 1 つの DB 接続に移動する必要があります すべての書き込みは別の接続に移動する必要があります

コア ライブラリのコードを最小限に変更して、Yii でこれを達成するにはどうすればよいですか?

また、場合によっては (コメントに記載されているように)、接続の各モデル タイプを制御する機能が必要になるため、読み取りもマスターに移動できます。

4

1 に答える 1

4

マスター管理パネルを使用して顧客向けの「インスタンス」をいくつか作成および管理できるアプリを作成したので、マスター アプリ内で実行されているクエリをインスタンス固有のデータベースのいずれかに「転送」する必要がありました。私が最初に行ったことを簡略化したバージョン (目標ほど厳しいものではありません) を示し、後でより強力なアプローチを示します。

すべてのクエリに複数のデータベースを使用する

事前に指定されたデータベースにクエリを送信するのは簡単ですCActiveRecord::getDbConnection。メソッドをオーバーライドするだけです。私がしたことはこれに切り詰めることができます:

abstract class InstanceActiveRecord extends CActiveRecord {
    public static $dbConnection = null;

    public function getDbConnection() {
        if (self::$dbConnection === null) {
            throw new CException('Database connection must be defined to work with instance records.');
        }

        return self::$dbConnection;
    }
}

したがって、すべての操作を特定のデータベースに向けたい場合は、 ActiveRecord モデルをInstanceActiveRecordの代わりにから派生させるCActiveRecordだけでInstanceActiveRecord::dbConnection = $connectionよいのです。

クエリの種類に基づく自動選択で複数のデータベースを使用する

このためには、さらに深く掘り下げる必要がありますCActiveRecordgetDbConnectionが主に によって使用されていることがわかりgetCommandBuilderます。これは、すべての削除/更新/挿入ファミリによって呼び出されるメソッドです。したがって、これらの関数から何らかのコンテキストを に渡す必要がありますgetDbConnection。ここで、使用する接続の選択が行われます。

このために、これらのファミリのすべてのメソッドをオーバーライドする必要があるため、合理的なアプローチは次のようになります。

ステップ 1.オプションのパラメーターを追加しgetDbConnectionてオーバーライドし、パラメーター値に基づいて必要な接続を返します。最も簡単なのは次のようなものです。

public function getDbConnection($writeContext = null) {
    if ($writeContext === null) {
        return parent::getDbConnection(); // to make sure nothing will ever break
    }

    // You need to get the values for $writeDb and $readDb in here somehow,
    // but this can be as trivially easy as you like (e.g. public static prop)
    return $writeContext ? $writeDb : $readDb;
}

ステップ 2.オプションのパラメータをgetCommandBuilder同じセマンティクスで追加し、それをオーバーライドして値を転送します。

public function getCommandBuilder($writeContext = null) {
    return $this->getDbConnection($writeContext)->getSchema()->getCommandBuilder();
}

ステップ 3.getCommandBuilder (それらの束があるでしょう) とgetDbConnection(私が見たときに内部のものよりも 2 つだけ多かった)のすべての呼び出しサイトを見つけgetCommandBuilder、それらをオーバーライドして、読み取り/書き込みコンテキストを適切に指定します。例:

public function deleteAll($condition='',$params=array()) {
    Yii::trace(get_class($this).'.deleteAll()','system.db.ar.CActiveRecord');

    // Just need to add the (true) value here to specify write context:
    $builder=$this->getCommandBuilder(true);
    $criteria=$builder->createCriteria($condition,$params);
    $command=$builder->createDeleteCommand($this->getTableSchema(),$criteria);
    return $command->execute();
}

これで準備完了です。trueまた、ここに示した/オプションよりも複雑なコンテキスト選択メカニズムを作成することを妨げるものは何もありませんfalse。概念は同じです。

実際的な懸念事項

これらはすべて、記載された目標を完全に達成しますが、このアプローチの保守性に関しては疑問が残ります。

このルートに進むと、 からコピー/貼り付けされた多くのコードが必要になることは事実ですCActiveRecord。これは、後でアプリを新しいバージョンのフレームワークに移行する可能性がある場合には理想的ではありません。そのためには、サブクラスを最新バージョンの と同期させる必要がありますCActiveRecord

これを軽減し、将来の生活を楽にするために、次のアプローチを検討できます。

  1. の一部のみをコピー/貼り付けしてオーバーライドするのではなくCActiveRecord、 の正確なコピー (もちろんプロパティを除く) を作成CActiveRecordし、そこで変更を実行します。つまり、オーバーライドするつもりのないメソッドもコピーしてください。
  2. 上記の変更を行います。これには、getDbConnection 他の 12 か 2 か所のオーバーライドとほんのわずかな編集が含まれることに注意してください。
  3. モデルが結果のクラスを拡張するようにします。

Yii の新しいバージョンにアップグレードするときが来たら、クラスを とCActiveRecord再び同期させる必要があります。お気に入りの差分ツールを起動して、クラスをターゲット バージョンの と比較しますCActiveRecord。diff ツールは、とマイナーな編集、およびYii のコアにgetDbConnection加えられた変更のみを表示します。CActiveRecordこれらの他の変更をクラスにコピーします。問題は 5 分で解決します。

于 2011-09-14T23:01:17.170 に答える