Mysql
 sql >> Database >  >> RDS >> Mysql

Come ordinare i risultati delle query in base alla distanza nel pacchetto Laravel QueryBuilder / MySQL Spatial?

Per prima cosa, diamo un'occhiata a come farlo con il generatore di query di base. Quindi, discuteremo come eseguire questa query con i modelli 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);
}

Il ST_Distance_Sphere() La funzione calcola una distanza in base alla quale possiamo ordinare i risultati. paginate() di Laravel il metodo esegue l'impaginazione automatica per noi utilizzando la page parametro passato tramite l'URL della richiesta. Leggi i documenti sull'impaginazione per maggiori informazioni. Con la funzione sopra, possiamo recuperare un set di risultati impaginato come segue:

$point = new Point($latitude, $longitude); 
$sortedDishes = paginateDishesFromPoint($point, 15); 

...dove Point è il Grimzy\LaravelMysqlSpatial\Types\Point classe dal il pacchetto stiamo usando e 15 è il numero di risultati per pagina.

Ora, proviamo a farlo con i modelli Eloquent. Utilizzeremo un ambito della query locale per incapsulare la logica necessaria per creare la parte della query che esegue l'ordinamento:

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');
    }
}

Questa implementazione utilizza i metadati sui modelli per aggiungere i nomi di tabella e campo alla query, quindi non è necessario aggiornare questo metodo se cambiano. Ora possiamo recuperare il set ordinato utilizzando il modello:

$point = new Point($latitude, $longitude); 
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);

$sortedDishes è un'istanza di LengthAwarePaginator di Laravel che racchiude una Collection dei modelli. Se passiamo i risultati a una vista, ecco come visualizzarli in un modello Blade:

<ul>
    @foreach($sortedDishes as $dish) 
        <li>{{ $dish->name }} is {{ $dish->distance }} meters away.</li>
    @endforeach
</ul>

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

Come mostrato sopra, l'impaginatore fornisce metodi pratici che possiamo utilizzare per spostarci facilmente tra i risultati impaginati.

In alternativa, potremmo utilizzare le richieste AJAX per caricare i risultati. Assicurati solo di passare la pagina corrente + 1 nella page parametro dei dati della richiesta.