我有两张表:listings
,其中包含产品的详细信息; bids
,其中包含网站的出价历史记录。
要获得简要相关性,请假设listings
包含以下字段:名称, category_id ,标语, short_description , seller_notes 。
在bids
我们有两个相关字段: listing_id 和 bid_amount 。
由于其他地方相关的原因,这需要在Eloquent中,因为我需要访问该模型。
问题似乎与MAX
出价金额线或bid_amount
的评估有关 - 无论我如何接近它,我总是得到一个不相关的结果,但没有明显的错误我能看到。
$listings = \App\Listing::join('bids', 'listings.id', '=', 'bids.listing_id')->select('listings.*','MAX(bids.bid_amount)');
if( !empty($request->keyword) ) {
$listings = $listings->where('listings.name','LIKE','%'.$request->keyword.'%')
->orWhere('listings.tagline','LIKE','%'.$request->keyword.'%')
->orWhere('listings.short_description','LIKE','%'.$request->keyword.'%')
->orWhere('listings.seller_notes','LIKE','%'.$request->keyword.'%');
}
if( !empty($request->category) ) {
$listings = $listings->where('listings.category_id','=',$request->category);
}
if( !empty($request->minimum_bid) && !empty($request->maximum_bid) ) {
$listings = $listings->whereBetween('bid_amount', [$request->minimum_bid, $request->maximum_bid]);
} else {
if( !empty($request->minimum_bid) ) {
$listings = $listings->where('bid_amount', '>', $request->minimum_bid);
}
if( !empty($request->maximum_bid) ) {
$listings = $listings->where('bid_amount', '<', $request->maximum_bid);
}
}
搜索是查找当前最高出价在minimum_bid
和maximum_bid
之间的结果(搜索框中的字段)。出现的问题是,可能有多个列表(listing_id
)在这些金额之间具有当前最高出价(MAX(bid_amount)
)。我想显示MAX(bid_amount)
bid.listing_id
位于minimum_bid
和maximum_bid
之间的商家信息。这可能会导致多个列表。
等效的MySQL查询应该是:
SELECT *
FROM listings
JOIN (SELECT listing_id,
Max(bid_amount) AS bid_amount
FROM bids
GROUP BY listing_id) bids
ON bids.listing_id = listings.id
WHERE ( listings.NAME LIKE '%keyword%'
OR listings.tagline LIKE '%keyword%'
OR listings.short_description LIKE' %keyword%'
OR listings.seller_notes LIKE '%keyword%' )
AND ( bids.bid_amount > minimum_bid )
AND ( bids.bid_amount < maximum_bid )
我知道这是愚蠢的,我做错了,我真的可以使用一双新鲜的眼睛。感谢您提供的任何帮助。
答案 0 :(得分:2)
问题中代码的问题在于,当我们实际需要每个列表的每组出价的最大值时,它会创建一个查询,以尝试查找出价所有的最高出价金额。让我们来看看如何用Eloquent做到这一点。我们将使用local query scopes来封装逻辑并清理API,以便我们可以像这样调用它:
$listings = Listing::withMaximumBidAmount()
->forOptionalCategory($request->category)
->forOptionalBidRange($request->minimum_bid, $request->maximum_bid)
->forOptionalKeyword($request->keyword)
->paginate($pageSize);
我假设Listing
模型与bids()
模型之间存在Bid
关系:
class Listing extends Model
{
...
public function bids()
{
return $this->hasMany(\App\Bid::class);
}
接下来,让我们添加将最高出价金额映射到模型的最重要范围:
public function scopeWithMaximumBidAmount($query)
{
$bids = $this->bids();
$bidsTable = $bids->getRelated()->getTable();
$listingKey = $bids->getForeignKeyName();
$bidsQuery = \App\Bid::select($listingKey)
->selectRaw('MAX(bid_amount) as bid_amount')
->groupBy($listingKey);
$subquery = DB::raw("({$bidsQuery->toSql()}) " . $bidsTable);
return $query->select($this->getTable() . '.*', 'bid_amount')
->join($subquery, function ($join) use ($bids) {
$join->on(
$bids->getQualifiedForeignKeyName(),
'=',
$bids->getQualifiedParentKeyName()
);
});
}
此实现使用模型和关系上的元数据来设置查询的表名和列,因此如果这些更改,我们不需要更新此方法。我们可以看到,我们首先构建一个子查询来加入,计算每个列表的最高出价。不幸的是,大部分内容都没有记录 - Laravel源代码是高级案例的必要参考。
上面的示例显示了我们如何为连接表提供原始SQL子查询,并传递闭包而不是列名来为连接添加任何专用子句。我们使用标准的Eloquent查询构建器创建子查询 - 因此我们可以根据需要安全地绑定参数 - 并使用toSql()
方法将其转换为SQL字符串。此范围会为包含最高出价的调用返回的每个bid_amount
添加Listing
属性。
以下是剩余的查询范围。这些都是不言自明的:
public function scopeForOptionalKeyword($query, $keyword)
{
if (empty($keyword)) {
return $query;
}
return $query->where(function ($query) use ($keyword) {
$query->where('name', 'LIKE', "%{$keyword}%")
->orWhere('tagline', 'LIKE', "%{$keyword}%")
->orWhere('short_description', 'LIKE', "%{$keyword}%")
->orWhere('seller_notes', 'LIKE', "%{$keyword}%");
});
}
public function scopeForOptionalCategory($query, $category)
{
if (empty($category)) {
return $query;
}
return $query->where('category_id', $category);
}
public function scopeForOptionalBidRange($query, $minimum, $maximum)
{
if (! empty($minimum)) {
$query->where('bid_amount', '>=', $minimum);
}
if (! empty($maximum)) {
$query->where('bid_amount', '<=', $maximum);
}
return $query;
}
当我们要匹配所需的SQL查询时,toSql()
方法在构造复杂查询构建器对象时确实很有用。如果遇到性能问题,我们可能还想检查表中的索引。如果我们有资源,可能值得索引bids.bid_amount
或调整关键字查找列。
答案 1 :(得分:1)
在加入查询中使用 DB :: raw ,将最高 bid_amount 设为 bid_amount 即可。我在您的查询中做了一些更改,现在应该可以正常工作。
$listings = Listing::join(DB::raw('(select listing_id, Max(bid_amount) as bid_amount from bids GROUP BY listing_id) bids'),'bids.listing_id','listings.id');
if( !empty($request->keyword) ) {
$listings = $listings->where('listings.name','LIKE','%'.$request->keyword.'%')
->orWhere('listings.tagline','LIKE','%'.$request->keyword.'%')
->orWhere('listings.short_description','LIKE','%'.$request->keyword.'%')
->orWhere('listings.seller_notes','LIKE','%'.$request->keyword.'%');
}
if( !empty($request->category) ) {
$listings = $listings->where('listings.category_id','=',$request->category);
}
if( !empty($request->minimum_bid) && !empty($request->maximum_bid) ) {
$listings = $listings->whereBetween('bid_amount', [$request->minimum_bid, $request->maximum_bid]);
} else {
if( !empty($request->minimum_bid) ) {
$listings = $listings->where('bid_amount', '>', $request->minimum_bid);
}
if( !empty($request->maximum_bid) ) {
$listings = $listings->where('bid_amount', '<', $request->maximum_bid);
}
}
return $listings->get();
希望有所帮助:)
答案 2 :(得分:0)
所以我离得太远了,这感觉很糟糕......如果有人可以建议如何改进,那就真的很有帮助。这很容易导致查询太多,而且无论如何它绝对不是最佳实践。我一直只是盯着该死的东西,以至于我无法看到更好/更清洁的方式。
// Create an instance that we can refine.
$listings = \App\Listing::select('*');
// Check if the keyword search exists in any relevant column.
if( !empty($request->keyword) ) {
$listings = $listings->where('listings.name','LIKE','%'.$request->keyword.'%')
->orWhere('listings.tagline','LIKE','%'.$request->keyword.'%')
->orWhere('listings.short_description','LIKE','%'.$request->keyword.'%')
->orWhere('listings.seller_notes','LIKE','%'.$request->keyword.'%');
}
// Check if the result is in the requested category.
if( !empty($request->category) ) {
$listings = $listings->where('listings.category_id','=',$request->category);
}
$listings = $listings->get();
// Check for a greatest bid between the min and max.
if( !empty($request->minimum_bid) || !empty($request->maximum_bid) ) {
if( !empty($request->minimum_bid) ) {
$listings = $listings->filter(function($listing) use( $request ){
return \App\Bid::getCurrentHighestBid($listing->id) >= $request->minimum_bid;
});
}
if( !empty($request->maximum_bid) ) {
$listings = $listings->filter(function($listing) use( $request ){
return \App\Bid::getCurrentHighestBid($listing->id) <= $request->maximum_bid;
});
}
}
// We're left with a collection, but we need to paginate the results.
// We use the collection to perform a secondary query to get only the
// relevant rows. *cough*hack*cough*
$listings = \App\Listing::select('*')->whereIn('id', $listings->pluck('id'))->paginate(30);
// Pass it on to the view.
return view('listings.main', ['listings' => $listings]);
我真的希望这可以帮助某人,并且可以改进。 Laravel具有用户友好的文档,但似乎缺乏真正的深入参考,所以我不确定这些函数是否以正确的方式使用。
答案 3 :(得分:0)
如果我理解你是正确的,你想获取minimum_bid和maximum_bid之间的所有列表,并按listing_id对它们进行排序?
你可以这样做:
//This gets you array of collections sorted by listing_id and sorted from
highest bid_amount to lowest.
$listings = Listing::join('bids', 'listings.id', '=', 'bids.listing_id');
if (!empty($request->minimum_bid) && !empty($request->maximum_bid)) {
$listings->whereBetween('bid_amount', [$request->minimum_bid,
$request->maximum_bid]);
} else {
if( !empty($minimum_bid) ) {
$listings->where('bid_amount', '>', $request->minimum_bid);
}
if( !empty($request->maximum_bid) ) {
$listings=$listings->where('bid_amount', '<', $request->maximum_bid);
}
}
$listings = $listings->get()
->sortByDesc('bid_amount')
->groupBy('listing_id');
结果看起来像这样(我测试了两个列表):
Collection {#196 ▼
#items: array:2 [▼
2 => Collection {#184 ▼
#items: array:3 [▼
0 => Listing {#204 ▶}
1 => Listing {#208 ▶}
2 => Listing {#207 ▶}
]
}
1 => Collection {#197 ▼
#items: array:4 [▼
0 => Listing {#203 ▶}
1 => Listing {#205 ▶}
2 => Listing {#206 ▶}
3 => Listing {#202 ▶}
]
}
]
}