Im trying to Right join in symfony. I tried as described here Doctrine 2 - Outer join query and here Symfony - Using Outer Joins with Doctrine ORM .
$query = $em->getRepository('AppBundle:raports')->createQueryBuilder('r')
->select('r')
->leftJoin('r.requestRaports rr WITH rr.formId = :formId', false)
->setParameter('formId', $requestId->getFormId())
->getQuery();
it gives
SELECT
r0_.id AS id_0,
r0_.adminComment AS adminComment_1,
r0_.addDate AS addDate_2,
r0_.submitDate AS submitDate_3,
r0_.statusId AS statusId_4,
r0_.userId AS userId_5,
r0_.requestId AS requestId_6,
r0_.requestRaports AS requestRaports_7
FROM
raports r0_
LEFT JOIN request_raports r1_ ON r0_.requestRaports = r1_.id
AND (r1_.formId = ?)
When i try
$query = $em->getRepository('AppBundle:raports')->createQueryBuilder('r')
->select('r')
->join('r.requestRaports rr WITH rr.formId = :formId', false)
->setParameter('formId', $requestId->getFormId())
->getQuery();
it looks like that
SELECT
r0_.id AS id_0,
r0_.adminComment AS adminComment_1,
r0_.addDate AS addDate_2,
r0_.submitDate AS submitDate_3,
r0_.statusId AS statusId_4,
r0_.userId AS userId_5,
r0_.requestId AS requestId_6,
r0_.requestRaports AS requestRaports_7
FROM
raports r0_
INNER JOIN request_raports r1_ ON r0_.requestRaports = r1_.id
AND (r1_.formId = ?)
But i want query like
SELECT * FROM raports r RIGHT JOIN request_raports rr ON
r.requestRaports = rr.id
How to make right join work in doctrine2?
You can ether use a LEFT JOIN and reversing your SQL like this
SELECT * FROM request_raports rr LEFT JOIN raports r ON r.requestRaports = rr.id
Or you can create your own "right join". If you look at leftJoin definition in doctrine, it's like :
leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
{
$parentAlias = substr($join, 0, strpos($join, '.'));
$rootAlias = $this->findRootAlias($alias, $parentAlias);
$join = new Expr\Join(
Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy
);
return $this->add('join', array($rootAlias => $join), true);
}
So, it may look like this :
$qb = $this->createQueryBuilder('b');
$rightJoin = new Expr\Join('RIGHT', 'r.requestRaports', 'rr', Expr\Join::WITH, 'rr.formId = :formId');
$qb
->select('r')
->add('join', ['r' => $rightJoin], true)
...
I have not tested this and don't know if its the best way to do it ...
Related
I clearly defined a WHERE statements in the main and sub-query. This is printed in the JPA log if I omit the subquery. However, the same where clause ist not applied, if I add the subquery. What is it about JPA Criteria query, that omits the WHERE clause (selection σ)?
// 1) MainQuery
// Create the FROM
Root<PubThread> rootPubThread = cq.from(PubThread.class);
// Create the JOIN fro the first select: join-chaining. You only need the return for ordering. e.g. cq.orderBy(cb.asc(categoryJoin.get(Pub_.title)));
Join<Pub, PubCategory> categoryJoin = rootPubThread.join(PubThread_.pups).join(Pub_.pubCategory);
// Create the WHERE
cq.where(criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)));
// Create the SELECT, at last
cq.select(rootPubThread).distinct(true);
// 2) Subquery
Subquery<PubThread> subquery = cq.subquery(PubThread.class);
Root<PubThread> rootPubThreadSub = subquery.from(PubThread.class);
subquery.where(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId));
Join<Pub, PubCategory> categoryJoinSub = rootPubThreadSub.join(PubThread_.pups).join(Pub_.pubCategory);
subquery.select(rootPubThreadSub);
Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
subquery.where(correlatePredicate);
cq.where(criteriaBuilder.exists(subquery));
Here is the output of this query:
select distinct
pubthread0_.id as id3_,
pubthread0_.dateCreated as dateCrea2_3_,
pubthread0_.dateModified as dateModi3_3_,
pubthread0_.name as name3_
from
pubthread pubthread0_
inner join
pub_pubthread pups1_ ON pubthread0_.id = pups1_.pubThreads_id
inner join
pub pub2_ ON pups1_.pups_id = pub2_.id
inner join
PubCategory pubcategor3_ ON pub2_.pubCategoryId = pubcategor3_.id
where
exists( select
pubthread4_.id
from
pubthread pubthread4_
inner join
pub_pubthread pups5_ ON pubthread4_.id = pups5_.pubThreads_id
inner join
pub pub6_ ON pups5_.pups_id = pub6_.id
inner join
PubCategory pubcategor7_ ON pub6_.pubCategoryId = pubcategor7_.id
where
pubthread4_.id=pubthread0_.id)
Got it. As soon as you defined a predicate, the where clause is rejected. That is for sure. Combine the where clause with a conjunction within a Predicate instance and append it in the where clause. Use an criteriaBuilder.and(..., ...). To semantically do the same thing.
So now, there are two conjunctional predicates: One for the subquery and one for the main query. Notice the obsolete single select(...) I ruled out from the code (it's commented).
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<PubThread> mainQuery = criteriaBuilder
.createQuery(PubThread.class);
// 1) MainQuery
// Create the FROM
Root<PubThread> rootPubThread = mainQuery.from(PubThread.class);
// Create the JOIN from the first select: join-chaining. You only need the return for ordering. e.g. cq.orderBy(cb.asc(categoryJoin.get(Pub_.title)));
Join<Pub, PubCategory> categoryJoin = rootPubThread.join(PubThread_.pups).join(Pub_.pubCategory);
// Create the WHERE
mainQuery.where(criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)));
// Create the SELECT, at last
mainQuery.select(rootPubThread).distinct(true);
// 2) Subquery
Subquery<PubThread> subquery = mainQuery.subquery(PubThread.class);
Root<PubThread> rootPubThreadSub = subquery.from(PubThread.class);
//subquery.where(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId));
Join<Pub, PubCategory> categoryJoinSub = rootPubThreadSub.join(PubThread_.pups).join(Pub_.pubCategory);
subquery.select(rootPubThreadSub);
//Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Predicate correlatePredicate = criteriaBuilder.and(
criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread),
criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)
);
subquery.where(correlatePredicate);
//Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Predicate mainPredicate = criteriaBuilder.and(
criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)),
criteriaBuilder.exists(subquery)
);
//cq.where(criteriaBuilder.exists(subquery));
mainQuery.where(mainPredicate);
TypedQuery<PubThread> typedQuery = em.createQuery(mainQuery);
List<PubThread> otherPubThreads = typedQuery.getResultList();
return otherPubThreads;
The result is something like this
where
pubthread4_.id=pubthread0_.id
and pubthread0_.id=1
New to Symfony2 and Doctrine2, i have a function in my entity repository to search entities after form submission. Input is array $get that contain form fields like $get['name'] = 'aname'.
My problem is that when i request with an id, or an id and a name, it's ok by with only a name, all my entities are matched because the query that has been build have no where clause.
Here is my code :
public function search(array $get, $flag = False){
/* Indexed column (used for fast and accurate table cardinality) */
$alias = 'd';
/* DB table to use */
$tableObjectName = 'mysiteMyBundle:DB';
$qb = $this->getEntityManager()
->getRepository($tableObjectName)
->createQueryBuilder($alias)
->select($alias.'.id');
$arr = array();
//Simple array, will grow after problem solved
$numericFields = array(
'id');
$textFields = array(
'name');
while($el = current($get)) {
$field = key($get);
if ( $field == '' or $field == Null or $el == '' or $el == Null ) {
next($get);
}
if ( in_array($field,$numericFields) ){
if ( is_numeric($el) ){
$arr[] = $qb->expr()->eq($alias.".".$field, $el);
}
} else {
if ( in_array($field,$textFields) ) {
$arr[] = $qb->expr()->like($alias.".".$field, $qb->expr()->literal('%'.$el.'%') );
}
}
next($get);
}
if(count($arr) > 0) $qb->andWhere(new Expr\Orx($arr));
else unset($arr);
$query = $qb->getQuery();
if($flag)
return $query;
else
return $query->getResult();
}
The query generated with only a name (ex "myname") input is :
SELECT d0_.id AS id0 FROM DB d0_
It should be:
SELECT d0_.id AS id0 FROM DB d0_ WHERE d0_.name LIKE '%myname%'
What's wrong with my code ?
Thanks !
I don't know if it's related, but do not use "OR" or "AND" operators, because they have a different meaning that the classic "&&" or "||". cf http://php.net/manual/en/language.operators.logical.php
So, first, replace "AND" by "&&", and "OR" by "||".
you should use the setParameter method
$query->where('id = :id')->setParameter('id', $id);
I am using CodeIgniter2 + Doctrine2, and have the following query:
$query = $this->doctrine->em->createQuery("
SELECT u
FROM ORM\Dynasties2\Characters u
WHERE u.fathersId = $key
AND u.deathDate IS NULL
AND u.isRuler = '0'
AND u.isFemale = '0'
AND u.useAI = '1'
AND u.bornDate <= $of_age
");
$sons_of_age = $query -> getResult();
And I only want to get ONE result, assuming there are any hits.
I've looked at Doctrine documentation about using ->LIMIT(1) but I have tried putting this into my query in various places, and only get errors.
Codeigniter has some functions builtin to do $query->row() but this does not seem to work - I wager because of the Doctrine integration.
Thanks!
You're looking for method Query::setMaxResults($number); Then you can use Query:getSingleResult();, but method Query:getSingleResult(); throws error if there's no record.
$query = $this->doctrine->em->createQuery("
SELECT u
FROM ORM\Dynasties2\Characters u
WHERE u.fathersId = $key
AND u.deathDate IS NULL
AND u.isRuler = '0'
AND u.isFemale = '0'
AND u.useAI = '1'
AND u.bornDate <= $of_age
");
$query->setMaxResults(1);
try {
$sons_of_age = $query->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
$sons_of_age = null;
}
$q = $this->_em->createQuery("SELECT s FROM app\models\Quest s
LEFT JOIN s.que c
WHERE s.type = '$sub'
AND c.id = '$id'");
Given a query like the one above, how would I retrieve the number of results?
Alternatively one can look at what Doctrine Paginator class does to a Query object to get a count (this aproach is most probably an overkill though, but it answers your question):
public function count()
{
if ($this->count === null) {
/* #var $countQuery Query */
$countQuery = $this->cloneQuery($this->query);
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
}
if ($this->useOutputWalker($countQuery)) {
$platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win
$rsm = new ResultSetMapping();
$rsm->addScalarResult($platform->getSQLResultCasing('dctrn_count'), 'count');
$countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'Doctrine\ORM\Tools\Pagination\CountOutputWalker');
$countQuery->setResultSetMapping($rsm);
} else {
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
}
$countQuery->setFirstResult(null)->setMaxResults(null);
try {
$data = $countQuery->getScalarResult();
$data = array_map('current', $data);
$this->count = array_sum($data);
} catch(NoResultException $e) {
$this->count = 0;
}
}
return $this->count;
}
You can either perform a count query beforehand:
$count = $em->createQuery('SELECT count(s) FROM app\models\Quest s
LEFT JOIN s.que c
WHERE s.type=:type
AND c.id=:id)
->setParameter('type', $sub);
->setParameter('id', $id);
->getSingleScalarResult();
Or you can just execute your query and get the size of the results array:
$quests = $q->getResult();
$count = count($quests);
Use the first method if you need the count so that you can make a decision before actually retrieving the objects.
what can i do if the parameter has no value?
my query:
$query = $this->_em->createQueryBuilder()
->select('u')
->from('Users', 'u')
->where('u.id = ?1')
->andWhere('u.status= ?2')
->setParameter(1, $userid)
->setParameter(2, $status)
->getQuery();
return $query->getResult();
if theres no $status, then it doesnt display anything.
i tried putting a condition before the query to check if its null but what value can i set $status iif theres no status set
The query builder is exactly there for building conditional queries. You could do:
$qb = $this->_em->createQueryBuilder();
$query = $qb->select('u')
->from('Users', 'u')
->where('u.id = ?1')
->setParameter(1, $userid);
if ($status) {
$qb->andWhere('u.status = ?2')
->setParameter(2, $status);
}
return $qb->getQuery()->getResult();
On a side note, it is best practice to use named placeholders e. g. like this:
$qb->andWhere('u.status = :status')
->setParameter('status', $status);
You could write:
->andWhere('(u.status= ?2 or ?2 is null)')