回答編集履歴
16
Oron → orOn
test
CHANGED
@@ -30,7 +30,7 @@
|
|
30
30
|
|
31
31
|
$join->on('information.money', '=', 'filter.money');
|
32
32
|
|
33
|
-
$join->O
|
33
|
+
$join->orOn('information.money', '>', 'filter.money');
|
34
34
|
|
35
35
|
});
|
36
36
|
|
15
id ソートは money に含める
test
CHANGED
@@ -188,6 +188,6 @@
|
|
188
188
|
|
189
189
|
`information`.`money` asc,
|
190
190
|
|
191
|
-
`it
|
191
|
+
`information`.`id` asc
|
192
192
|
|
193
193
|
```
|
14
id ソートは money に含める
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
使いやすくしておく
test
CHANGED
@@ -46,7 +46,7 @@
|
|
46
46
|
|
47
47
|
|
48
48
|
|
49
|
-
public function scope
|
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(
|
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
|
-
->
|
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
テーブル名
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
大幅に修正
test
CHANGED
@@ -4,29 +4,25 @@
|
|
4
4
|
|
5
5
|
```php
|
6
6
|
|
7
|
-
class Item extends
|
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
|
-
->
|
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
|
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->o
|
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
|
-
|
97
|
+
まず発想の転換が必要です。結果的に **表示したいレコードは `item_shop` テーブル行単位** なので,カスタムピボットクラス `ItemShop` が必要なことに注意しましょう。これに `Item` と `Shop` と `Information` を後から Eager Loading で取ってくるという流れでいきましょう。
|
98
|
+
|
99
|
+
**(複数商品が選択された場合,同じ店舗が商品数ぶんだけ現れてもいいと考える)**
|
58
100
|
|
59
101
|
|
60
102
|
|
61
|
-
**「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを対象として
|
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('
|
113
|
+
ItemShop::with('item', 'shop', 'information')
|
72
114
|
|
73
|
-
->
|
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
|
151
|
+
`items`
|
110
152
|
|
111
|
-
on `items`.`id` = `item
|
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 `it
|
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
|
-
`
|
179
|
+
`name` like ?
|
166
180
|
|
167
181
|
and `filter`.`id` is null
|
168
182
|
|
10
テーブル名指定を修正
test
CHANGED
@@ -26,7 +26,7 @@
|
|
26
26
|
|
27
27
|
->leftJoinSub(clone $query, 'filter', function (JoinClause $join) {
|
28
28
|
|
29
|
-
$join->on('i
|
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 `i
|
151
|
+
on `item_shop`.`item_id` = `filter`.`item_id`
|
152
152
|
|
153
153
|
and (
|
154
154
|
|
9
typo
test
CHANGED
@@ -34,7 +34,7 @@
|
|
34
34
|
|
35
35
|
$join->on('information.money', '=', 'filter.money');
|
36
36
|
|
37
|
-
$join->O
|
37
|
+
$join->orOn('information.money', '>', 'filter.money');
|
38
38
|
|
39
39
|
});
|
40
40
|
|
8
SQL変換
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
修正
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
修正
test
CHANGED
@@ -12,15 +12,21 @@
|
|
12
12
|
|
13
13
|
{
|
14
14
|
|
15
|
-
|
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
|
-
->le
|
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_
|
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
|
-
|
57
|
+
メインクエリに `item_shop` `information` を結合しつつ,サブクエリとしてそれらのコピーも用意します。`information` 単独では `item_id` が取得できず,これらはセットにする必要があるためです。
|
52
58
|
|
53
59
|
|
54
60
|
|
55
|
-
|
61
|
+
**「金額がより高いもの」** または **「金額が同じだが情報IDがより大きいもの」** だけを対象として先ほどの組み合わせのオリジナルとコピーを外部結合し,結合相手が存在しなかったところには `NULL` が埋められます。そして,`NULL` が埋められたところだけを逆に残せば **「金額が最も安いもの」** だけが残ります。
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
これをスコープとして定義しましょう。
|
56
66
|
|
57
67
|
|
58
68
|
|
5
修正
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
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
シンタックスエラー
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
修正
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
リターン
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
|
|