Laravel以前は、ほぼコードにSQL文を埋め込んでいたので、Eloquentよりクエリビルダーの方が馴染みあります。特に複数のDBテーブルをjoinした検索などには。
しかし、各Modelにおいてリレーションを定義していると、それを使用しないのがもったいないように思えてきます。
クエリビルダーでできることをEloquentではどうやるのか、興味ありありになってきました。
前回と同じ検索、親子関係のテーブルをリレーションを使ってできるか考えてみましょう。
前回と同様に、商品product
と商品画像product_image
の親子関係、つまり、1対多の関係があるとします。
その関係をモデルで定義するには、以下。一応名前は、product_images
と複数形にしてありますが、関数名のコンフリクトがないなら単数形でもOKです(要はプログラム内でどちらかに統一すること)。
namespace App; use Illuminate\Database\Eloquent\Model; class Product extends Model { ... public function product_images() { return $this->hasMany('App\ProductImage'); } }
Laravelのマニュアルによると、has
が親子のテーブルをjoinしてくれるようです。
$products = Product::has('product_images')->get();
この実行は以下のようなSQL文となります。
select * from `product` where exists (select * from `product_image` where `product_image`.`product_id` = `product`.`product_id`)
ちょっと通常のjoinとは違いますね。
しかし、商品画像のレコードを1つでも持つ商品は、これ使えそうですね。そうなら、検索値で絞るとすると、
$input = [ 'name' => '商品名', 'mime' => 'image/gif' ]; $products = Product::has('product_images') ->where('name', $input['name']) ->where('mime', $input['mime']) ->get();
しかし、これを実行するとエラーとなります。なぜなら、whereは両方とも、Productに対しての条件となり、mimeの項目名が、productに存在しないというエラーとなります。
正しくやるには、前回のクエリビルダーで使用したwhereIn
のようなものが必要です。
$input = [ 'name' => '商品名', 'mime' => 'image/gif' ]; $products = Product::where('name', $input['name']) ->whereHas('product_images', function($query) use($input) { $query->where('mime', $input['mime']); })->get();
今回は、クエリビルダーと違って、whereIn
を使うではなく、whereHas
となります。また、whereHas
ゆえに、Product::hasは要らなくなります。ちょっと慣れが必要ですね。
これを実行すると、SQL文は以下のようになります。
select * from `product` where `name` = 'Produt' and exists (select * from `product_image` where `product_image`.`product_id` = `product`.`product_id` and `mime` = 'image/gif')メルマガ購読の申し込みはこちらから。