【问题标题】:Larval left join with Eloquent causing id field to be overwritten幼虫与 Eloquent 左连接导致 id 字段被覆盖
【发布时间】:2017-05-30 00:48:31
【问题描述】:

我正在使用 Laravel 构建搜索列表应用程序;该应用程序需要按距离(我已经建立)搜索商家,并包含对商家的最新订阅(如果有),以便我可以按距离订购。

这里的两种模式是商业模式和订阅模式。一个企业可以有多个订阅(尽管永远只有一个处于活动状态)。

控制器

$businesses = Business::distance($place['lat'], $place['lng'], $request->distance)
                        ->ofShopType($request->shop_type)
                        ->uptoBudget($request->price)
                        ->leftJoin('subscriptions', function($join) {
                            $join->on('businesses.id', '=', 'subscriptions.business_id')
                                 ->orderBy('subscriptions.created_at', 'desc');
                        });
return $businesses = $businesses->get();

商业模式

public function scopeDistance($query,$from_latitude,$from_longitude,$distance)
{
    $raw = \DB::raw('ROUND ( ( 3959 * acos( cos( radians('.$from_latitude.') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians('.$from_longitude.') ) + sin( radians('.$from_latitude.') ) * sin( radians( latitude ) ) ) ), 1 ) AS distance');
    return $query->select('*')->addSelect($raw)
             ->orderBy( 'distance', 'ASC' )
             ->groupBy('businesses.id')
             ->having('distance', '<=', $distance);
}

所以我遇到的困难是business.id 字段被subscription.id 覆盖。 我已经进行了一些搜索,并阅读了在 leftJoin 之前包含 -&gt;select('businesses.*') 应该解决此问题,但是这样做会出现以下错误:

SQLSTATE[42S22]: Column not found: 1054 Unknown column
      'business.distance' in 'having clause'
      (SQL: select `businesses`.*
          from `businesses`
          left join `subscriptions`
                   on `businesses`.`id` = `subscriptions`.`business_id`
          where `shop_type_id` = 1
            and `min_cost` <= 100
          group by `distance`, `businesses`.`id`
          having `distance` <= 50
          order by `distance` asc)

第二个问题是left join好像拿到了第一条记录,我想获取最近的一条记录(bycreated_at)。您可以看到我的控制器中已经有一个 orderBy 行,但这没有影响,因为我可以看到它仍然返回旧记录,即使有新记录可用。

【问题讨论】:

  • 你为什么按distance分组?
  • 这是一个错误,我已经更新了查询。距离字段仍然存在问题,但现在在有子句中。
  • 有时编写 SQL 比弄清楚如何使用 3rd 方软件创建 SQL 更容易。
  • @Imran 请在 sql fiddle 上添加您的表

标签: mysql laravel eloquent


【解决方案1】:

您说得对,使用select('businesses.*') 将防止id 字段被覆盖。

我建议如下使用selectRaw

public function scopeDistance($query, $from_latitude, $from_longitude, $distance)
{
    $raw = \DB::raw('ROUND ( ( 3959 * acos( cos( radians('.$from_latitude.') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians('.$from_longitude.') ) + sin( radians('.$from_latitude.') ) * sin( radians( latitude ) ) ) ), 1 ) AS distance');
    return $query->selectRaw("businesses.*, $raw")->orderBy( 'distance', 'ASC' )->groupBy('businesses.id')->having('distance', '<=', $distance);
}

【讨论】:

    【解决方案2】:

    如果您在控制器中使用-&gt;select('businesses.*'),您将覆盖您从scopeDistance() 中选择的内容。 您应该在范围函数中使用表别名。我还会从范围函数中删除groupBy(), 因为它在那里没有意义。 最好也删除orderBy(),因为以后不能(轻松)覆盖它。

    public function scopeDistance($query,$from_latitude,$from_longitude,$distance)
    {
        $raw = \DB::raw('ROUND ( ( 3959 * acos( cos( radians('.$from_latitude.') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians('.$from_longitude.') ) + sin( radians('.$from_latitude.') ) * sin( radians( latitude ) ) ) ), 1 ) AS distance');
        return $query->select('businesses.*')->addSelect($raw)
            ->having('distance', '<=', $distance);
    }
    

    如果您想按上次订阅创建时间对结果进行排序,则需要使用聚合函数进行排序。 在您的情况下,它应该是ORDER BY MAX(subscriptions.created_at) DESC。这里也是 GROUP BY business.id 的正确位置。 在 JOIN 子句中使用 ORDER BY 没有任何意义。 我希望得到一个异常,因为我不知道如何将其转换为有效的 SQL 表达式。 但似乎 laravel 只是忽略了它。 所以你的控制器中的代码可能是:

    $businesses = Business::distance($place['lat'], $place['lng'], $request->distance)
        ->ofShopType($request->shop_type)
        ->uptoBudget($request->price)
        ->leftJoin('subscriptions', 'businesses.id', '=', 'subscriptions.business_id')
        ->groupBy('business.id')
        ->orderByRaw('max(subscriptions.created_at) DESC')
        ->orderBy('distance', 'ASC');
    return $businesses->get();
    

    【讨论】:

    • 虽然这确实按我想要的顺序返回,但它不包括匹配的订阅记录。你能补充一下如何包含这个吗?
    【解决方案3】:

    您可以将lastSubscription() 关系添加到您的Business 模型:

    public function lastSubscription()
    {
        return $this->hasOne(Subscription::class)->orderBy('created_at', 'desc');
    }
    

    然后在你的控制器中你就可以做到:

    return Business::distance($place['lat'], $place['lng'], $request->distance)
                            ->ofShopType($request->shop_type)
                            ->uptoBudget($request->price)
                            ->with('lastSubscription')
                            ->get();
    

    【讨论】:

      【解决方案4】:

      我认为您不应该重新评估$bussinesses var,而只返回查询结果。

      return Business::distance($place['lat'], $place['lng'], $request->distance)
                              ->ofShopType($request->shop_type)
                              ->uptoBudget($request->price)
                              ->leftJoin('subscriptions', function($join) {
                                  $join->on('businesses.id', '=', 'subscriptions.business_id')
                                       ->orderBy('subscriptions.created_at', 'desc');
                              })->get();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-04
        • 1970-01-01
        • 2018-07-05
        • 1970-01-01
        相关资源
        最近更新 更多