我正在使用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
行,但这没有任何影响,因为即使有更新的记录,我仍然可以看到它仍然返回旧记录。
答案 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();