Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wie sortiere ich Abfrageergebnisse nach Entfernung im Laravel QueryBuilder / MySQL Spatial-Paket?

Lassen Sie uns zunächst einen Blick darauf werfen, wie Sie dies mit dem einfachen Abfrage-Generator tun. Dann besprechen wir, wie diese Abfrage mit Eloquent-Modellen ausgeführt wird:

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);
}

Die ST_Distance_Sphere() Funktion berechnet eine Entfernung, nach der wir die Ergebnisse sortieren können. Laravels paginate() -Methode führt für uns mithilfe der page eine automatische Paginierung durch -Parameter, der über die Anforderungs-URL übergeben wird. Lesen Sie die Paginierungsdokumentation für mehr Informationen. Mit der obigen Funktion können wir eine paginierte Ergebnismenge wie folgt abrufen:

$point = new Point($latitude, $longitude); 
$sortedDishes = paginateDishesFromPoint($point, 15); 

...wo Point ist der Grimzy\LaravelMysqlSpatial\Types\Point Klasse aus dem Paket wir verwenden, und 15 ist die Anzahl der Ergebnisse pro Seite.

Versuchen wir dies nun mit Eloquent-Modellen. Wir verwenden einen lokalen Abfragebereich um die Logik zu kapseln, die benötigt wird, um den Teil der Abfrage zu erstellen, der die Reihenfolge ausführt:

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');
    }
}

Diese Implementierung verwendet Metadaten in den Modellen, um die Tabellen- und Feldnamen zur Abfrage hinzuzufügen, sodass wir diese Methode nicht aktualisieren müssen, wenn sie sich ändern. Jetzt können wir den bestellten Satz mit dem Modell abrufen:

$point = new Point($latitude, $longitude); 
$sortedDishes = Dish::orderByDistanceFrom($point)->paginate($pageSize);

$sortedDishes ist eine Instanz von Laravels LengthAwarePaginator die eine Collection umschließt der Modelle. Wenn wir die Ergebnisse an eine Ansicht übergeben, können Sie sie wie folgt in einer Blade-Vorlage anzeigen:

<ul>
    @foreach($sortedDishes as $dish) 
        <li>{{ $dish->name }} is {{ $dish->distance }} meters away.</li>
    @endforeach
</ul>

<a href="{{ $sortedDishes->nextPageUrl() }}">Load more...</a>

Wie oben gezeigt, bietet der Paginator bequeme Methoden die wir verwenden können, um einfach zwischen Seitenergebnissen zu wechseln.

Alternativ könnten wir AJAX-Anfragen verwenden, um die Ergebnisse zu laden. Stellen Sie einfach sicher, dass Sie die aktuelle Seite + 1 übergeben auf der page Parameter der Anfragedaten.