問題
下記のSQL文をクエリビルダに変換し、投稿(posts)のいいね(likes)の総数を取得したい。
SQL
1SELECT *,count(likes.id) AS likes_count 2FROM posts 3JOIN likes 4on posts.id = likes.post_id 5GROUP BY posts.id
このSQL文を実際に実行すると、画像の通り無事にデータを取得できる。
このSQL文をクエリビルダに変換したい。
SQL
1SELECT *,count(likes.id) AS likes_count 2FROM posts 3JOIN likes 4on posts.id = likes.post_id 5GROUP BY posts.id
現時点、自分で書いたクエリビルダ
Laravel
1$query = DB::table('posts') 2 ->join('likes', 'posts.id', '=', 'likes.post_id') 3 ->select('*', 'count(likes.id) AS likes_count') 4 ->groupBy('posts.id') 5 ->get();
上記のクエリビルダだとA column was not found
のエラーが発生する。
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'count(likes.id)' in 'field list' (SQL: select *, `count(likes`.`id)` as `likes_count` from `posts` inner join `likes` on `posts`.`id` = `likes`.`post_id` group by `posts`.`id`)
count(likes.id)
がエラーの原因で、うまくlikes(いいね)の集計ができていない。
後一歩まで来ているが、残りわずかというところで実装できていません。
お忙しいと思いますがご教授いただければ、幸いです。
お願い致します
詳細情報
こちらでは、より詳しい情報を記述していきます。
環境
Laravel6.0
(laravel6.6にアップグレードしても改善なし)
モデル
postモデル
post
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6 7class post extends Model 8{ 9 protected $fillable = [ 10 'title', 'body', 'user_id' 11 ]; 12 13 public function user() 14 { 15 return $this->belongsTo('App\User'); 16 } 17 public function tags() 18 { 19 return $this->belongsToMany('App\Models\tag', 'post_tags'); 20 } 21 22 public function likes() 23 { 24 return $this->hasMany('App\Models\like'); 25 } 26} 27
likeモデル
like
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6 7class like extends Model 8{ 9 protected $fillable = [ 10 'user_id', 'post_id' 11 ]; 12 13 public function post() 14 { 15 return $this->belongsTo('App\Models\post'); 16 } 17 18 public function user() 19 { 20 return $this->belongsTo('App\User'); 21 } 22} 23
マイグレーション
post
1 Schema::create('posts', function (Blueprint $table) { 2 $table->bigIncrements('id'); 3 $table->unsignedBigInteger('user_id'); 4 $table->foreign('user_id')->references('id')->on('users'); 5 $table->string('title'); 6 $table->text('body'); 7 $table->timestamps(); 8 });
like
1 Schema::create('likes', function (Blueprint $table) { 2 $table->bigIncrements('id'); 3 $table->unsignedBigInteger('user_id'); 4 $table->foreign('user_id')->references('id')->on('users'); 5 $table->unsignedBigInteger('post_id'); 6 $table->foreign('post_id')->references('id')->on('posts'); 7 $table->timestamps(); 8 });
背景
指定のいいね数の記事のみを取得する、絞り込み検索を実装しようとしています。
元々はwithCount()
を利用して、集計を実施していました。
その集計を元に指定のいいね数(最低:$lgtm_min, 最高: max)の範囲の記事を取得。
getだとエラーなし
$query = DB::table('posts') ->join('likes', 'posts.id', '=', 'likes.post_id') ->select('*', 'count(likes.id) AS likes_count') ->groupBy('posts.id'); if ($lgtm_min !== null) { $query->having('likes_count', '>=', $lgtm_min); } if ($lgtm_max !== null) { $query->having('likes_count', '<=', $lgtm_max); } $posts = $query->orderBy('posts.created_at', 'desc')->get();
get()
で取得する分にはエラーなしで実行できます。
paginateだとエラー
$query = DB::table('posts') ->join('likes', 'posts.id', '=', 'likes.post_id') ->select('*', 'count(likes.id) AS likes_count') ->groupBy('posts.id'); if ($lgtm_min !== null) { $query->having('likes_count', '>=', $lgtm_min); } if ($lgtm_max !== null) { $query->having('likes_count', '<=', $lgtm_max); } $posts = $query->orderBy('posts.created_at', 'desc')->paginate(20);
エラー
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'likes_count' in 'having clause' (SQL: select count(*) as aggregate from `posts` where (`posts`.`title` like %100% or `posts`.`body` LIKE %100%) having `likes_count` >= 1)
調べてみると、
withCount()
は、having()
と->paginate()
と併用して利用できないことが判明しました。
【Laravelのissue】withCount(), having() and paginate() together not working
そのため、withCount()
を利用せずにlikes_count
を定義する必要が出てきました。
そして、下記のSQL文をクエリビルダに変換しようと考えています
SQL
1SELECT *,count(likes.id) AS likes_count 2FROM posts 3JOIN likes 4on posts.id = likes.post_id 5GROUP BY posts.id
$query = Post::withCount('likes'); if ($lgtm_min !== null) { $query->having('likes_count', '>=', $lgtm_min); } if ($lgtm_max !== null) { $query->having('likes_count', '<=', $lgtm_max); } $posts = $query->orderBy('posts.created_at', 'desc')->paginate(20);
今はSQLで解決しようとしてますが、これが難しければ集計テーブルを作成し、それをjoin
で結合するかも検討しています。
抜粋なしのコード
要望があったので、記述します。
index
1 $query = Post::withCount('likes'); 2 $posts = DetailedSearch::DetailedSearch($query, $keyword, $request); 3 return view('posts.index', compact('posts', 'all_posts_count', 'keyword'));
DetailedSearch
1<?php 2 3namespace App\Services; 4 5class DetailedSearch 6{ 7 public static function DetailedSearch($query, $keyword, $request) 8 { 9 // values 10 $order = $request->input('order'); 11 $lgtm_min = $request->input('lgtm-min'); 12 $lgtm_max = $request->input('lgtm-max'); 13 $period = $request->input('period'); 14 $period_start = $request->input('period-start'); 15 $period_end = $request->input('period-end'); 16 17 //keyword 18 $keyword_space_half = mb_convert_kana($keyword, 's'); 19 $keywords = preg_split('/[\s]+/', $keyword_space_half); 20 preg_match_all('/#([a-zA-z0-90-9ぁ-んァ-ヶ亜-熙]+)/u', $keyword, $match); 21 $no_tag_keywords = array_diff($keywords, $match[0]); 22 $tags = $match[1]; 23 24 //LGTM sum search 25 if ($lgtm_min !== null) { 26 $query->having('likes_count', '>=', $lgtm_min); 27 } 28 if ($lgtm_max !== null) { 29 $query->having('likes_count', '<=', $lgtm_max); 30 } 31 32 33 34 // search order 35 if ($order == 'new') { 36 //paginate()だとエラーあり。こちらもget()にすればエラーなし 37 $posts = $query->orderBy('posts.created_at', 'desc')->paginate(20); 38 } else { 39 //get()だとエラーなし。こちらもpaginate()にすればエラーあり 40 $posts = $query->orderBy('likes_count', 'desc')->get(); 41 } 42 return $posts; 43 } 44}
ご教授のほどよろしくお願い致します。
回答2件
あなたの回答
tips
プレビュー