回答編集履歴

16

Oron → orOn

2019/05/03 19:04

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -30,7 +30,7 @@
30
30
 
31
31
  $join->on('information.money', '=', 'filter.money');
32
32
 
33
- $join->Oron('information.money', '>', 'filter.money');
33
+ $join->orOn('information.money', '>', 'filter.money');
34
34
 
35
35
  });
36
36
 

15

id ソートは money に含める

2019/05/03 19:04

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -188,6 +188,6 @@
188
188
 
189
189
  `information`.`money` asc,
190
190
 
191
- `items`.`id` asc
191
+ `information`.`id` asc
192
192
 
193
193
  ```

14

id ソートは money に含める

2019/05/03 12:11

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -40,7 +40,9 @@
40
40
 
41
41
  ->select('item_shop.*')
42
42
 
43
- ->orderBy('information.money');
43
+ ->orderBy('information.money')
44
+
45
+ ->orderBy('information.id');
44
46
 
45
47
  }
46
48
 
@@ -120,8 +122,6 @@
120
122
 
121
123
  ->orderByLowestMoney()
122
124
 
123
- ->orderBy('items.id')
124
-
125
125
  ->get();
126
126
 
127
127
  ```

13

使いやすくしておく

2019/05/03 12:11

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -46,7 +46,7 @@
46
46
 
47
47
 
48
48
 
49
- public function scopeSearchByItemName(Builder $query, string $keyword): Builder
49
+ public function scopeWhereItem(Builder $query, ...$args): Builder
50
50
 
51
51
  {
52
52
 
@@ -54,7 +54,11 @@
54
54
 
55
55
  ->join('items', 'item_shop.item_id', '=', 'items.id')
56
56
 
57
- ->where('items.name', 'like', '%' . addcslashes($keyword, '\_%') . '%');
57
+ ->where(function (Builder $query) use ($args) {
58
+
59
+ $query->setModel(new Item())->where(...$args);
60
+
61
+ });
58
62
 
59
63
  }
60
64
 
@@ -112,7 +116,7 @@
112
116
 
113
117
  ItemShop::with('item', 'shop', 'information')
114
118
 
115
- ->searchByItemName($query)
119
+ ->whereItem('items.name', 'like', '%' . addcslashes($query, '\_%') . '%')
116
120
 
117
121
  ->orderByLowestMoney()
118
122
 
@@ -176,7 +180,7 @@
176
180
 
177
181
  where
178
182
 
179
- `items`.`name` like ?
183
+ (`items`.`name` like ?)
180
184
 
181
185
  and `filter`.`id` is null
182
186
 

12

テーブル名

2019/05/03 12:07

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -54,7 +54,7 @@
54
54
 
55
55
  ->join('items', 'item_shop.item_id', '=', 'items.id')
56
56
 
57
- ->where('name', 'like', '%' . addcslashes($keyword, '\_%') . '%');
57
+ ->where('items.name', 'like', '%' . addcslashes($keyword, '\_%') . '%');
58
58
 
59
59
  }
60
60
 
@@ -176,7 +176,7 @@
176
176
 
177
177
  where
178
178
 
179
- `name` like ?
179
+ `items`.`name` like ?
180
180
 
181
181
  and `filter`.`id` is null
182
182
 

11

大幅に修正

2019/05/03 11:49

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -4,29 +4,25 @@
4
4
 
5
5
  ```php
6
6
 
7
- class Item extends Model
7
+ class ItemShop extends Pivot
8
8
 
9
9
  {
10
10
 
11
+ protected $table = 'item_shop';
12
+
13
+
14
+
11
- public function scopeOrderByMoney(Builder $query): Builder
15
+ public function scopeOrderByLowestMoney(Builder $query): Builder
12
16
 
13
17
  {
14
18
 
15
- $query
19
+ return $query
16
-
17
- ->join('item_shop', 'items.id', '=', 'item_shop.item_id')
18
20
 
19
21
  ->join('information', 'item_shop.id', '=', 'information.item_shop_id')
20
22
 
21
- ->select('information.*', 'item_shop.item_id');
23
+ ->leftJoin('information as filter', function (JoinClause $join) {
22
24
 
23
-
24
-
25
- return $query
26
-
27
- ->leftJoinSub(clone $query, 'filter', function (JoinClause $join) {
28
-
29
- $join->on('item_shop.item_id', '=', 'filter.item_id');
25
+ $join->on('information.item_shop_id', '=', 'filter.item_shop_id');
30
26
 
31
27
  $join->on(function (JoinClause $join) {
32
28
 
@@ -34,7 +30,7 @@
34
30
 
35
31
  $join->on('information.money', '=', 'filter.money');
36
32
 
37
- $join->orOn('information.money', '>', 'filter.money');
33
+ $join->Oron('information.money', '>', 'filter.money');
38
34
 
39
35
  });
40
36
 
@@ -42,9 +38,53 @@
42
38
 
43
39
  ->whereNull('filter.id')
44
40
 
45
- ->select('items.*')
41
+ ->select('item_shop.*')
46
42
 
47
43
  ->orderBy('information.money');
44
+
45
+ }
46
+
47
+
48
+
49
+ public function scopeSearchByItemName(Builder $query, string $keyword): Builder
50
+
51
+ {
52
+
53
+ return $query
54
+
55
+ ->join('items', 'item_shop.item_id', '=', 'items.id')
56
+
57
+ ->where('name', 'like', '%' . addcslashes($keyword, '\_%') . '%');
58
+
59
+ }
60
+
61
+
62
+
63
+ public function item(): BelongsTo
64
+
65
+ {
66
+
67
+ return $this->belongsTo(Item::class);
68
+
69
+ }
70
+
71
+
72
+
73
+ public function shop(): BelongsTo
74
+
75
+ {
76
+
77
+ return $this->belongsTo(Shop::class);
78
+
79
+ }
80
+
81
+
82
+
83
+ public function information(): HasMany
84
+
85
+ {
86
+
87
+ return $this->hasMany(Information::class);
48
88
 
49
89
  }
50
90
 
@@ -54,25 +94,27 @@
54
94
 
55
95
 
56
96
 
57
- メインクエリに `item_shop` `information` を結合しつつ,サブクエリしてそれらのコピーも用意しま。`information` 単独では `item_id` できず,これらはセットにする必要があるためです
97
+ まず発想の転換が必要です。結果的**表示したいレコードは `item_shop` テーブル行単位** なので,カスタムピボットクラス `ItemShop` が必要なこに注意しましょうこれに `Item` と `Shop` と `Information` を後から Eager Loading ってくるという流れましょう
98
+
99
+ **(複数商品が選択された場合,同じ店舗が商品数ぶんだけ現れてもいいと考える)**
58
100
 
59
101
 
60
102
 
61
- **「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを対象として先ほどの組み合わせのオリジナルとコピーを外部結合し,結合相手が存在しなかったところには `NULL` 埋められます。そして,`NULL` が埋められたところだけを逆に残せば **「金額が最も安いもの」** だけが残ります。
103
+ `item_shop` テーブルのソートのために,`information` を内部結合します。さらに繰り返して外部結合を行い,この際は **「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを対象として,結合相手が存在しなかったところには `NULL` 埋めます。そして,`NULL` が埋められたところだけを逆に残せば **「金額が最も安いもの」** だけが残ります。これをスコープとして定義しましょう。
62
104
 
63
105
 
64
106
 
65
- これをスコープとして定義しましょう
107
+ `items` に関しては `whereHas` 使っても解決できますが,クロージャだと書きにくい上サブクエリだとパフォーマンが劣化する可能性に繋がるので,こちらもついでにスコープとして定義しておき
66
108
 
67
109
 
68
110
 
69
111
  ```
70
112
 
71
- Item::with('middle.shop', 'middle.information')
113
+ ItemShop::with('item', 'shop', 'information')
72
114
 
73
- ->where('items.name', 'like', '%' . addcslashes($query, '\_%') . '%')
115
+ ->searchByItemName($query)
74
116
 
75
- ->orderByMoney()
117
+ ->orderByLowestMoney()
76
118
 
77
119
  ->orderBy('items.id')
78
120
 
@@ -90,7 +132,7 @@
90
132
 
91
133
 
92
134
 
93
- 実行されるSQL(参考)
135
+ 最初に実行されるSQL(参考)
94
136
 
95
137
 
96
138
 
@@ -98,17 +140,17 @@
98
140
 
99
141
  select
100
142
 
101
- `items`.*
143
+ `item_shop`.*
102
144
 
103
145
  from
104
146
 
105
- `items`
147
+ `item_shop`
106
148
 
107
149
  inner join
108
150
 
109
- `item_shop`
151
+ `items`
110
152
 
111
- on `items`.`id` = `item_shop`.`item_id`
153
+ on `item_shop`.`item_id` = `items`.`id`
112
154
 
113
155
  inner join
114
156
 
@@ -118,37 +160,9 @@
118
160
 
119
161
  left join
120
162
 
121
- (
163
+ `information` as `filter`
122
164
 
123
- select
124
-
125
- `information`.*,
126
-
127
- `item_shop`.`item_id`
128
-
129
- from
130
-
131
- `items`
132
-
133
- inner join
134
-
135
- `item_shop`
136
-
137
- on `items`.`id` = `item_shop`.`item_id`
138
-
139
- inner join
140
-
141
- `information`
142
-
143
- on `item_shop`.`id` = `information`.`item_shop_id`
144
-
145
- where
146
-
147
- `items`.`name` like ?
148
-
149
- ) as `filter`
150
-
151
- on `item_shop`.`item_id` = `filter`.`item_id`
165
+ on `information`.`item_shop_id` = `filter`.`item_shop_id`
152
166
 
153
167
  and (
154
168
 
@@ -162,7 +176,7 @@
162
176
 
163
177
  where
164
178
 
165
- `items`.`name` like ?
179
+ `name` like ?
166
180
 
167
181
  and `filter`.`id` is null
168
182
 

10

テーブル名指定を修正

2019/05/03 11:46

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  ->leftJoinSub(clone $query, 'filter', function (JoinClause $join) {
28
28
 
29
- $join->on('information.item_id', '=', 'filter.item_id');
29
+ $join->on('item_shop.item_id', '=', 'filter.item_id');
30
30
 
31
31
  $join->on(function (JoinClause $join) {
32
32
 
@@ -148,7 +148,7 @@
148
148
 
149
149
  ) as `filter`
150
150
 
151
- on `information`.`item_id` = `filter`.`item_id`
151
+ on `item_shop`.`item_id` = `filter`.`item_id`
152
152
 
153
153
  and (
154
154
 

9

typo

2019/05/03 10:28

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  $join->on('information.money', '=', 'filter.money');
36
36
 
37
- $join->Oron('information.money', '>', 'filter.money');
37
+ $join->orOn('information.money', '>', 'filter.money');
38
38
 
39
39
  });
40
40
 

8

SQL変換

2019/05/03 05:50

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -83,3 +83,93 @@
83
83
 
84
84
 
85
85
  LIKE 検索における `\` `_` `%` のエスケープも忘れずに。また,最安値が重複したときのために第二ソート軸として一意な `id` も入れておきましょう。
86
+
87
+
88
+
89
+ ----
90
+
91
+
92
+
93
+ 実行されるSQL(参考)
94
+
95
+
96
+
97
+ ```sql
98
+
99
+ select
100
+
101
+ `items`.*
102
+
103
+ from
104
+
105
+ `items`
106
+
107
+ inner join
108
+
109
+ `item_shop`
110
+
111
+ on `items`.`id` = `item_shop`.`item_id`
112
+
113
+ inner join
114
+
115
+ `information`
116
+
117
+ on `item_shop`.`id` = `information`.`item_shop_id`
118
+
119
+ left join
120
+
121
+ (
122
+
123
+ select
124
+
125
+ `information`.*,
126
+
127
+ `item_shop`.`item_id`
128
+
129
+ from
130
+
131
+ `items`
132
+
133
+ inner join
134
+
135
+ `item_shop`
136
+
137
+ on `items`.`id` = `item_shop`.`item_id`
138
+
139
+ inner join
140
+
141
+ `information`
142
+
143
+ on `item_shop`.`id` = `information`.`item_shop_id`
144
+
145
+ where
146
+
147
+ `items`.`name` like ?
148
+
149
+ ) as `filter`
150
+
151
+ on `information`.`item_id` = `filter`.`item_id`
152
+
153
+ and (
154
+
155
+ `information`.`id` > `filter`.`id`
156
+
157
+ and `information`.`money` = `filter`.`money`
158
+
159
+ or `information`.`money` > `filter`.`money`
160
+
161
+ )
162
+
163
+ where
164
+
165
+ `items`.`name` like ?
166
+
167
+ and `filter`.`id` is null
168
+
169
+ order by
170
+
171
+ `information`.`money` asc,
172
+
173
+ `items`.`id` asc
174
+
175
+ ```

7

修正

2019/05/03 05:39

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  ->join('item_shop', 'items.id', '=', 'item_shop.item_id')
18
18
 
19
- ->join('information', 'item_shop.id', '=', 'information.item_shop_id');
19
+ ->join('information', 'item_shop.id', '=', 'information.item_shop_id')
20
20
 
21
21
  ->select('information.*', 'item_shop.item_id');
22
22
 

6

修正

2019/05/03 05:37

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -12,15 +12,21 @@
12
12
 
13
13
  {
14
14
 
15
- return $query
15
+ $query
16
16
 
17
17
  ->join('item_shop', 'items.id', '=', 'item_shop.item_id')
18
18
 
19
- ->join('information', 'item_shop.id', '=', 'information.item_shop_id')
19
+ ->join('information', 'item_shop.id', '=', 'information.item_shop_id');
20
20
 
21
- ->leftJoin('information as filter', function (JoinClause $join) {
21
+ ->select('information.*', 'item_shop.item_id');
22
22
 
23
+
24
+
25
+ return $query
26
+
27
+ ->leftJoinSub(clone $query, 'filter', function (JoinClause $join) {
28
+
23
- $join->on('information.item_shop_id', '=', 'filter.item_shop_id');
29
+ $join->on('information.item_id', '=', 'filter.item_id');
24
30
 
25
31
  $join->on(function (JoinClause $join) {
26
32
 
@@ -48,11 +54,15 @@
48
54
 
49
55
 
50
56
 
51
- **「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを対象として `information` 同士外部結合。結合相手が存在なかったころには `NULL` が埋めます。そして,`NULL` が埋めれたところだけを逆残せば **「金額最も安いもの」** だけが残ります。
57
+ メインクエリに `item_shop` `information` を結合しつつ,サブクエリしてそれのコピーも用意します。`information` 単独では `item_id` 取得できず,これはセットする必要あるためです。
52
58
 
53
59
 
54
60
 
55
- `item_shop` `information` は11対応な,結果的最安値店舗に関する `item_shop` だけが残ります。これをスコープとして定義しましょう。
61
+ **「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを象として先ほど組み合わせオリジナルとコピーを外部結合し,結合相手が存在しなかったところ `NULL` が埋められます。そして,`NULL` が埋められたところだけを逆に残せば **「金額最も安いもの」** だけが残ります。
62
+
63
+
64
+
65
+ これをスコープとして定義しましょう。
56
66
 
57
67
 
58
68
 

5

修正

2019/05/03 05:32

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -64,7 +64,7 @@
64
64
 
65
65
  ->orderByMoney()
66
66
 
67
- ->orderBy('id')
67
+ ->orderBy('items.id')
68
68
 
69
69
  ->get();
70
70
 

4

typo

2019/05/03 04:32

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -60,7 +60,7 @@
60
60
 
61
61
  Item::with('middle.shop', 'middle.information')
62
62
 
63
- ->where('item.name', 'like', '%' . addcslashes($query, '\_%') . '%')
63
+ ->where('items.name', 'like', '%' . addcslashes($query, '\_%') . '%')
64
64
 
65
65
  ->orderByMoney()
66
66
 

3

シンタックスエラー

2019/05/03 04:30

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -60,7 +60,7 @@
60
60
 
61
61
  Item::with('middle.shop', 'middle.information')
62
62
 
63
- ->where('item.name', 'like', '%' . addcslashes($query, '\_%') . '%'
63
+ ->where('item.name', 'like', '%' . addcslashes($query, '\_%') . '%')
64
64
 
65
65
  ->orderByMoney()
66
66
 

2

修正

2019/05/03 04:22

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -48,7 +48,7 @@
48
48
 
49
49
 
50
50
 
51
- **「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを対象として `information` 同士を外部結合。結合相手が存在しなかったところには `NULL` が埋められます。そして,`NULL` が埋められたところだけを逆に残せば **「金額が最も小さいもの」** だけが残ります。
51
+ **「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを対象として `information` 同士を外部結合。結合相手が存在しなかったところには `NULL` が埋められます。そして,`NULL` が埋められたところだけを逆に残せば **「金額が最もいもの」** だけが残ります。
52
52
 
53
53
 
54
54
 

1

リターン

2019/05/03 04:21

投稿

mpyw
mpyw

スコア5223

test CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  {
14
14
 
15
- $query
15
+ return $query
16
16
 
17
17
  ->join('item_shop', 'items.id', '=', 'item_shop.item_id')
18
18