Laravel Eloquent 最大秒 table?

Laravel Eloquent with the max in second table?

我有两个表:listings,其中包含产品的详细信息,以及 bids,其中包含网站的出价历史记录。

为了简要关联,假设 listings 具有以下字段:namecategory_id标语short_descriptionseller_notes.

bids中我们有两个相关字段:listing_idbid_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_bidmaximum_bid 之间的结果(搜索框中的字段)。出现的问题是可能有多个列表 (listing_id) 在这些金额之间具有当前最高出价 (MAX(bid_amount))。我想显示 bid.listing_idMAX(bid_amount)minimum_bidmaximum_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_amountbid_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 或调整关键字查找列。