Как преобразовать необработанный SQL в Yii2, например поисковый запрос

Я не могу преобразовать необработанный SQL-запрос в метод, подобный Yii2. Я хотел бы реализовать представление сетки из моего RAW sql с фильтрацией и сортировкой. Я использую ActiveDataProvider с методом в ModelSearch как Yii по умолчанию.

Я пытался использовать Model::findBySql, но он не позволяет мне фильтровать или сортировать результаты в представлении сетки. Я не хочу использовать SQLDataProvider, потому что в моих запросах есть отношения.

Я вижу, что изменение Model::FindBySql($sql) на Model::find позволяет мне сортировать и фильтровать, но результаты не такие, как ожидалось. Мне нужно преобразовать этот SQL, чтобы использовать метод Model::Find()

Мой sql, который я изо всех сил пытаюсь изменить,

$sql = 'SELECT A.*, (6371 * acos(cos(radians("'.$mapSearch->gps_lat.'")) * cos(radians(gps_lat))*cos(radians(gps_long)-radians("'.$mapSearch->gps_long.'"))+sin(radians("'.$mapSearch->gps_lat.'"))*sin(radians(gps_lat)))) AS distance FROM address A JOIN contest_has_address CA On A.id = CA.address_id JOIN contest C On C.id = CA.contest_id JOIN contest_has_date CD On C.id = CD.contest_id JOIN date D On D.id = CD.date_id WHERE main = 1 AND C.status = 1 AND D.start_time > "'.$today.'" HAVING distance < "'.$mapSearch->distance.'" ORDER BY distance ASC';

мой контроллер:

if($mapSearch->save(false)) {
                $lat = $mapSearch->gps_lat;
                $long = $mapSearch->gps_long;
                $sql = 'SELECT A.*, (6371 *     acos(cos(radians("'.$mapSearch->gps_lat.'")) *      cos(radians(gps_lat))*cos(radians(gps_long)-    radians("'.$mapSearch->gps_long.'"))+sin(radians("'.$mapSearch->gps_lat.'")    )*sin(radians(gps_lat)))) AS distance FROM address A JOIN     contest_has_address CA On A.id = CA.address_id JOIN contest C On C.id =     CA.contest_id JOIN contest_has_date CD On C.id = CD.contest_id JOIN date D     On D.id = CD.date_id WHERE main = 1 AND C.status = 1 AND D.start_time >     "'.$today.'" HAVING distance < "'.$mapSearch->distance.'" ORDER BY distance     ASC';
                $models =     Address::findBySql($sql)->all(); 
                $count =     Yii::$app->db->createCommand($sql)->queryScalar();
                $dataProvider =     $searchModel->searchMapAddress(Yii::$app->request->queryParams, $sql);


 return $this->render('map', [
                    'sql'=>$sql,
                    'searchModel'=>$searchModel,
                    'models'=>$models,
                    'dataProvider'=>$dataProvider,
                    'mapSearch'=>$mapSearch,
                    'lat'=>$mapSearch->gps_lat,
                    'long'=>$mapSearch->gps_long,
                ]);

Моя модель

$query = Address::findBySql($sql);
    $query->joinWith(['contest']);

    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]); 

и просмотр:

echo GridView::widget([
                    'dataProvider' => $dataProvider,
                    'filterModel' => $searchModel,
                    'layout'=> '{items}',

person luk    schedule 31.03.2019    source источник


Ответы (1)


Предполагая, что ваш необработанный SQL-запрос работает правильно, вы можете использовать ActiveRecord или Конструктор запросов, чтобы создать свой запрос .

Для использования функций MYSQL внутри запроса необходимо использовать \yii\db\Expresion, и при построении запроса вы должны использовать ->createCommand()->rawSQL в конце запроса, заменяя его на ->one() или ->all(), и повторять запрос, чтобы увидеть, какой запрос RAW SQL построен, и сравнить его с исходным запросом.

Вы можете использовать следующий запрос:

$query=Address::find()->alias('A')
->select([new Expression('A.*, (6371 * acos(cos(radians("' . $mapSearch->gps_lat . '")) * cos(radians(gps_lat))*cos(radians(gps_long) -radians("' . $mapSearch->gps_long . '")) + sin(radians("' . $mapSearch->gps_lat . '"))*sin(radians(gps_lat)))) AS distance')])
->join('left join', '{{content_has_address}} CA', 'A.id = CA.address_id')
->join('left join', '{{contest}} C', 'C.id = CA.contest_id')
->join('left join', '{{contest_has_date}} CD', 'C.id = CD.contest_id')
->join('left join', '{{date}} D', 'D.id = CD.date_id')
->where(
    ['AND',
        ['=', 'main', 1],
        ['=', 'C.status', 1],
        ['>', 'D.start_time', $today]
    ]
)
->having(['<', 'distance', $mapSearch->distance])
->orderBy('distance asc');
$dataProvider = new ActiveDataProvider([
    'query' => $query,
]); 
person Muhammad Omer Aslam    schedule 31.03.2019
comment
acos(cos(radians(.... не используйте сервер sql в качестве калькулятора. Рассчитайте в своем php-коде и передайте результат только запросу - person Marcin Orlowski; 31.03.2019
comment
@MarcinOrlowski спасибо за совет, сделаю это, еще не рассматривал производительность - person luk; 31.03.2019
comment
@MarcinOrlowski вопрос был о преобразовании запроса, а не об его оптимизации, я ничего не добавлял - person Muhammad Omer Aslam; 01.04.2019
comment
@MuhammadOmerAslam: я думал, что публикую эту заметку под постом OP, а не под вашим ответом. Видимо нет - мой плохой, извините. - person Marcin Orlowski; 01.04.2019
comment
@MarcinOrlowski, нормально, брат, бывает :) - person Muhammad Omer Aslam; 01.04.2019