Laravel 模型上特征范围内的查询
Scoping Queries within Trait on a Laravel Model
一点背景……
我的应用程序中有两个模型 'locatable'。我可以很容易地在存储库中编写查询 return 我需要的东西,但是,我相信在 Laravel 范围和特征等的帮助下可以做得更好(我可能是错的) .
所以,我想出了以下解决方案:
示例抽象模型
abstract class AbstractModel extends Model implements SomeOtherStuff
{
public function scopeNearby(Builder $query)
{
return $query->selectRaw('
(
6373000 * acos(
cos( radians( ' . $this->location->latitude . ' ) )
* cos( radians( X(' . $this->location->table . '.location) ) )
* cos( radians( Y(' . $this->location->table . '.location) ) - radians( ' . $this->location->longitude . ' ) )
+ sin( radians( ' . $this->location->latitude . ' ) )
* sin( radians( X(' . $this->location->table . '.location) ) )
)
) AS distance
')
// I wanted to use ->with('location') below instead of ->join() but it didn’t work
->join($this->location->table, $this->location->primaryKey, '=', $this->primaryKey);
}
// All other Abstract stuff here
}
示例用户模型
class User extends AbstractModel implements SomeOtherStuff
{
/**
* Get the location of the user.
*/
public function location()
{
return $this->hasOne(‘App\Models\User\Location', 'user_id');
}
// All other model stuff here
}
示例用户控制器
class UserController extends AbstractController
{
/**
* Return the nearby users.
*
* @return \Illuminate\Http\JsonResponse
*/
public function nearby()
{
$users = $this->shield->user()->nearby()->toSql();
// Print out the query to ensure it is structured correctly
echo $users;
}
}
我的问题
上面的解决方案有效,但是,这是非常错误的(在我看来)! nearby()
范围应该只适用于“可定位”的模型。自然地,我认为最好的解决方案是像这样实现一个特征:
trait Locatable
{
/**
* Scope a query to include the distance from the current model.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNearby(Builder $query)
{
// Same as above implementation
}
}
这个问题是我需要的模型的属性是 protected
,因此无法通过特征访问...
我的问题
假设以上是有道理的,请看下面我的问题:
- 有没有更好的方法来实现这个所需的功能?如果有,请有人指出正确的方向吗?
- 这是较低的优先级,但为什么
->with('location')
不起作用?我用错了吗?我希望它能在查询中添加一个内部连接,但它没有……
实际上应该这样做
trait Locatable {
/**
* Scope a query to include the distance from the current model.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNearby($query)
{
if(!$this->location) {
return $query;
}
return $query->whereHas('location',
function($query) {
$query->selectRaw('
//Your query for distance (Use the getTable() function wherever required...)
) AS distance
')
->having('distance', '<', 25);
}
);
}
}
回答您的问题。
以上是更好的方法。您应该使用 whereHas()
进行关系查询。
with 查询将不起作用,因为 with 不连接表,它只执行 WHERE EXISTS () 查询,然后将其作为与返回模型的关系添加。想要了解更多……你能做的是……
(在你的控制器中任何你想写的地方......只是为了测试)
dd($user->with('locations')->toSql());
如果您遇到任何问题,请告诉我...:)
一点背景……
我的应用程序中有两个模型 'locatable'。我可以很容易地在存储库中编写查询 return 我需要的东西,但是,我相信在 Laravel 范围和特征等的帮助下可以做得更好(我可能是错的) .
所以,我想出了以下解决方案:
示例抽象模型
abstract class AbstractModel extends Model implements SomeOtherStuff
{
public function scopeNearby(Builder $query)
{
return $query->selectRaw('
(
6373000 * acos(
cos( radians( ' . $this->location->latitude . ' ) )
* cos( radians( X(' . $this->location->table . '.location) ) )
* cos( radians( Y(' . $this->location->table . '.location) ) - radians( ' . $this->location->longitude . ' ) )
+ sin( radians( ' . $this->location->latitude . ' ) )
* sin( radians( X(' . $this->location->table . '.location) ) )
)
) AS distance
')
// I wanted to use ->with('location') below instead of ->join() but it didn’t work
->join($this->location->table, $this->location->primaryKey, '=', $this->primaryKey);
}
// All other Abstract stuff here
}
示例用户模型
class User extends AbstractModel implements SomeOtherStuff
{
/**
* Get the location of the user.
*/
public function location()
{
return $this->hasOne(‘App\Models\User\Location', 'user_id');
}
// All other model stuff here
}
示例用户控制器
class UserController extends AbstractController
{
/**
* Return the nearby users.
*
* @return \Illuminate\Http\JsonResponse
*/
public function nearby()
{
$users = $this->shield->user()->nearby()->toSql();
// Print out the query to ensure it is structured correctly
echo $users;
}
}
我的问题
上面的解决方案有效,但是,这是非常错误的(在我看来)! nearby()
范围应该只适用于“可定位”的模型。自然地,我认为最好的解决方案是像这样实现一个特征:
trait Locatable
{
/**
* Scope a query to include the distance from the current model.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNearby(Builder $query)
{
// Same as above implementation
}
}
这个问题是我需要的模型的属性是 protected
,因此无法通过特征访问...
我的问题
假设以上是有道理的,请看下面我的问题:
- 有没有更好的方法来实现这个所需的功能?如果有,请有人指出正确的方向吗?
- 这是较低的优先级,但为什么
->with('location')
不起作用?我用错了吗?我希望它能在查询中添加一个内部连接,但它没有……
实际上应该这样做
trait Locatable {
/**
* Scope a query to include the distance from the current model.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeNearby($query)
{
if(!$this->location) {
return $query;
}
return $query->whereHas('location',
function($query) {
$query->selectRaw('
//Your query for distance (Use the getTable() function wherever required...)
) AS distance
')
->having('distance', '<', 25);
}
);
}
}
回答您的问题。
以上是更好的方法。您应该使用
whereHas()
进行关系查询。with 查询将不起作用,因为 with 不连接表,它只执行 WHERE EXISTS () 查询,然后将其作为与返回模型的关系添加。想要了解更多……你能做的是……
(在你的控制器中任何你想写的地方......只是为了测试)
dd($user->with('locations')->toSql());
如果您遇到任何问题,请告诉我...:)