Doctrine2 LEFT JOIN with 2 conditions - mysql

I'm trying to find a 'Product' by ID, and to left join all it's 'Photo' on two conditions: the locale AND the active state.
Here's my QueryBuilder :
$queryBuilder = $this->createQueryBuilder('p')
->select('p, photos, photoTranslation')
->leftJoin('p.photos', 'photos')
->leftJoin('photos.translations', 'photoTranslation')
->where('p.id = :id')
->andWhere('(photoTranslation.locale = :locale OR photoTranslation.locale IS NULL)')
->andWhere('(photoTranslation.active = :active OR photoTranslation.active IS NULL)')
->setParameters(array(
'id' => $id
'locale' => $this->getLocale(),
'active' => true
));
It works fine when there are no Photos or when there are ACTIVE photos, but not when there's an inactive Photo because it doesn't match one of the two conditions.
If I use only one condition, for instance only the locale part, it works fine :
$queryBuilder = $this->createQueryBuilder('p')
->select('p, photos, photoTranslation')
->leftJoin('p.photos', 'photos')
->leftJoin('photos.translations', 'photoTranslation')
->where('p.id = :id')
->andWhere('(photoTranslation.locale = :locale OR photoTranslation.locale IS NULL)')
->setParameters(array(
'id' => $id
'locale' => $this->getLocale()
));
For now, I loop on theses results and unset all inactive Photos... but I'd like a clean way to do in the QueryBuilder.
I also tried to put the conditions on the LEFT JOIN clause :
->leftJoin('photo.translations', 'phototTranslation', Doctrine\ORM\Query\Expr\JOIN::WITH, 'photoTranslation.locale = :locale AND photoTranslation.active = :active')
But it always returns the Photo, even if it's inactive.

For this problem a solution may be:
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
$qb
->select('p', 'pp')
->from('Product', 'p')
->leftJoin('p.photos', 'pp')
->leftJoin('pp.translations', 'ppt', Doctrine\ORM\Query\Expr\Join::WITH, $qb->expr()->andX(
$qb->expr()->eq('ppt.locale', ':locale'),
$qb->expr()->eq('ppt.active', ':active')
))
->where('p.id', ':productId')
->setParameters(
array(
'productId', $productId,
'active', $active,
'locale', $locale
)
);
$query = $qb->getQuery();
return $query->getResult(); // or ->getSingleResult();
NOTE: this example is the way to do it in Symfony2 (2.3) entity repository

Related

yii1 CGridview with custom SQL

i need to use yii1 CGridView with the custom SQL.
i have followed this article
inside model I've created
public function searchUserStats()
{
$count = Yii::app()->db->createCommand()
->select('count(DISTINCT user_id)')
->from('casino_sg_sessions')
->queryScalar();
$sql = '
SELECT
SUM(bet) bet_sum,
SUM(win) win_sum,
SUM(bet_count) bet_count,
user_id,
userName,
modified
FROM
(SELECT
(SELECT IFNULL(SUM(tr1.amount), 0) FROM casino_sg_transactions tr1 WHERE t.id = tr1.session_id AND tr1.action = \'bet\' ) AS bet,
(SELECT IFNULL(SUM(tr2.amount), 0) FROM casino_sg_transactions tr2 WHERE t.id = tr2.session_id AND tr2.action = \'win\' ) AS win,
(SELECT count(0) FROM casino_sg_transactions tr3 WHERE t.id = tr3.session_id) AS bet_count,
t.user_id,
u.userName,
t.modified
FROM `casino_sg_sessions` t
INNER JOIN user u ON u.id = t.user_id
ORDER BY modified DESC
) user_stats
GROUP BY user_id
ORDER BY modified DESC
';
$command = Yii::app()->db->createCommand($sql);
$dataProvider = new CSqlDataProvider($command, array(
'totalItemCount'=>$count,
'pagination'=>array(
'pageSize'=>15,
),
));
return $dataProvider;
}
in the controller:
$model = new CasinoSgSessions('searchUserStats');
$this->render('index', array(
'model' => $model,
));
inside the view i call it this way:
$this->widget('application.widgets.grid.CGridView', array(
'dataProvider' => $model->searchUserStats(),
'filter' => $model,
'columns' => array(
[
'header' => 'User ID',
'name' => 'user_id',
'value' => '$data->user_id',
'htmlOptions' => array('width'=>'150px'),
],
.....
.....
i receive paginated grid without data.
spent huge amount of time and now gave up, what i did wrong ?
sql query itself works good.
PS: i cant use mysql views and create model for the view.
PPS: query will be a bit more complex, so the only way i see is using CGridView through the sql query.
in the template instead of
$data->user_id
i had to use
$data['user_id']
absolutely not expected behavior. Seems to me they didn't hear about SOLID

Zend Framework 2 SQL Join Issue

I am trying to use two left outer joins with Zend Framework 2's SQL classes but for some reason it is not returning one result but the other one is working fine. I've ran the actual SQL in MySQL Workbench and it returns just like I want but it is not doing it with Zend Framework. Here is my code:
Pure SQL:
SELECT groups.group_name, members.username, groups.id FROM groups
LEFT OUTER JOIN group_admins ON groups.id = group_admins.group_id
LEFT OUTER JOIN members ON group_admins.user_id = members.id
WHERE group_admins.user_id = " . parent::getUserId()['id']
This returns the result I wish, (which can be seen here: http://imgur.com/8ydmn4f)
Now, here is the Zend Framework 2 code I have in place:
$select_admins = new Select();
$select_admins->from(array(
'g' => 'groups',
))
->join(array(
'ga' => 'group_admins'
), 'g.id = ga.group_id')
->join(array(
'm' => 'members'
), 'ga.user_id = m.id', array('username'))
->where(array('ga.user_id' => parent::getUserId()['id']));
$query_group_admin = parent::$sql->getAdapter()->query(parent::$sql->buildSqlString($select_admins), Adapter::QUERY_MODE_EXECUTE);
$group_admins = array();
foreach ($query_group_admin as $group_admin) {
$group_admins[] = $group_admin;
}
// get the group members
$select = new Select();
$select->from(array(
'g' => 'group_members'
))
->join(array(
'm' => 'members'
), 'g.member_id = m.id')
->join(array(
'grp' => 'groups'
), 'g.group_id = grp.id')
->where(array(
'g.group_id' => $group_id
));
$query = parent::$sql->getAdapter()->query(parent::$sql->buildSqlString($select), Adapter::QUERY_MODE_EXECUTE);
$member_username = array();
foreach ($query as $member) {
$member_username[] = $member['username'];
}
// get the rest of the group info
$fetch = $this->gateway->select(array(
'id' => $group_id
));
$row = $fetch->current();
if (!$row) {
return false;
}
return array(
'admins' => implode(", ", $group_admins),
'members' => implode(", ", $member_username),
'info' => $row
);
Controller:
public function grouphomeAction()
{
$id = $this->params()->fromRoute('id', 0);
if (0 === $id) {
return $this->redirect()->toRoute('members/groups', array('action' => 'index'));
}
if (!$this->getGroupsService()->getGroupInformation($id)) {
return $this->redirect()->toRoute('members/groups', array('action' => 'index'));
}
return new ViewModel(array('group_info' => $this->getGroupsService()->getGroupInformation($id)));
}
However, this only shows the group name, group creator and group members but leave the group admins field empty.
Here is the print_r result of the array returned:
Array ( [admins] => [members] => jimmysole, fooboy [info] => ArrayObject Object ( [storage:ArrayObject:private] => Array ( [id] => 2 [group_name] => Tim's Group [group_creator] => timlinden [group_created_date] => 2017-01-16 17:39:56 ) ) )
If it helps, here is a screenshot as well of the page - http://imgur.com/xUQMaUu
Any help would be appreciated!
Thanks.
Basically your joins are INNER JOINS...I know....you must hate Zend right now :p . By default they are INNER JOINS so i assume that is what is wrong. SO try to specify the type of join and you should be fine. You can find more examples here: examples
$select12->from('foo')->join('zac', 'm = n', array('bar', 'baz'), Select::JOIN_OUTER);

WordPress Custom Query for Taxonomy

I have to edit somebody's code and I am not familiar with querying posts using the method they are. I have tried editing the query to various alternatives, however I am having no luck. I want to change the following query to select data from a custom taxonomy called 'job_cat' where the value equals a POST value. The code is below:
$querystr = "
SELECT DISTINCT wposts.*
FROM $wpdb->posts wposts
LEFT JOIN $wpdb->postmeta wpostmeta ON wposts.ID = wpostmeta.post_id
LEFT JOIN $wpdb->postmeta wpostmeta2 ON wposts.ID = wpostmeta2.post_id
LEFT JOIN $wpdb->term_relationships ON (wposts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN $wpdb->term_taxonomy ON ($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
WHERE wp_term_taxonomy.term_id = 94";
// If a job type is set and not equal to nothing or any jobs
if(isset($_POST['search-location']) && $_POST['search-location'] !== "" && $_POST['search-location'] !== "All") {
$querystr .= " AND (wpostmeta2.meta_key = 'region' AND wpostmeta2.meta_value = '".$_POST['search-location']."')";
}
$querystr .= " ORDER BY post_date DESC LIMIT 50";
//Execute
//echo $querystr;
$pageposts = $wpdb->get_results($wpdb->prepare($querystr, OBJECT));
Any help would be great!!
This code is extremely dangerous to use in a production WordPress site. You need to remove it and any other template file larded up with SQL queries like this.
Instead, use WP_Query which automatically sanitizes your SQL requests. It has a huge number of parameters you can set to query anything from WordPress. Here is an example using WP_Query to get posts by a given taxonomy, with tax_query.
<?php
// 1- Setup an argument to give to WP_Query
$taxonomy_args = array(
// 2- use the tax_query to examine a taxonomy
'tax_query' => array(
array(
'taxonomy' => 'job_cat',
'terms' => 'XYZ',
'field' => 'slug',
'include_children' => true,
'operator' => 'IN'
)
),
// 3- Make sure to define how many posts and what post-type they should be
'posts_per_page' => 50,
'post_type' => 'search-location',
);
$s = new WP_Query( $some_args );
if ( $s->have_posts() ) : $s->the_post();
// 4- Here you will manipulate the data you get back
endif;
?>

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

Zend Framework 2: LEFT JOIN issue

public function getInterests($userID) {
$result = $this->tableGateway->select(function (Select $select) use ($userID) {
$select->join('interests', 'users_interests.interest_id = interests.interest_id', array('*'), 'left');
$where = new Where();
$where->equalTo('user_id', $userID);
$select->where($where);
});
return $result;
}
Here is my method. It simply selects all records from users_interests with user_id = $userID and joins the 'interests' table. So far, so good, but when trying to display the fetched results, the fields from the joined table just do not exist. Here is the dump of the $result:
Zend\Db\ResultSet\ResultSet Object
(
[allowedReturnTypes:protected] => Array
(
[0] => arrayobject
[1] => array
)
[arrayObjectPrototype:protected] => Object\Model\UsersInterests Object
(
[settings_id] =>
[user_id] =>
[interest_id] =>
)
[returnType:protected] => arrayobject
[buffer:protected] =>
[count:protected] => 2
[dataSource:protected] => Zend\Db\Adapter\Driver\Pdo\Result Object
(
[statementMode:protected] => forward
[resource:protected] => PDOStatement Object
(
[queryString] => SELECT `users_interests`.*, `interests`.* FROM `users_interests` LEFT JOIN `interests` ON `users_interests`.`interest_id` = `interests`.`interest_id` WHERE `user_id` = :where1
)
[options:protected] =>
[currentComplete:protected] =>
[currentData:protected] =>
[position:protected] => -1
[generatedValue:protected] => 0
[rowCount:protected] => 2
)
[fieldCount:protected] => 6
[position:protected] =>
)
I badly need help on this because I am supposed to finish my project until Sunday. Thanks in advance.
You can use the following to apply left join. $select::JOIN_LEFT instead of 'left'.
public function getInterests($userID) {
$result = $this->tableGateway->select(function (Select $select) use ($userID) {
$select->join('interests', 'users_interests.interest_id = interests.interest_id', array('*'), $select::JOIN_LEFT);
$where = new Where();
$where->equalTo('user_id', $userID);
$select->where($where);
});
return $result;
}
It seems you have a problem in the WHERE clause of the join. This also shows in the error here:
[queryString] => SELECT `users_interests`.*, `interests`.* FROM `users_interests` LEFT JOIN .
`interests` ON `users_interests`.`interest_id` = `interests`.`interest_id`
WHERE `user_id` = :where1
Try this:
$select->from($this->table)
->join('interests', 'users_interests.interest_id = interests.interest_id',
array('*'), 'left');
$where = new Where();
$where->equalTo('user_id', $userID) ;
$select->where($where);
I can not follow your code completely, like here:
$this->tableGateway->select(function (Select $select) use ($userID) {
But, here is a very nice article on this. I think, you can simplify your code a little.
Have you iterated over the resultset? You can see there's two matching rows:
[rowCount:protected] => 2
You have a ResultSet object, but it will not load any of the rows until requested, they are "lazy loaded" when you iterate over the object.
You can force the resultset to get them all for you:
var_dump($resultSet->toArray()); // force load all rows
or iterate over the ResultSet:
foreach($resultset as $row) {
var_dump($row); // each row loaded on request
}
I have written about this before and maybe it will help you as well.
TableGateway with multiple FROM tables