質問するログイン新規登録

質問編集履歴

8

バージョン情報の詳細を記述

2020/11/12 09:24

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -49,7 +49,8 @@
49
49
  こちらでは、より詳しい情報を記述していきます。
50
50
 
51
51
  ## 環境
52
- Laravel6
52
+ Laravel6.0
53
+ (laravel6.6にアップグレードしても改善なし)
53
54
 
54
55
  ## モデル
55
56
  ### postモデル

7

背景を修正

2020/11/12 09:24

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -141,6 +141,7 @@
141
141
  元々は`withCount()`を利用して、集計を実施していました。
142
142
  その集計を元に指定のいいね数(最低:$lgtm_min, 最高: max)の範囲の記事を取得。
143
143
 
144
+ ### getだとエラーなし
144
145
  ```
145
146
  $query = DB::table('posts')
146
147
  ->join('likes', 'posts.id', '=', 'likes.post_id')
@@ -154,16 +155,36 @@
154
155
  $query->having('likes_count', '<=', $lgtm_max);
155
156
  }
156
157
 
158
+ $posts = $query->orderBy('posts.created_at', 'desc')->get();
159
+ ```
160
+
161
+ `get()`で取得する分にはエラーなしで実行できます。
162
+
163
+ ### paginateだとエラー
164
+ ```
165
+ $query = DB::table('posts')
166
+ ->join('likes', 'posts.id', '=', 'likes.post_id')
167
+ ->select('*', 'count(likes.id) AS likes_count')
168
+ ->groupBy('posts.id');
169
+
170
+ if ($lgtm_min !== null) {
171
+ $query->having('likes_count', '>=', $lgtm_min);
172
+ }
173
+ if ($lgtm_max !== null) {
174
+ $query->having('likes_count', '<=', $lgtm_max);
175
+ }
176
+
157
177
  $posts = $query->orderBy('posts.created_at', 'desc')->paginate(20);
158
178
  ```
159
179
 
160
- しかし、
161
180
 
162
181
  エラー
163
182
  ```
164
183
  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)
165
184
  ```
166
185
 
186
+
187
+
167
188
  調べてみると、
168
189
  `withCount()`は、`having()`と`->paginate()`と併用して利用できないことが判明しました。
169
190
 
@@ -237,55 +258,8 @@
237
258
  $query->having('likes_count', '<=', $lgtm_max);
238
259
  }
239
260
 
240
- // period search
241
- if ($period !== null) {
242
- switch ($period) {
243
- case "day":
244
- $query->where([
245
- ['posts.created_at', '>=', date("Y-m-d 00:00:00")],
246
- ['posts.created_at', '<=', date("Y-m-d 23:59:59")]
247
- ]);
248
- case "week":
249
- $query->where([
250
- ['posts.created_at', '>=', date("Y-m-d 00:00:00", strtotime("-1 week"))],
251
- ['posts.created_at', '<=', date("Y-m-d 23:59:59")]
252
- ]);
253
- case "month":
254
- $query->where([
255
- ['posts.created_at', '>=', date("Y-m-d 00:00:00", strtotime("-1 month"))],
256
- ['posts.created_at', '<=', date("Y-m-d 23:59:59")]
257
- ]);
258
- case "period":
259
- $query->where([
260
- ['posts.created_at', '>=', date("{$period_start} 00:00:00")],
261
- ['posts.created_at', '<=', date("{$period_end} 23:59:59")]
262
- ]);
263
- }
264
- }
265
261
 
266
- if ($keyword !== null) {
267
- // tags search
268
- if (count($tags) !== 0) {
269
- $query
270
- ->join('post_tags', 'posts.id', '=', 'post_tags.post_id')
271
- ->join('tags', 'post_tags.tag_id', '=', 'tags.id')
272
- ->whereIn('tags.name', $tags)
273
- ->groupBy('posts.id')
274
- ->havingRaw('count(distinct tags.id) = ?', [count($tags)]);
275
- }
276
262
 
277
- // keywords search
278
- foreach ($no_tag_keywords as $no_tag_keyword) {
279
- $query
280
- ->where(function ($query) use ($no_tag_keyword) {
281
- $query
282
- ->where('posts.title', 'like', '%' . $no_tag_keyword . '%')
283
- ->orWhere('posts.body', 'LIKE', "%{$no_tag_keyword}%");
284
- });
285
- }
286
- }
287
-
288
-
289
263
  // search order
290
264
  if ($order == 'new') {
291
265
  //paginate()だとエラーあり。こちらもget()にすればエラーなし

6

モデルを追加

2020/11/12 09:22

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -52,8 +52,68 @@
52
52
  Laravel6
53
53
 
54
54
  ## モデル
55
+ ### postモデル
55
56
 
56
57
  ```post
58
+ <?php
59
+
60
+ namespace App\Models;
61
+
62
+ use Illuminate\Database\Eloquent\Model;
63
+
64
+ class post extends Model
65
+ {
66
+ protected $fillable = [
67
+ 'title', 'body', 'user_id'
68
+ ];
69
+
70
+ public function user()
71
+ {
72
+ return $this->belongsTo('App\User');
73
+ }
74
+ public function tags()
75
+ {
76
+ return $this->belongsToMany('App\Models\tag', 'post_tags');
77
+ }
78
+
79
+ public function likes()
80
+ {
81
+ return $this->hasMany('App\Models\like');
82
+ }
83
+ }
84
+
85
+ ```
86
+
87
+ ### likeモデル
88
+ ```like
89
+ <?php
90
+
91
+ namespace App\Models;
92
+
93
+ use Illuminate\Database\Eloquent\Model;
94
+
95
+ class like extends Model
96
+ {
97
+ protected $fillable = [
98
+ 'user_id', 'post_id'
99
+ ];
100
+
101
+ public function post()
102
+ {
103
+ return $this->belongsTo('App\Models\post');
104
+ }
105
+
106
+ public function user()
107
+ {
108
+ return $this->belongsTo('App\User');
109
+ }
110
+ }
111
+
112
+ ```
113
+
114
+ ## マイグレーション
115
+
116
+ ```post
57
117
  Schema::create('posts', function (Blueprint $table) {
58
118
  $table->bigIncrements('id');
59
119
  $table->unsignedBigInteger('user_id');

5

コメントアウトを追加

2020/11/12 08:59

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -228,8 +228,10 @@
228
228
 
229
229
  // search order
230
230
  if ($order == 'new') {
231
+ //paginate()だとエラーあり。こちらもget()にすればエラーなし
231
232
  $posts = $query->orderBy('posts.created_at', 'desc')->paginate(20);
232
233
  } else {
234
+ //get()だとエラーなし。こちらもpaginate()にすればエラーあり
233
235
  $posts = $query->orderBy('likes_count', 'desc')->get();
234
236
  }
235
237
  return $posts;

4

抜粋なしのコードを追加

2020/11/12 08:42

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -136,4 +136,105 @@
136
136
 
137
137
  今はSQLで解決しようとしてますが、これが難しければ集計テーブルを作成し、それを`join`で結合するかも検討しています。
138
138
 
139
+ ## 抜粋なしのコード
140
+ 要望があったので、記述します。
141
+
142
+ ```index
143
+ $query = Post::withCount('likes');
144
+ $posts = DetailedSearch::DetailedSearch($query, $keyword, $request);
145
+ return view('posts.index', compact('posts', 'all_posts_count', 'keyword'));
146
+ ```
147
+
148
+ ```DetailedSearch
149
+ <?php
150
+
151
+ namespace App\Services;
152
+
153
+ class DetailedSearch
154
+ {
155
+ public static function DetailedSearch($query, $keyword, $request)
156
+ {
157
+ // values
158
+ $order = $request->input('order');
159
+ $lgtm_min = $request->input('lgtm-min');
160
+ $lgtm_max = $request->input('lgtm-max');
161
+ $period = $request->input('period');
162
+ $period_start = $request->input('period-start');
163
+ $period_end = $request->input('period-end');
164
+
165
+ //keyword
166
+ $keyword_space_half = mb_convert_kana($keyword, 's');
167
+ $keywords = preg_split('/[\s]+/', $keyword_space_half);
168
+ preg_match_all('/#([a-zA-z0-90-9ぁ-んァ-ヶ亜-熙]+)/u', $keyword, $match);
169
+ $no_tag_keywords = array_diff($keywords, $match[0]);
170
+ $tags = $match[1];
171
+
172
+ //LGTM sum search
173
+ if ($lgtm_min !== null) {
174
+ $query->having('likes_count', '>=', $lgtm_min);
175
+ }
176
+ if ($lgtm_max !== null) {
177
+ $query->having('likes_count', '<=', $lgtm_max);
178
+ }
179
+
180
+ // period search
181
+ if ($period !== null) {
182
+ switch ($period) {
183
+ case "day":
184
+ $query->where([
185
+ ['posts.created_at', '>=', date("Y-m-d 00:00:00")],
186
+ ['posts.created_at', '<=', date("Y-m-d 23:59:59")]
187
+ ]);
188
+ case "week":
189
+ $query->where([
190
+ ['posts.created_at', '>=', date("Y-m-d 00:00:00", strtotime("-1 week"))],
191
+ ['posts.created_at', '<=', date("Y-m-d 23:59:59")]
192
+ ]);
193
+ case "month":
194
+ $query->where([
195
+ ['posts.created_at', '>=', date("Y-m-d 00:00:00", strtotime("-1 month"))],
196
+ ['posts.created_at', '<=', date("Y-m-d 23:59:59")]
197
+ ]);
198
+ case "period":
199
+ $query->where([
200
+ ['posts.created_at', '>=', date("{$period_start} 00:00:00")],
201
+ ['posts.created_at', '<=', date("{$period_end} 23:59:59")]
202
+ ]);
203
+ }
204
+ }
205
+
206
+ if ($keyword !== null) {
207
+ // tags search
208
+ if (count($tags) !== 0) {
209
+ $query
210
+ ->join('post_tags', 'posts.id', '=', 'post_tags.post_id')
211
+ ->join('tags', 'post_tags.tag_id', '=', 'tags.id')
212
+ ->whereIn('tags.name', $tags)
213
+ ->groupBy('posts.id')
214
+ ->havingRaw('count(distinct tags.id) = ?', [count($tags)]);
215
+ }
216
+
217
+ // keywords search
218
+ foreach ($no_tag_keywords as $no_tag_keyword) {
219
+ $query
220
+ ->where(function ($query) use ($no_tag_keyword) {
221
+ $query
222
+ ->where('posts.title', 'like', '%' . $no_tag_keyword . '%')
223
+ ->orWhere('posts.body', 'LIKE', "%{$no_tag_keyword}%");
224
+ });
225
+ }
226
+ }
227
+
228
+
229
+ // search order
230
+ if ($order == 'new') {
231
+ $posts = $query->orderBy('posts.created_at', 'desc')->paginate(20);
232
+ } else {
233
+ $posts = $query->orderBy('likes_count', 'desc')->get();
234
+ }
235
+ return $posts;
236
+ }
237
+ }
238
+ ```
239
+
139
240
  ご教授のほどよろしくお願い致します。

3

エラー文を追加

2020/11/12 08:41

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -99,6 +99,12 @@
99
99
 
100
100
  しかし、
101
101
 
102
+ エラー
103
+ ```
104
+ 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)
105
+ ```
106
+
107
+ 調べてみると、
102
108
  `withCount()`は、`having()`と`->paginate()`と併用して利用できないことが判明しました。
103
109
 
104
110
  [【Laravelのissue】withCount(), having() and paginate() together not working](https://github.com/laravel/framework/issues/28476)

2

`having()`を追加

2020/11/12 08:38

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -99,7 +99,7 @@
99
99
 
100
100
  しかし、
101
101
 
102
- `withCount()`は、`->paginate()`と併用して利用できないことが判明しました。
102
+ `withCount()`は、`having()`と`->paginate()`と併用して利用できないことが判明しました。
103
103
 
104
104
  [【Laravelのissue】withCount(), having() and paginate() together not working](https://github.com/laravel/framework/issues/28476)
105
105
 

1

詳細を記述

2020/11/12 08:24

投稿

gyu
gyu

スコア3

title CHANGED
File without changes
body CHANGED
@@ -1,6 +1,6 @@
1
1
  # 問題
2
2
 
3
- 下記のSQL文をクエリビルダに変換し、投稿(posts)のいいね(likes)の総数を取得したい。
3
+ 下記のSQL文をクエリビルダに変換し、投稿(posts)のいいね(likes)の総数を取得したい。
4
4
 
5
5
  ```SQL
6
6
  SELECT *,count(likes.id) AS likes_count
@@ -82,7 +82,10 @@
82
82
  その集計を元に指定のいいね数(最低:$lgtm_min, 最高: max)の範囲の記事を取得。
83
83
 
84
84
  ```
85
- $query = Post::withCount('likes');
85
+ $query = DB::table('posts')
86
+ ->join('likes', 'posts.id', '=', 'likes.post_id')
87
+ ->select('*', 'count(likes.id) AS likes_count')
88
+ ->groupBy('posts.id');
86
89
 
87
90
  if ($lgtm_min !== null) {
88
91
  $query->having('likes_count', '>=', $lgtm_min);
@@ -102,7 +105,7 @@
102
105
 
103
106
  そのため、`withCount()`を利用せずに`likes_count`を定義する必要が出てきました。
104
107
 
105
- そして、下記のSQL文をクエリビルダに変換する必要があります
108
+ そして、下記のSQL文をクエリビルダに変換しようと考えています
106
109
 
107
110
  ```SQL
108
111
  SELECT *,count(likes.id) AS likes_count
@@ -112,6 +115,19 @@
112
115
  GROUP BY posts.id
113
116
  ```
114
117
 
118
+ ```
119
+ $query = Post::withCount('likes');
120
+
121
+ if ($lgtm_min !== null) {
122
+ $query->having('likes_count', '>=', $lgtm_min);
123
+ }
124
+ if ($lgtm_max !== null) {
125
+ $query->having('likes_count', '<=', $lgtm_max);
126
+ }
127
+
128
+ $posts = $query->orderBy('posts.created_at', 'desc')->paginate(20);
129
+ ```
130
+
115
131
  今はSQLで解決しようとしてますが、これが難しければ集計テーブルを作成し、それを`join`で結合するかも検討しています。
116
132
 
117
133
  ご教授のほどよろしくお願い致します。