Estoy tratando de obtener una lista de usuarios que están cerca del usuario autenticado y también devolver la distancia entre ellos usando Eloquent de Laravel. Conseguí que funcionara usando SQL sin procesar, pero ahora ya casi estoy haciendo que funcione correctamente usando principalmente elocuente.
Entonces, en el modelo de Usuario tengo este método:
public function getUsersAround($coordinates, $radius = 5) { return $this->whereHas('location', function ($query) use ($coordinates, $radius) { $query->distance($coordinates, $radius); })->paginate(10); }
Luego, en el modelo de ubicación, tengo el siguiente alcance:
public function scopeDistance($query, $coordinates, $radius) { return $query ->join('profiles', 'profiles.id', '=', 'locations.profile_id') ->having('distance', '<', $radius) ->selectRaw("profile_id, (6371 * ACOS(COS(RADIANS($coordinates->latitude)) * COS(RADIANS(latitude)) * COS(RADIANS(longitude) - RADIANS($coordinates->coordinates)) + SIN(RADIANS($coordinates->latitude)) * SIN(RADIANS(latitude)))) AS distance") ->orderBy('distance', 'asc'); }
Entonces, todos los usuarios en el radio se devuelven correctamente, pero también quiero agregar la distancia calculada a cada usuario devuelto.
Lo extraño es que si dd($query->distance($coordinates, $radius));
Veo a todos los usuarios + una nueva propiedad de distancia en cada objeto de usuario (con el valor correcto), pero si devuelvo la consulta y dd(auth()->user()->getUsersAround($coordinates, $radius))
dentro del controlador, la propiedad de distancia ya no está allí.
¿Hay alguna manera de devolver la distancia calculada dentro de la consulta de alcance y persistir en el objeto Usuarios al devolver la lista de usuarios?
¡Gracias!
Si desea incluir la distancia en los resultados, necesita cargar la relación de location
con el atributo de distance
. Dado que ya está seleccionando la distance
en el alcance de la consulta en el modelo de ubicación, puede lograr esto con bastante facilidad:
public function getUsersAround($coordinates, $radius = 5) { return $this->whereHas('location', function ($query) use ($coordinates, $radius) { $query->distance($coordinates, $radius); })->with([ 'location' => function ($query) use ($coordinates, $radius) { $query->select('locations.*')->distance($coordinates, $radius); } ])->paginate(10); }
Observe el with('location')
, que carga con entusiasmo el modelo de ubicación. Agregué un select('locations.*')
para que la relación de location
tenga sus propios atributos, y la distance($coordinates, $radius)
también seleccionará el profile_id
y los atributos de distance
.
Entonces, cuando llama a auth()->user()->getUsersAround($coordinates, $radius)
dentro de su controlador, puede acceder al atributo de distance
con $user->location->distance
:
$users = auth()->user()->getUsersAround($coordinates, $radius); foreach ($users as $user) { $distance = $user->location->distance; }
Eso es porque está usando get() dentro de un alcance que no es correcto. los ámbitos locales están destinados a definir conjuntos comunes de restricciones (como para filtrar datos). consulte la documentación sobre ámbitos locales para obtener más detalles.
Para agregar algo a un resultado de selección, puede usar addSelect .