Yii2 Left Join in query builder - yii2

I have two tables, the report_details and the name. In the report_details table, I have for and from which is an id that is related to the table name. What is the proper syntax in Yii2 to get both the for and from the column on the name table? This is my query so far...
$query = new yii\db\Query;
$query->select('report_details.reference_no, report_details.subject, report_details.doc_for, report_details.doc_from, report_details.doc_date, report_details.doc_name, report_details.drawer_id, report_details.user_id, name.name_id, name.position, name.fname, name.mname, name.lname')
->from('report_details')
->join('LEFT JOIN', 'name', 'report_details.doc_for = name.name_id')
->where(['report_details.reference_no' => $model->reference_no]);
$results = $query->all();

you could use ->leftJoin( )
$query = new yii\db\Query;
$query->select('report_details.reference_no, report_details.subject,
report_details.doc_for, report_details.doc_from,
report_details.doc_date, report_details.doc_name,
report_details.drawer_id, report_details.user_id,
name.name_id, name.position, name.fname, name.mname, name.lname')
->from('report_details')
->leftJoin( 'name', 'report_details.doc_for = name.name_id')
->where(['report_details.reference_no' => $model->reference_no]);
$results = $query->all();

Related

Yii2 select query where multiple values

When I want select where IN query, how to define multiple values in where clause?
Note on the ':k'=>1 and ':k'=>, how to use it for 2 values?
$query = Model::find()->where('id = :id and type = :k' ,[':id'=>$id, ':k'=>1,':k'=>27])->count();
Conditions can be also defined using array syntax:
$count = Model::find()
->where([
'AND',
['=', 'id', $id],
['IN', 'type', [1, 27]],
])
->count();
You could try using IN baded on an array
assuming
$myArray = array(1,27);
$query = Model::find()->where(['IN', 'id', $myArray])
->andWhere('id = :id', [':id' => $id])->count();
You can do it in this way:
$types = [1, 27];
$query = Model::find()
->where(['id' => $id])
->andWhere(['type' => $types])
->count();
Yii2 will convert your $types array to IN condition. SQL query will be:
SELECT COUNT(*) FROM <table> WHERE id = <id> AND type IN (1, 27);

How to transform raw SQL into Yii2 like find query

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

Symfony2 Doctrine error: Cannot count query that uses a HAVING clause. Use the output walkers for pagination

I am trying to get collections that are non-empty, i.e. have at least 1 object. Collection entity has OneToMany relationship with Object entity. I am using KNP paginator to paginate result. This is my function:
public function fetchAction(Request $request){
$em = $this->getDoctrine()->getManager();
$page = $request->get('page', 1);
$limit = 10;
$collections = $em->createQueryBuilder()
->select('c')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
->getQuery();
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit);
return $this->render('CollectionBundle:Collection:fetch.html.twig', [
'collections' => $collections
]);
}
Error
I keep getting following error
Cannot count query that uses a HAVING clause. Use the output walkers for pagination
Without 'Having' clause everything works fine, but I must get non-empty collections.
wrap-queries solved this problem
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit,array('wrap-queries'=>true));
You can implement the Manual counting, as described here in the doc.
As example, you can modify your code as follow:
$count = $em->createQueryBuilder()
->select('COUNT(c)')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
getSingleScalarResult();
$collections = $em->createQueryBuilder()
->select('c')
->add('from', 'CollectionBundle:Collection c LEFT JOIN c.object o')
->having('COUNT(o.id)>0')
->orderBy('c.date', 'DESC')
->getQuery();
$collections->setHint('knp_paginator.count', $count);
$collections = $this->get("knp_paginator")->paginate($collections, $page, $limit,array('distinct' => false));
return $this->render('CollectionBundle:Collection:fetch.html.twig', [
'collections' => $collections
]);
Hope this help
My solution is based on #Matteo's solution, since my query was a bit complicated I wanted to share my version also:
$qb = $this->createQueryBuilder('c');
$qb->select('count(c.id)')
->addSelect('COUNT(DISTINCT m.id) AS HIDDEN messageCount')
->addSelect('COUNT(DISTINCT f.id) AS HIDDEN fileCount')
->join('c.user', 'u')
->join('c.status', 's')
->join('c.company', 'comp')
->leftJoin('c.files', 'f')
->leftJoin('c.messages', 'm');
$this->_set_filters($filter, $qb);
$qb->groupBy('c.id');
$countQuery = $qb->getQuery();
/** wrap query with SELECT COUNT(*) FROM ($sql)
* I don't know what exactly does this block but
* I coppied it from Doctrine\ORM\Tools\Pagination\Paginator::getCountQuery()
*/
$platform = $this->getEntityManager()->getConnection()->getDatabasePlatform();
$rsm = new Query\ResultSetMapping();
$rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count');
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class);
$countQuery->setResultSetMapping($rsm);
return $countQuery->getSingleScalarResult(); //returns integer

WHERE IN with Doctrine 2 DBAL

How to use a WHERE IN clause with Doctrine DBAL ?
The following query doesn't work, it search the name "Bob","Elvis","Bill" (as a string) :
$users = $dbc->fetchAssoc("SELECT * FROM users WHERE name IN(:users_names)", array(
'users_names' => '"Bob","Elvis","Bill"'
));
I tried with an array, it's the same problem.
Try this :
$searchParameters = array("Bob","Elvis","Bill");
$users = "SELECT * FROM users WHERE name IN (?1)";
$q = $em->createQuery($users)
->setParameter(1, $searchParameters);
$result = $q->execute();

ZF2 how to rename name of field with join

Because my join includes a field named 'id' as well, I need to rename this field name during my sql so it won't override my id field name from the first selected tabel.
My query look likes as follow;
$select = new \Zend\Db\Sql\Select();
$select->from('websites');
$select->join(array('s' => 'websites_statistics'), 's.website_id = websites.id');
$select->where(array('websites.website' => $website));
$select->order('s.timestamp DESC')->limit(1);
$rowset = $this->tableGateway->selectWith($select);
$row = $rowset->current();
return $row;
So, 's' 'id' field should be renamed to something like 'stat_id'.
Thanks in advance!
Nick
$select = new \Zend\Db\Sql\Select();
$select->from('websites');
->join(array('s' => 'websites_statistics'), 's.website_id = websites.id',
array('stat_id' => 's.id')); // <-- here is the alias
->where(array('websites.website' => $website));
->order('s.timestamp DESC')
->limit(1);
$db = Zend_Db_Table::getDefaultAdapter();
$select = $db->select();
$select->from(array('p' => 'sub_categories'), array('subcategory_id'=>'p.subcategory_id','subname'=>'p.name'))
->join(array('pa' => 'categories'), 'pa.category_id = p.category_id', array('catname'=>'pa.name'));
$result = $this->getAdapter()->fetchAll($select);
*
And also we can use this method
use Zend\Db\Sql\Expression;
->join(array('s' => 'websites_statistics'), 's.website_id = websites.id',
array('stat_id' => new Expression('s.id'))); // <-- here is the alias
This is best method If you have to use Mysql 'AS' on zf2
example : array('Month' => new Expression('DATE_FORMAT(`salesInvoiceIssuedDate`, "%m")'))