Laravel Eloquent 最大秒 table?
Laravel Eloquent with the max in second table?
我有两个表:listings
,其中包含产品的详细信息,以及 bids
,其中包含网站的出价历史记录。
为了简要关联,假设 listings
具有以下字段:name、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)
)。我想显示 bid.listing_id
的 MAX(bid_amount)
在 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 )
我知道这是我做错的愚蠢的事情,我真的可以用一双新的眼睛。感谢您提供的任何帮助。
所以我并没有离得太远,这感觉很老套...如果有人可以建议如何改进,那将非常有帮助。这很容易导致过多的查询,而且这绝对不是最佳做法。我一直盯着这该死的东西看太久了,以至于我看不到 better/cleaner 的方法。
// 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 有用户友好的文档,但似乎缺乏真正深入的参考,所以我永远不确定这些函数是否以正确的方式使用。
如果我理解你是对的,你想获取 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 ▶}
]
}
]
}
在您的 join 查询中使用 DB::raw 以获得 Max 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();
希望对您有所帮助:)
问题中代码的问题在于它创建了一个查询,试图在我们实际需要每个出价的 所有 的出价中找到最大出价每个列表的出价组。让我们看看如何用 Eloquent 来做到这一点。我们将使用 local query scopes 来封装逻辑并清理 API 以便我们可以这样调用它:
$listings = Listing::withMaximumBidAmount()
->forOptionalCategory($request->category)
->forOptionalBidRange($request->minimum_bid, $request->maximum_bid)
->forOptionalKeyword($request->keyword)
->paginate($pageSize);
我假设 Listing
模型包含与 Bid
模型的 bids()
关系:
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()
);
});
}
此实现使用模型和关系上的元数据来设置查询的 table 名称和列,因此如果这些更改,我们不需要更新此方法。如我们所见,我们首先构建一个子查询以加入计算每个列表的最高出价。不幸的是,其中大部分内容都没有记录——Laravel 源代码是高级案例的必要参考。
上面的示例显示了我们如何为连接的 table 提供一个原始的 SQL 子查询,并传递一个闭包而不是列名来为连接添加任何专门的子句。我们使用标准 Eloquent 查询构建器创建子查询——因此我们可以在需要时安全地绑定参数——并使用 toSql()
方法将其转换为 SQL 字符串。此范围向包含最高出价的调用返回的每个 Listing
添加一个 bid_amount
属性。
这是剩余的查询范围。这些都是不言自明的:
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()
方法在构建复杂的查询生成器对象时确实很有帮助。如果遇到性能问题,我们可能还想检查 table 上的索引。如果我们有资源,可能值得索引 bids.bid_amount
或调整关键字查找列。
我有两个表:listings
,其中包含产品的详细信息,以及 bids
,其中包含网站的出价历史记录。
为了简要关联,假设 listings
具有以下字段:name、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)
)。我想显示 bid.listing_id
的 MAX(bid_amount)
在 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 )
我知道这是我做错的愚蠢的事情,我真的可以用一双新的眼睛。感谢您提供的任何帮助。
所以我并没有离得太远,这感觉很老套...如果有人可以建议如何改进,那将非常有帮助。这很容易导致过多的查询,而且这绝对不是最佳做法。我一直盯着这该死的东西看太久了,以至于我看不到 better/cleaner 的方法。
// 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 有用户友好的文档,但似乎缺乏真正深入的参考,所以我永远不确定这些函数是否以正确的方式使用。
如果我理解你是对的,你想获取 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 ▶}
]
}
]
}
在您的 join 查询中使用 DB::raw 以获得 Max 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();
希望对您有所帮助:)
问题中代码的问题在于它创建了一个查询,试图在我们实际需要每个出价的 所有 的出价中找到最大出价每个列表的出价组。让我们看看如何用 Eloquent 来做到这一点。我们将使用 local query scopes 来封装逻辑并清理 API 以便我们可以这样调用它:
$listings = Listing::withMaximumBidAmount()
->forOptionalCategory($request->category)
->forOptionalBidRange($request->minimum_bid, $request->maximum_bid)
->forOptionalKeyword($request->keyword)
->paginate($pageSize);
我假设 Listing
模型包含与 Bid
模型的 bids()
关系:
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()
);
});
}
此实现使用模型和关系上的元数据来设置查询的 table 名称和列,因此如果这些更改,我们不需要更新此方法。如我们所见,我们首先构建一个子查询以加入计算每个列表的最高出价。不幸的是,其中大部分内容都没有记录——Laravel 源代码是高级案例的必要参考。
上面的示例显示了我们如何为连接的 table 提供一个原始的 SQL 子查询,并传递一个闭包而不是列名来为连接添加任何专门的子句。我们使用标准 Eloquent 查询构建器创建子查询——因此我们可以在需要时安全地绑定参数——并使用 toSql()
方法将其转换为 SQL 字符串。此范围向包含最高出价的调用返回的每个 Listing
添加一个 bid_amount
属性。
这是剩余的查询范围。这些都是不言自明的:
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()
方法在构建复杂的查询生成器对象时确实很有帮助。如果遇到性能问题,我们可能还想检查 table 上的索引。如果我们有资源,可能值得索引 bids.bid_amount
或调整关键字查找列。