Lad os først se på, hvordan du gør dette med den grundlæggende forespørgselsbygger. Derefter vil vi diskutere, hvordan man udfører denne forespørgsel med Eloquent-modeller:
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);
}
ST_Distance_Sphere()
funktion beregner en afstand, som vi kan sortere resultater efter. Laravels paginate()
metoden udfører automatisk paginering for os ved hjælp af page
parameter sendt gennem anmodningens URL. Læs sideinddelingsdokumenterne
for mere information. Med funktionen ovenfor kan vi hente et pagineret resultatsæt som følger:
$point = new Point($latitude, $longitude);
$sortedDishes = paginateDishesFromPoint($point, 15);
...hvor Point
er Grimzy\LaravelMysqlSpatial\Types\Point
klasse fra pakken
vi bruger, og 15
er antallet af resultater pr. side.
Lad os nu prøve at gøre dette med Eloquent-modeller. Vi bruger et lokalt forespørgselsomfang for at indkapsle den logik, der er nødvendig for at oprette den del af forespørgslen, der udfører bestillingen:
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');
}
}
Denne implementering bruger metadata på modellerne til at tilføje tabel- og feltnavne til forespørgslen, så vi behøver ikke at opdatere denne metode, hvis de ændrer sig. Nu kan vi hente det bestilte sæt ved hjælp af modellen:
$point = new Point($latitude, $longitude);
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);
$sortedDishes
er en forekomst af Laravels LengthAwarePaginator
som ombryder en Collection
af modellerne. Hvis vi videregiver resultaterne til en visning, kan du se dem her i en Bladeskabelon:
<ul>
@foreach($sortedDishes as $dish)
<li>{{ $dish->name }} is {{ $dish->distance }} meters away.</li>
@endforeach
</ul>
<a href="{{ $sortedDishes->nextPageUrl() }}">Load more...</a>
Som vist ovenfor giver paginatoren bekvemmelighedsmetoder som vi nemt kan bruge til at flytte mellem sideresultater.
Alternativt kunne vi bruge AJAX-anmodninger til at indlæse resultaterne. Bare sørg for at bestå den aktuelle side + 1 på page
parameter for anmodningsdataene.