ローカルスコープとは?
ローカルスコープによりアプリケーション全体で簡単に再利用可能な、一連の共通制約を定義できます。例えば、人気のある(popular)ユーザーを全員取得する必要が、しばしばあるとしましょう。スコープを定義するには、scopeを先頭につけた、Eloquentモデルのメソッドを定義します。
※ Eloquent:利用の開始 5.4 Laravel ローカルスコープ参照
要約すると、EloquentモデルのSQL周りをメソッドとして定義することができ、コントローラーから呼び出すことができます。
Railsでもlaravelでもそうですが、僕は直近までコントローラーにSQLを書いていました。
それでも動くのですが、アプリケーションの規模が膨大になってきたり、SQLの一部を変更したい場合に、そのSQLを書いている箇所全て直さなければいけません。
ちょっとそれだとイケてないので、ローカルスコープを使ってSQLを関数に直したいと思います。
注意点
ローカルスコープは、クエリビルダに対しては適用できないので注意
例)
値が取れる例
$a = Customer::select(DB::raw('count(detail)')) //この部分の記述が違う
->joinContacts()
->joinContactCategories()
->whereRaw('age >= 18')
->whereRaw('age <= 22')
->groupBy('age')
->get();
値が取れなかった例
$a = DB::table('customers') //この部分の記述が違う
->select(DB::raw('count(detail)'))
->joinContacts()
->joinContactCategories()
->whereRaw('age >= 18')
->whereRaw('age <= 22')
->groupBy('age')
->get();
以前のコントローラー
処理内容としては、複数ドロップダウンメニューが設置してあり、その選択した結果に応じて動的にグラフを変えるというものです。
-
$area = $request->area;
等の部分は、ドロップダウンメニューのvalueを$requestとして受け取っています。 -
->join...
の部分は、複数テーブルをjoinしています。 -
->when($area, function...
の部分は、第一引数が正だった場合に where句を使用しています。
つまり、ドロップダウンメニューの値が選択されていればwhere句を使用し、選択されていなければwhere句を使用しないという処理になります。
public function getCharts(Request $request)
{
$area = $request->area;
$rent = $request->rent;
$country = $request->country;
$gender = $request->gender;
$age = $request->age;
$query = DB::table('residents')
->select(DB::raw('count(*) as value, month'))
->join('properties as pr', 'pr.id', '=', 'residents.property_id')
->join('ages as ag', 'ag.id', '=', 'residents.age_id')
->join('countries as co', 'co.id', '=', 'residents.country_id')
->join('areas as ar', 'ar.id', '=', 'pr.area_id')
->when($area, function ($query) use ($area) {
return $query->where('area_id', '=', $area);
})
->when($rent, function ($query) use ($rent) {
return $query->where('rent', '=', $rent);
})
->when($country, function ($query) use ($country) {
return $query->where('country_id', '=', $country);
})
->when($gender, function ($query) use ($gender) {
return $query->where('gender', '=', $gender);
})
->when($age, function ($query) use ($age) {
return $query->where('', '=', $age);
})
->groupBy('month');
return $query->get();
}
ローカルスコープの使い方
ローカルスコープはモデルに定義します。
scope+メソッド名 という形で記述していきます。
メソッド名は処理の内容が明確になるような命名がいいです。(自分がまだ出来てないですが...)
public function scope+名前($query, 引数)
{
# 処理内容
return 絞り込んだビルダ(検索条件)
}
モデルへの記述
residents(入居者)テーブルに対してクエリを投げたかったので、Residentモデルに全て記述しました。
(ベストプラクティスが知りたい...)
// JOIN周り
public function scopeJoinProperties($query)
{
return $query->join('properties as pr', 'pr.id', '=', 'residents.property_id');
}
public function scopeJoinAges($query)
{
return $query->join('ages as ag', 'ag.id', '=', 'residents.age_id');
}
public function scopeJoinCountries($query)
{
return $query->join('countries as co', 'co.id', '=', 'residents.country_id');
}
public function scopeJoinAreas($query)
{
return $query->join('areas as ar', 'ar.id', '=', 'pr.area_id');
}
// Where句周り
public function scopeWhereAreas($query, $area)
{
if ($area != '') {
return $query->where('area_id', '=', $area);
}
}
public function scopeWhereCountries($query, $country)
{
if ($country != '') {
return $query->where('country_id', '=', $country);
}
}
public function scopeWhereAges($query, $age)
{
if ($age != '') {
$query->where('age_id', '=', $age);
}
}
public function scopeWhereGender($query, $gender)
{
if ($gender != '') {
$query->where('gender', '=', $gender);
}
}
public function scopeWhereRent($query, $rent)
{
if ($rent != '') {
$query->where('rent', '=', $rent);
}
}
コントローラー側での呼び出し
コントローラー側で呼び出すときは、 scopeを除外した変数名で呼び出します。
【例】
scopeJoinProperties
メソッドであれば、
コントローラーから呼び出す時に joinproperties()
のような形で呼び出します。
public function getCharts(Request $request)
{
$area = $request->area;
$rent = $request->rent;
$country = $request->country;
$gender = $request->gender;
$age = $request->age;
$query = Resident::select()
->select(DB::raw('count(*) as value, month'))
->joinProperties()
->joinAges()
->joinCountries()
->joinAreas()
->joinRoomtypes()
->whereAreas($area)
->whereAges($age)
->whereCountries($country)
->whereGender($gender)
->whereRent($rent)
->groupBy('month');
return $query->get();
}
見た目的にもだいぶスッキリしたように見えます。
まだ荒削りなので、もっと簡略化できる部分を探していきます。
他にもグローバルスコープなるものがあるらしいので、使う機会が来たらまた記事にまとめます。
ここもっとこうした方がいいよ、等のご指摘ありましたらいただけますと幸いです。