Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como classificar os resultados da consulta por distância no pacote Laravel QueryBuilder / MySQL Spatial?


Primeiro, vamos dar uma olhada em como fazer isso com o construtor de consultas básico. Em seguida, discutiremos como executar essa consulta com modelos 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);
}

A ST_Distance_Sphere() A função calcula uma distância pela qual podemos classificar os resultados. paginate() do Laravel método executa paginação automática para nós usando a page parâmetro passado pelo URL de solicitação. Leia os documentos de paginação Para maiores informações. Com a função acima, podemos buscar um conjunto de resultados paginado da seguinte forma:
$point = new Point($latitude, $longitude); 
$sortedDishes = paginateDishesFromPoint($point, 15); 

...onde Point é o Grimzy\LaravelMysqlSpatial\Types\Point class do do pacote estamos usando e 15 é o número de resultados por página.

Agora, vamos tentar fazer isso com modelos Eloquent. Usaremos um escopo de consulta local para encapsular a lógica necessária para criar a parte da consulta que executa a ordenação:
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');
    }
}

Essa implementação usa metadados nos modelos para adicionar os nomes de tabela e campo à consulta, portanto, não precisamos atualizar esse método se eles forem alterados. Agora podemos buscar o conjunto ordenado usando o modelo:
$point = new Point($latitude, $longitude); 
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);

$sortedDishes é uma instância do LengthAwarePaginator do Laravel que envolve uma Collection dos modelos. Se passarmos os resultados para uma visualização, veja como exibi-los em um modelo Blade:
<ul>
    @foreach($sortedDishes as $dish) 
        <li>{{ $dish->name }} is {{ $dish->distance }} meters away.</li>
    @endforeach
</ul>

<a href="{{ $sortedDishes->nextPageUrl() }}">Load more...</a>

Conforme mostrado acima, o paginador fornece métodos de conveniência que podemos usar para mover facilmente entre os resultados paginados.

Como alternativa, poderíamos usar solicitações AJAX para carregar os resultados. Apenas certifique-se de passar a página atual + 1 na page parâmetro dos dados da solicitação.