Larval左边加入Eloquent导致id字段被覆盖

时间:2017-05-30 00:48:32

标签: mysql laravel eloquent

我正在使用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覆盖。 我已经完成了一些搜索,并在->select('businesses.*')解决此问题之前阅读了leftJoin,但是通过这样做我收到以下错误:

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)

第二个问题是左连接似乎得到了第一条记录,我想获得最新的记录(created_at)。您可以看到我的控制器中已经有一个orderBy行,但这没有任何影响,因为即使有更新的记录,我仍然可以看到它仍然返回旧记录。

4 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:0)

您可以为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();

答案 2 :(得分:0)

如果您在控制器中使用->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 :(得分:0)

我认为您不应该重新分配$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();