Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment trier les résultats des requêtes par distance dans le package Laravel QueryBuilder / MySQL Spatial ?

Voyons d'abord comment procéder avec le générateur de requêtes de base. Ensuite, nous verrons comment exécuter cette requête avec les modèles 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);
}

Le ST_Distance_Sphere() La fonction calcule une distance sur laquelle nous pouvons trier les résultats. paginate() de Laravel la méthode effectue une pagination automatique pour nous en utilisant la page paramètre transmis via l'URL de la demande. Lisez la documentation sur la pagination pour plus d'informations. Avec la fonction ci-dessus, nous pouvons récupérer un jeu de résultats paginé comme suit :

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

...où Point est le Grimzy\LaravelMysqlSpatial\Types\Point classe de le paquet nous utilisons, et 15 est le nombre de résultats par page.

Maintenant, essayons de faire cela avec des modèles Eloquent. Nous utiliserons une portée de requête locale pour encapsuler la logique nécessaire pour créer la partie de la requête qui effectue le tri :

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

Cette implémentation utilise des métadonnées sur les modèles pour ajouter les noms de table et de champ à la requête afin que nous n'ayons pas besoin de mettre à jour cette méthode si elles changent. Nous pouvons maintenant récupérer l'ensemble commandé en utilisant le modèle :

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

$sortedDishes est une instance de LengthAwarePaginator de Laravel qui enveloppe une Collection des modèles. Si nous passons les résultats à une vue, voici comment les afficher dans un modèle Blade :

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

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

Comme indiqué ci-dessus, le paginateur fournit des des méthodes pratiques que nous pouvons utiliser pour nous déplacer facilement entre les résultats paginés.

Alternativement, nous pourrions utiliser des requêtes AJAX pour charger les résultats. Assurez-vous simplement de passer la page actuelle + 1 dans la page paramètre des données de la requête.