質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

2回答

4767閲覧

いいね数の集計をLaravelで実装したい

gyu

総合スコア3

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2020/11/12 08:15

編集2020/11/12 09:24

問題

下記の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}

ご教授のほどよろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

phper.k

2020/11/12 08:27 編集

Laravel5.7では確かに不具合があるようですが、6.x に不具合があるとは書かれていません。 具体的にどう言う不具合があったのですか? wiCount と paginate は同時に使ったこともあるし、何か不具合が会ったこともありません。
gyu

2020/11/12 08:36 編集

コメントありがとうございます 他のissueのページでのやりとりでは、laravel6でも発生しているというコメントがありました。 そのissueから、リンクを貼ったissueページに辿り着きました。一番わかりやすい例として添付しております。 そのページを閉じてしまったので、今はどのタイトルかは覚えていませんが、like_countでエラーが起こるのが現状です。
phper.k

2020/11/12 08:37 編集

実際うまくいかなかったコードを提示してください。 Eloquent を使ったコードの方ですよ。
gyu

2020/11/12 08:43

かなり長いですが、追加しました! お願いします
phper.k

2020/11/12 08:44 編集

detailどうのの部分は本件の本筋ではないので、Post とLike で説明すればいい?
phper.k

2020/11/12 08:52

↑の回答お願いします。 あと Like は Post と User をリレーションする belongsToMany で問題ない?
gyu

2020/11/12 09:00

Post.phpとLike.phpを追加しました。 抜粋箇所が今回の大きな箇所なので、PostとLikeで大丈夫です
gyu

2020/11/12 09:01

likeはhasManyです。
guest

回答2

0

ベストアンサー

回答

時間がく雑回答ですみません。
これでどうでしょうか?

diff

1$query = DB::table('posts') 2 ->join('likes', 'posts.id', '=', 'likes.post_id') 3- ->select('*', 'count(likes.id) AS likes_count') 4+ ->select( 5+ '*', 6+ DB::raw('count(likes.id) AS likes_count') 7+ ) 8 ->groupBy('posts.id') 9 ->get();

参考: エスケープなしのSQLを使用する

解説

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`)

laravel関数の解釈で

sql

1SELECT *,count(likes.id) AS likes_count

としてほしいところを、

sql

1select *, `count(likes`.`id)` as `likes_count` from

ってなってるのが、カラムが無いよってエラーになっています。

回答保留

背景の withCount() まわりは別内容(?)なので一旦検索が出来ますように。

投稿2020/11/12 09:24

bracket_i

総合スコア193

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gyu

2020/11/12 10:56

ありがとうございます! 解説でより一層理解できました!! 無事に検索できました。
guest

0

==== 消した ====

Laravel の開発にあたっては、以下のプラグインは必ずインストすべし。

https://github.com/barryvdh/laravel-debugbar

基本的に、LaravelのDB周りの処理は、 Eloquent で書くべき。

DB::select()なんて滅多に使う場面がない。よほど、Eloquent では十分なパフォーマンスが出ない場合などに限られる。DBに用意されているメソッドはEloquentでも使えるのだから、Eloquentでかけないはずがない。

Laravel のベストプラクティスがまとめられているので、参考にすると良いでしょう。

https://github.com/alexeymezenin/laravel-best-practices

投稿2020/11/12 09:24

編集2020/11/12 14:15
phper.k

総合スコア3923

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

phper.k

2020/11/12 14:12 編集

なんだよ。 コメントもつけないんかよ
gyu

2020/11/14 08:19

問題を解決し、teratailの確認が遅れ申し訳ないです。 Laravel のベストプラクティスがまとめられているページを紹介いただきありがとうございます 記事を参考にEloquentを利用した記述に変更しました。本当にありがとうございました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問