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

Hvordan sorterer man forespørgselsresultater efter afstand i Laravel QueryBuilder / MySQL Spatial-pakke?

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 + 1page parameter for anmodningsdataene.



  1. Returner alle ikke-beregnede kolonner fra en tabel i SQL Server

  2. Fejl ved forbindelse til databasen:(ved brug af klassen org.gjt.mm.mysql.Driver) Sætningen (1) har ingen åben markør

  3. Kan ikke oprette forbindelse til mysql-database med play-slick 1.0.1/slick 3.0:konfigurationsfejl

  4. Når et script udføres på SQLPlus, udskriver det en sekvens af tal i stedet for output