问题
I can't convert raw SQL query in to Yii2 like method. I'd like to implement grid view from my RAW sql with filtering and sorting. I'm using ActiveDataProvider with method in the ModelSearch as Yii default way.
I did try to use Model::findBySql but it is not letting me filter or sort my results in the grid view. I don't want to useSQLDataProvider because I have relations in my queries.
I see that changing Model::FindBySql($sql) to Model::find is letting me sort and filter but the results are not as expected. I have to transform this SQL to use Model::Find() method
My sql I struggle to change is
$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';
my Controller:
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,
]);
My Model
$query = Address::findBySql($sql);
$query->joinWith(['contest']);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
and view:
echo GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'layout'=> '{items}',
回答1:
Assuming that your raw SQL query is working correctly you can use ActiveRecord or Query Builder to create your query.
For using MYSQL functions inside the query you must use \yii\db\Expresion, and while building the query you should use ->createCommand()->rawSQL
at the end of the query replacing with ->one()
or ->all()
, and echo the query to see what the RAW SQL query is built and compare it with the original query.
You can use the following query:
$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,
]);
来源:https://stackoverflow.com/questions/55443342/how-to-transform-raw-sql-into-yii2-like-find-query