Låt oss först ta en titt på hur du gör detta med den grundläggande frågebyggaren. Sedan kommer vi att diskutera hur man kör den här frågan 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 beräknar ett avstånd som vi kan sortera resultat efter. Laravels paginate()
metod utför automatisk sidnumrering åt oss med hjälp av page
parametern som skickas genom webbadressen för begäran. Läs pagineringsdokumenten
för mer information. Med funktionen ovan kan vi hämta en sidnumrerad resultatuppsättning enligt följande:
$point = new Point($latitude, $longitude);
$sortedDishes = paginateDishesFromPoint($point, 15);
...där Point
är Grimzy\LaravelMysqlSpatial\Types\Point
klass från paketet
vi använder och 15
är antalet resultat per sida.
Låt oss nu försöka göra detta med Eloquent-modeller. Vi använder ett lokalt frågeomfång för att kapsla in logiken som behövs för att skapa den del av frågan som utför beställningen:
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');
}
}
Den här implementeringen använder metadata på modellerna för att lägga till tabell- och fältnamnen i frågan så vi behöver inte uppdatera den här metoden om de ändras. Nu kan vi hämta den beställda uppsättningen med modellen:
$point = new Point($latitude, $longitude);
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);
$sortedDishes
är en instans av Laravels LengthAwarePaginator
som omsluter en Collection
av modellerna. Om vi skickar resultaten till en vy, så här visar du dem i en Blade-mall:
<ul>
@foreach($sortedDishes as $dish)
<li>{{ $dish->name }} is {{ $dish->distance }} meters away.</li>
@endforeach
</ul>
<a href="{{ $sortedDishes->nextPageUrl() }}">Load more...</a>
Som visas ovan tillhandahåller paginatorn bekvämlighetsmetoder som vi kan använda för att enkelt flytta mellan sökresultat.
Alternativt kan vi använda AJAX-förfrågningar för att ladda resultaten. Se bara till att passera aktuell sida + 1 på page
parametern för begäransdata.