sql >> Databasteknik >  >> RDS >> Mysql

Hur sorterar man frågeresultat efter avstånd i Laravel QueryBuilder / MySQL Spatial-paket?

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 + 1page parametern för begäransdata.



  1. Hur genererar man hela DDL för ett Oracle-schema (skriptbart)?

  2. Gå med SQL Server-drivrutinen kan inte ansluta, inloggningen misslyckades

  3. Eliminera dubbletter av kolumner i MySQL left join-fråga

  4. Hur man lägger till en logotyp till en formulärrubrik i Microsoft Access