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.