sql >> Databáze >  >> RDS >> Mysql

Jak seřadit výsledky dotazů podle vzdálenosti v balíčku Laravel QueryBuilder / MySQL Spatial?

Nejprve se podívejme, jak to udělat pomocí základního tvůrce dotazů. Poté probereme, jak provést tento dotaz pomocí modelů Eloquent:

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() funkce vypočítá vzdálenost, podle které můžeme výsledky seřadit. Laravelova paginate() metoda za nás provádí automatické stránkování pomocí page parametr předaný přes adresu URL požadavku. Přečtěte si dokumentaci stránkování Pro více informací. Pomocí výše uvedené funkce můžeme načíst stránkovanou sadu výsledků následovně:

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

...kde Point je Grimzy\LaravelMysqlSpatial\Types\Point třídy z balíčku používáme, a 15 je počet výsledků na stránku.

Nyní to zkusme udělat s modely Eloquent. Použijeme místní rozsah dotazu k zapouzdření logiky potřebné k vytvoření části dotazu, která provádí řazení:

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

Tato implementace používá metadata na modelech k přidání názvů tabulek a polí do dotazu, takže pokud se změní, nemusíme tuto metodu aktualizovat. Nyní můžeme získat objednanou sadu pomocí modelu:

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

$sortedDishes je instancí Laravelova LengthAwarePaginator který obaluje Collection modelů. Pokud výsledky předáme pohledu, zde je návod, jak je zobrazit v šabloně Blade:

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

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

Jak je uvedeno výše, stránkovač poskytuje pohodlné metody které můžeme použít ke snadnému pohybu mezi stránkovanými výsledky.

Případně bychom mohli k načtení výsledků použít požadavky AJAX. Nezapomeňte předat aktuální stránku + 1 na page parametr dat požadavku.



  1. Nejlepší noční můra starší databáze MySQL

  2. Převeďte výsledky dotazu na seznam oddělený čárkami v MariaDB

  3. Jak získat nejnovější 2 položky na kategorii v jednom výběru (s mysql)

  4. Jsou funkce PostgreSQL transakční?