首先,让我们看一下如何使用基本查询生成器来执行此操作。然后,我们将讨论如何使用 Eloquent 模型执行此查询:
function paginateDishesFromPoint(Point $point, $pageSize)
{
$distanceField = "ST_Distance_Sphere(locations.coordinates, "
. "ST_GeomFromText('{$point->toWKT()}') AS distance";
return DB::table('dishes')
->select('dishes.*', DB::raw($distanceField))
->join('dish_locations', 'dish_locations.dish_id', '=', 'dishes.id')
->join('locations', 'locations.id', '=', 'dish_locations.location_id')
->orderBy('distance')
->paginate($pageSize);
}
ST_Distance_Sphere() 函数计算我们可以对结果进行排序的距离。 Laravel 的 paginate() 方法使用通过请求 URL 传递的 page 参数为我们执行自动分页。阅读pagination docs 了解更多信息。使用上面的函数,我们可以获取一个分页的结果集,如下所示:
$point = new Point($latitude, $longitude);
$sortedDishes = paginateDishesFromPoint($point, 15);
...其中Point 是我们正在使用的the package 中的Grimzy\LaravelMysqlSpatial\Types\Point 类,15 是每页的结果数。
现在,让我们尝试用 Eloquent 模型来做这件事。我们将使用local query scope 来封装创建执行排序的查询部分所需的逻辑:
class Dish extends Model
{
...
public function locations()
{
return $this->belongsToMany(App\Location::class);
}
public function scopeOrderByDistanceFrom($query, Point $point)
{
$relation = $this->locations();
$locationsTable = $relation->getRelated()->getTable();
$distanceField = "ST_Distance_Sphere($locationsTable.coordinates, "
. "ST_GeomFromText('{$point->toWKT()}') AS distance";
return $query
->select($this->getTable() . '.*', DB::raw($distanceField))
->join(
$relation->getTable(),
$relation->getQualifiedForeignKeyName(),
'=',
$relation->getQualifiedParentKeyName()
)
->join(
$locationsTable,
$relation->getRelated()->getQualifiedKeyName(),
'=',
$relation->getQualifiedRelatedKeyName()
)
->orderBy('distance');
}
}
此实现使用模型上的元数据将表和字段名称添加到查询中,因此如果它们发生更改,我们无需更新此方法。现在我们可以使用模型获取有序集:
$point = new Point($latitude, $longitude);
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);
$sortedDishes 是 Laravel 的 LengthAwarePaginator 的一个实例,它包装了模型的 Collection。如果我们将结果传递给视图,以下是如何在 Blade 模板中显示它们:
<ul>
@foreach($sortedDishes as $dish)
<li>{{ $dish->name }} is {{ $dish->distance }} meters away.</li>
@endforeach
</ul>
<a href="{{ $sortedDishes->nextPageUrl() }}">Load more...</a>
如上所示,分页器提供了convenience methods,我们可以使用它在分页结果之间轻松移动。
或者,我们可以使用 AJAX 请求来加载结果。只要确保在请求数据的page参数中传递当前页面+1即可。