小ネタです。
master/slave構成を取っているDBを使用する場合、LaravelではRead/Write接続を設定して
Connectionの切替を行うことが多いかと思われます。
Read/Write接続の参考:https://readouble.com/laravel/5.3/ja/database.html
たまに、レプリケーションの遅延等々を考えた際に、処理によっては必ずWrite接続(master)を
使用してクエリを実行したいケースが発生することはないでしょうか。
自分はそんなケースがあったので、Write接続を使用するように、以下のようなコードを書きました。
※ 受注(Order)情報を取得する、というケースのイメージになります。
...中略
class Order extends Model
{
...中略
protected $table = 'orders';
...中略
public function details()
{
return $this->hasMany(OrderDetail::class, 'order_code', 'order_code');
}
}
$order = Order::useWritePdo()->find($orderId);
useWritePdoを呼び出すことで、Write接続を使用して検索クエリが実行されます。
接続取得の切り分けは、
Illuminate\Database\Connection#select → getPdoForSelect
の辺りで実装されていたので、そちらを見るとよく分かるかなと思います。
ここまではすぐできるのですが、上記の例で受注明細(OrderDetail)をEager Loadingで取得したい場合、かつ
Write接続で検索したい場合はどうすればよいのだろう?というのが今回ちょっと詰まったところです。
単純にwithを使うとこんな感じになって、
$order = Order::useWritePdo()->with('details')->find($orderId);
以下2本のクエリが実行されます。
select * from `orders` where `orders`.`id` = ? limit 1
select * from `order_details` where `order_details`.`order_code` in (?)
これでよさそうな感じがしますが、Illuminate\Database\Connection#selectにデバッグ張って見てみると、
2本目のクエリはRead接続を使用して実行されていることが確認できました。
※ Illuminate\Database\Connection#select実行時に、接続切り分けの引数「$useReadPdo」がtrueになっている。
親の検索で使用されたBuilderと、違うインスタンスが使用されている?ようです。
今回は、2本目の検索もWrite接続を使用したかったのです。
色々調べてみたのですが、withのcallbackでEager Loadingで使用されるBuilderインスタンスが
触れるということだったので、そちらでuseWritePdoを呼び出してWrite接続を使用することにしました。
$order = Order::useWritePdo()->with(['details' => function ($query) {
/** @var \Illuminate\Database\Query\Builder $query */
$query->useWritePdo();
}])->find($orderId);
これで、Eager Loading時のクエリもWrite接続を使用することが確認できました。
ちょっとスマートじゃない感もあるので、、、よりよいやり方があれば教えてください。
環境
Laravel Framework 5.2.45
PHP 7.0.3
参考ページ
以下を参考にさせていただきました。
ありがとうございました。