一覧画面では代表的なカラムのみ取得、詳細画面では全カラムを持ってくる、みたいなことがしたい場合、SELECT句はどこに書けばいいのかという話。
私が知らないだけで、定石みたいなのが既に存在しているのであればごめんなさい。
調査対象はLaravel5.7です。
場所
コントローラ
クエリビルダのマニュアルに書かれているのがこの方法。
なんといってもわかりやすい。
class HogeController extends Controller
{
/**
* 一覧を取得する
* @param \Illuminate\Http\Request
* @return \Illuminate\Database\Eloquent\Collection
*/
public function list(Request $request)
{
return Foo::select(['id', 'name'])->get();
}
/**
* 詳細を取得する
* @param \Illuminate\Http\Request
* @return Foo
*/
public function detail(Request $request)
{
return Foo::find($request->id);
}
}
これで一覧画面ではSELECT id, name
が、詳細画面ではSELECT *
が発行されます。
一番楽で便利ではあるのですが、気になるのはコントローラにカラム名を直接書いてしまってるところです。
DBのカラム名を変更したりした場合、呼び出している全箇所で変更しないといけないので大変です。
どうせViewで使ってたりするから何れにせよ修正は必要なのですが、まあ対象箇所は減らしたいですよね。
モデル
Eloquentのマニュアルで、直接的ではないものの解説されている方法です。
class HogeController extends Controller
{
/**
* 一覧を取得する
* @param \Illuminate\Http\Request
* @return \Illuminate\Database\Eloquent\Collection
*/
public function list(Request $request)
{
return Foo::list()->get();
}
}
class Foo extends Model
{
/**
* list()を呼んだらscopeList()が呼ばれる
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder $query
*/
public static function scopeList($query)
{
return $query->select(['id', 'name']);
}
}
モデルにscopeHoge()
というメソッドを実装すると、コントローラからhoge()
で呼び出せます。
第一引数には自動的にクエリビルダインスタンスが入ってくるので、そちらを操作してreturnしましょう。
それだけでメソッドチェーンに組み込めるようになります。
メソッドに引数を渡したい場合は、scopeHoge()
に入れるとhoge()
の第二引数以降に入ってきます。
やってることはコントローラに書いたのと同じですが、モデルに書いたことで一カ所に集約できました。
これによってテーブル構造が変更になったとしても、この一カ所だけ修正すればよくなります。
欠点としては、メソッド名が異なるしIDEも追ってくれないので、仕組みを知らなければどこがどう動いてるのかわかりにくいことでしょうか。
というかLaravelはこんなのばっかりで、組み込みメソッドすら何があるのか未だにわからない。
visibleプロパティ
シリアライズのマニュアルで解説されています。
class HogeController extends Controller
{
/**
* 一覧を取得する
* @param \Illuminate\Http\Request
* @return \Illuminate\Database\Eloquent\Collection
*/
public function list(Request $request)
{
return Foo::all();
}
/**
* 詳細を取得する
* @param \Illuminate\Http\Request
* @return Foo
*/
public function detail(Request $request)
{
return Foo::find($request->id)->setVisible([]);
}
}
class Foo extends Model
{
protected $visible = ['id', 'name'];
}
モデルに$visible
というプロパティをセットしておくと、結果セットをtoArray()
などシリアライズした際に、該当のカラムしか出力されなくなります。
全カラムを取得したい場合はsetVisible([])
と空配列をセットしてキャンセルできます。
APIなど、さくっとJSONを返したい場合に便利です。
ただし、あくまで出力から消しているだけで、SELECT対象は*
のままなので負荷軽減には役立ちません。
また$foo->detail
のように対象カラムを直接指定すると見ることもできます。
同じように$hidden
プロパティも存在し、こちらは隠しておきたいプロパティを指定できます。
$visible
も$hidden
も使いやすいかといったらちょっと微妙ですね。
フロントエンド
GraphQLなんかだとフロントエンドでSELECT対象を指定できるわけですが……そんな権限フロントエンドに渡したいか?
どうせ
const ALLOWED_SELECT_COLUMN = ['id', 'name', …];
こんなことを書く羽目になるんだ。
まあたしかにPOSTでcolumn=id,name
とか受け付けるようなコードを書いたことはあるけれども。
まとめ
自分ならモデルに書くかな。
おまけ
一番最初の一覧を取得するやつ、実はreturn Foo::all(['id', 'name']);
と書けます。
この書き方マニュアルにも載ってないし使ってる人も見たことがない。