sql >> Database >  >> RDS >> Mysql

Hoe zoekresultaten op afstand sorteren in Laravel QueryBuilder / MySQL Spatial-pakket?

Laten we eerst eens kijken hoe we dit kunnen doen met de basisquerybuilder. Vervolgens bespreken we hoe u deze query kunt uitvoeren met Eloquent-modellen:

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

De ST_Distance_Sphere() functie berekent een afstand waarop we resultaten kunnen sorteren. Laravel's paginate() methode voert automatische paginering voor ons uit met behulp van de page parameter doorgegeven via de aanvraag-URL. Lees de pagination docs voor meer informatie. Met de bovenstaande functie kunnen we als volgt een gepagineerde resultatenset ophalen:

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

...waar Point is de Grimzy\LaravelMysqlSpatial\Types\Point class van het pakket we gebruiken, en 15 is het aantal resultaten per pagina.

Laten we dit nu proberen met Eloquent-modellen. We gebruiken een lokaal zoekbereik om de logica in te kapselen die nodig is om het deel van de query te maken dat de bestelling uitvoert:

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

Deze implementatie gebruikt metadata op de modellen om de tabel- en veldnamen aan de query toe te voegen, dus we hoeven deze methode niet bij te werken als ze veranderen. Nu kunnen we de bestelde set ophalen met het model:

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

$sortedDishes is een instantie van Laravel's LengthAwarePaginator die een Collection omhult van de modellen. Als we de resultaten doorgeven aan een weergave, kunt u ze als volgt weergeven in een Blade-sjabloon:

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

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

Zoals hierboven weergegeven, biedt de paginator gemaksmethoden die we kunnen gebruiken om gemakkelijk tussen paginaresultaten te schakelen.

Als alternatief kunnen we AJAX-verzoeken gebruiken om de resultaten te laden. Zorg ervoor dat u de huidige pagina + 1 doorgeeft op de page parameter van de verzoekgegevens.



  1. MySQL-databasemap herstellen vanaf een herstelde harde schijf

  2. PI() Voorbeelden in SQL Server

  3. Hoe IntelliJ verbinden met lokale MySQL?

  4. MySQL-opgeslagen procedure veroorzaakte `Opdrachten niet gesynchroniseerd`