ORM?流行りものでしょ
SQLで良くね?😨→
クエリスコープで保守しやすくなるぞ🐱●抽象的に内容がわかるから保守性アップ
●条件分岐と組みあわせてクエリの組み立ての柔軟性
●コードが短く目と頭にやさしい#駆け出しエンジニア #プログラミング初心者 https://t.co/5sbnGp5iYW— 優さん🌷わくわく開発YouTuber (@yuu13n6) May 16, 2020
もくじ
まずはSQLとQuery Builderの比較
中間テーブルとの結合のSQLを書いてみる
SQLで書いた場合
<?php namespace App\Repositories\Status\Member; use App\Http\Models\Members; use App\Http\Models\Section; use Illuminate\Http\Request; use App\Repositories\Status\Member\PerformanceListRepositoryInterface; use Illuminate\Support\Facades\DB; use Illuminate\Database\Eloquent\Model; use Illuminate\Pagination\Paginator; class PerformanceListRepository implements PerformanceListRepositoryInterface { public function execute() { return self::getMembersWithSection(); } public function getMembersWithSection() { $sql = <<< SQL SELECT tb_member.member_id, tb_member.fullname, tb_member.count_burning, tb_member.count_join_project, tb_member.count_pm, tb_member.count_member, tb_member.count_sales, tb_member.skill, tb_sectiosection_name FROM tb_member INNER JOIN ( SELECT member_id, section_id, created FROM tb_member_section WHERE deleted = 0 AND created = ( SELECT MAX(created) FROM tb_member_section AS tmp_tb_member_section WHERE tb_member_section.member_id = tmp_tb_member_section.member_id AND tb_member_section.deleted = 0 ) ) as ud ON tb_member.member_id = ud.member_id LEFT JOIN tb_section ON tb_section.section_id = ud.section_id AND tb_section.deleted = 0 WHERE (tb_member.deleted = 0) GROUP BY ud.member_id SQL; } }
QueryBuilderで書いた場合
<?php namespace App\Repositories\Status\Member; use App\Http\Models\Members; use App\Repositories\Status\Member\PerformanceListRepositoryInterface; use Illuminate\Support\Facades\DB; class PerformanceListRepository implements PerformanceListRepositoryInterface { public function execute() { return self::getMembersWithSection(); } public function getMembersWithSection() { $query = Members::query()->where(function ($query) { return $query->where('tb_member.deleted', '=', '0'); }); $subQuery = DB::table('tb_member_section') ->select('member_id', 'section_id', 'created') ->where('deleted', '=', '0') ->whereRaw('created = ( SELECT MAX(created) FROM tb_member_section AS tmp_tb_member_section WHERE tb_member_section.member_id = tmp_tb_member_section.member_id AND tb_member_section.deleted = 0 ) '); //var_dump($subQuery->toSql()); // string(629) "select `member_id`, `section_id`, `created` from `tb_member_section` where `deleted` = ? and created = ( SELECT MAX(created) FROM tb_member_section AS tmp_tb_member_section WHERE tb_member_section.member_id = tmp_tb_member_section.member_id AND tb_member_section.deleted = 0 )" //exit(); return $query->select( 'tb_member.member_id', 'tb_member.fullname', 'tb_member.count_burning', 'tb_member.count_join_project', 'tb_member.count_pm', 'tb_member.count_member', 'tb_member.count_sales', 'tb_member.skill', 'tb_section.section_name' )->join(DB::raw('('. $subQuery->toSql() .') as ud'), function ($join) { $join->on('tb_member.member_id','=','ud.member_id'); }) ->mergeBindings($tb_member_section) ->leftjoin('tb_section', function ($join) { $join->on('tb_section.section_id', '=', 'ud.section_id') ->where('tb_section.deleted', '=', '0'); }) ->groupBy('ud.member_id') ->paginate(20); //var_dump($query->toSql()); // string(1128) "select `tb_member`.`member_id`, `tb_member`.`fullname`, `tb_member`.`count_burning`, `tb_member`.`count_join_project`, `tb_member`.`count_pm`, `tb_member`.`count_member`, `tb_member`.`count_sales`, `tb_member`.`skill`, `tb_section`.`section_name` from `tb_member` inner join (select `member_id`, `section_id`, `created` from `tb_member_section` where `deleted` = ? and created = ( SELECT MAX(created) FROM tb_member_section AS tmp_tb_member_section WHERE tb_member_section.member_id = tmp_tb_member_section.member_id AND tb_member_section.deleted = 0 )) as ud on `tb_member`.`member_id` = `ud`.`member_id` left join `tb_section` on `tb_section`.`section_id` = `ud`.`section_id` and `tb_section`.`deleted` = ? where (`tb_member`.`deleted` = ?) group by `ud`.`member_id` limit 20 offset 0" //exit(); } }
- ->join() 内部結合
- ->leftJoin() 左外部結合
- ->mergeBindings($tb_section) サブクエリのバインドの値をマージ
行数が少なめになりました🐱
サブクエリ使っちゃってるのでムズいが。
// モデルでリレーションを定義してあげるともっと良いです。
Query Builderの何が嬉しいの?
クエリスコープを利用することで保守がしやすくなります
<?php namespace App\Http\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; use DB; use Illuminate\Support\Collection; class Plan extends Model { ・・・ /** * 生きているデータ。論理削除されていない * * @param Builder $query * @return Builder */ public function scopeIsNotDeleted(Builder $query) { return $query->where('deleted', '0'); } /** * 炎上案件かどうか * * @param Builder $query * @return Builder */ public function scopeIsBurning(Builder $query) { return $query->where('deleted', '=', 0) ->where(function ($query) { $query->where('is_burning', '=', 1); }); } /** * 担当か関係者として関わっている * * @param Builder $query * @return Builder */ public function scopeisMyPlan(Builder $query, $memberId) { return $query->where('deleted', '=', 0) ->where(function ($query) use($memberId) { $query->orWhere('member_id', '=', $memberId) ->orWhere('member_ids', 'like', '%,'.$memberId.',%'); }); } ・・・ }
定義したクエリスコープは次のように利用できます
isNotDeleted(), isMyPlan(), isBurning()として利用しています。
// 会員の集計ステータス更新 $members = Members::query()->where(function ($query) { return $query->where('tb_member.deleted', '=', '0'); })->get(); foreach ($members as $member) { $countPm = Plan::isNotDeleted()->where('member_id', $member->member_id)->count(); $countUser = Plan::isNotDeleted()->where('member_ids','like','%,'.$member->member_id.',%')->count(); $countJoinPlan = Plan::isMyPlan($member->member_id)->count(); $countSales = Plan::isMyPlan($member->member_id)->sum('plan_total_price'); $countBurning = Plan::isMyPlan($member->member_id) ->isBurning() ->count(); $memberInstance = Members::find($member->member_id); $memberInstance->count_join_project = $countJoinPlan; $memberInstance->count_burning = $countBurning; $memberInstance->count_pm = $countPm; $memberInstance->count_member = $countUser; $memberInstance->count_sales = $countSales; $memberInstance->save(); }
スコープとして関数にすることでパッと見で抽象的にクエリの内容を理解しやすくなります🐱