Tengo una vista de cuadrícula en la que muestro la distancia entre la latitud y longitud del usuario registrado (establecida en su perfil) y la latitud y longitud de la línea de cuadrícula. He creado una clase auxiliar que me da una función para devolver dicha distancia. Mi vista de cuadrícula para la distancia es esta:

        [
            'format'=>'raw',
            'attribute'=>'distance (km)',
            'value'=> function ($data) {
                $latFrom = Yii::$app->user->identity->profile->city->latitude;
                $longFrom = Yii::$app->user->identity->profile->city->longitude;
                $latTo = $data->createdBy->profile->city->latitude;
                $longTo = $data->createdBy->profile->city->longitude;

                return GeoHelper::distance($latFrom, $longFrom, $latTo, $longTo);
            },
        ],

Estoy tratando de agregar ordenamiento por distancia en mi vista de cuadrícula, pero parece que no puedo encontrar cómo hacerlo. Intenté agregar una propiedad pública $ distance al modelo de búsqueda y establecerla como segura y luego agregar

    $dataProvider->sort->attributes['distance'] = [
        'asc' => ['distance' => SORT_ASC],
        'desc' => ['distance' => SORT_DESC],
    ];

Pero no suerte

¿Alguna idea?

1
Benoît 21 mar. 2018 a las 17:56

3 respuestas

La mejor respuesta

¡Lo resolví gracias a la ayuda de Yerke! En Post model, creé una nueva función de búsqueda

public static function findWithDistance()
{
    $latitudeFrom = Yii::$app->user->identity->profile->city->latitude;
    $longFrom = Yii::$app->user->identity->profile->city->longitude;

    return parent::find()
        ->addSelect([
            "post.*",
            "round(6371 * acos( cos( radians(postcity.latitude) ) * cos( radians({$latitudeFrom}) ) * cos( radians({$longFrom}) - radians(postcity.longitude)) + sin(radians(postcity.latitude)) * sin( radians({$latitudeFrom})))) as distance",
        ])
        ->leftJoin('user', 'post.created_by = user.id')
        ->leftJoin('profile', 'user.id = profile.user_id')
        ->leftJoin('city postcity', 'profile.location = postcity.id');
}

Que llamo en el modelo PostSearch si el usuario ha iniciado sesión:

public function search($params)
{
    $query = (!Yii::$app->user->isGuest && Yii::$app->user->identity->profile->city!=null) ? Post::findWithDistance() : Post::find();
...
1
Benoît 17 abr. 2018 a las 13:46

En su columna gridview, define el atributo como

'attribute'=>'distance (km)',

Pero en tu proveedor lo llamas 'distancia'

$dataProvider->sort->attributes['distance']

Intente configurar la columna en su vista de cuadrícula de esta manera:

'attribute'=>'distance',    

Si necesita que aparezca el 'km' en el encabezado, establezca la propiedad 'etiqueta' de la columna.

0
Patrick 22 mar. 2018 a las 12:47

Por supuesto, esto no es un problema, que podría resolverse tan fácilmente.

El valor que desea ordenar se calcula en PHP, pero DB no tiene idea de esos valores. Como dijo @Sergei Beregov, puede usar ArrayDataProvider, sí, de esa manera es posible. Sin embargo, dependiendo del recuento de usuarios, podría ser bastante costoso (si tiene muchos usuarios)

Si su DB admite funciones de Geo, sugeriría calcular los valores de distancia en la propia DB. Por ejemplo, en MySQL puede usar ST_Distance () para calcular la distancia entre dos puntos y mezclarla con las relaciones ActiveRecord en Yii2. He escrito un equipo experimental que utiliza funciones Geo en mi blog Relaciones espaciales en Yii2. Lo siento, está en ruso, pero espero que sea útil.

Siempre que pueda calcular la distancia en DB, puede ordenarla fácilmente en la cola de una manera normal como 'distance' => SORT_ASC

2
Yerke 25 mar. 2018 a las 17:20
49409653