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.