sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo ordenar los resultados de la consulta por distancia en el paquete espacial Laravel QueryBuilder / MySQL?

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.