Primero, echemos un vistazo a cómo hacer esto con el generador de consultas básico. Luego, discutiremos cómo ejecutar esta consulta con 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);
}
El ST_Distance_Sphere()
La función calcula una distancia por la que podemos ordenar los resultados. paginate()
de Laravel El método realiza la paginación automática para nosotros usando la page
parámetro pasado a través de la URL de solicitud. Lea los documentos de paginación
para más información. Con la función anterior, podemos obtener un conjunto de resultados paginados de la siguiente manera:
$point = new Point($latitude, $longitude);
$sortedDishes = paginateDishesFromPoint($point, 15);
...donde Point
es el Grimzy\LaravelMysqlSpatial\Types\Point
clase de el paquete
estamos usando, y 15
es el número de resultados por página.
Ahora, intentemos hacer esto con modelos Eloquent. Usaremos un ámbito de consulta local para encapsular la lógica necesaria para crear la parte de la consulta que realiza el pedido:
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');
}
}
Esta implementación usa metadatos en los modelos para agregar la tabla y los nombres de los campos a la consulta, por lo que no necesitamos actualizar este método si cambian. Ahora podemos buscar el conjunto ordenado usando el modelo:
$point = new Point($latitude, $longitude);
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);
$sortedDishes
es una instancia de LengthAwarePaginator
de Laravel que envuelve una Collection
de los modelos Si pasamos los resultados a una vista, así es como se muestran en una plantilla Blade:
<ul>
@foreach($sortedDishes as $dish)
<li>{{ $dish->name }} is {{ $dish->distance }} meters away.</li>
@endforeach
</ul>
<a href="{{ $sortedDishes->nextPageUrl() }}">Load more...</a>
Como se muestra arriba, el paginador proporciona métodos convenientes que podemos usar para movernos fácilmente entre los resultados paginados.
Alternativamente, podríamos usar solicitudes AJAX para cargar los resultados. Solo asegúrese de pasar la página actual + 1 en la page
parámetro de los datos de la solicitud.