如何在Laravel QueryBuilder/MySQL Spatial包中按距离对查询结果进行排序?

rak*_*ete 5 php mysql laravel eloquent laravel-5

首先,我想向您展示当前的数据库结构.有三个表:

菜肴(身份证,姓名)

位置(id,name,coordinates(POINT))

dish_location(location_id,dish_id)

现在我想实现一个API,它获取用户的位置(纬度,经度)并返回按距离(km)排序的菜单列表.我已经有一种方法,需要两个纬度和两个经度,并给我一个距离.但我相信你可以告诉我一种方法,这是一种在MySQL查询中直接执行此操作的更高效的方法.

附加:我想在API中执行"加载更多"功能.所以我通过了已经收到的物品的数量,或者在这种情况下如何解决这个问题呢?

我将它用于MySQL Spatial包

Cy *_*nol 6

首先,让我们看一下如何使用基本查询构建器执行此操作。然后,我们将讨论如何使用 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);
}
Run Code Online (Sandbox Code Playgroud)

ST_Distance_Sphere()函数计算我们可以用来对结果进行排序的距离。Laravel 的paginate()方法使用page通过请求 URL 传递的参数为我们执行自动分页。阅读分页文档以获取更多信息。使用上面的函数,我们可以按如下方式获取分页结果集:

$point = new Point($latitude, $longitude); 
$sortedDishes = paginateDishesFromPoint($point, 15); 
Run Code Online (Sandbox Code Playgroud)

...我们正在使用的包中PointGrimzy\LaravelMysqlSpatial\Types\Point类在哪里,是每页的结果数。15

现在,让我们尝试使用 Eloquent 模型来做到这一点。我们将使用本地查询范围来封装创建执行排序的查询部分所需的逻辑:

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');
    }
}
Run Code Online (Sandbox Code Playgroud)

此实现使用模型上的元数据将表和字段名称添加到查询中,因此如果它们发生变化,我们不需要更新此方法。现在我们可以使用模型获取有序集:

$point = new Point($latitude, $longitude); 
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);
Run Code Online (Sandbox Code Playgroud)

$sortedDishes是 Laravel 的一个实例,LengthAwarePaginator它包装了一个Collection模型。如果我们将结果传递给视图,以下是在 Blade 模板中显示它们的方法:

$point = new Point($latitude, $longitude); 
$sortedDishes = paginateDishesFromPoint($point, 15); 
Run Code Online (Sandbox Code Playgroud)

如上所示,分页器提供了方便的方法,我们可以使用这些方法轻松地在分页结果之间移动。

或者,我们可以使用 AJAX 请求来加载结果。只要确保在请求数据的参数中传递当前页面+1即可page