Symfony querybuilder subquery using sprintf() and alias - php

I'm trying to make a "complex" (for me) querybuilder. Here is the basic SQL request:
SELECT DISTINCT fif.idfichefrais, fif.idvisiteur, fif.date, fif.nbjustificatifs, fif.montantvalide, fif.datemodif, fif.idetat, montantTotalFraisForfait, montantTotalFraisHorsForfait, libelleFraisHorsForfait
FROM FicheFrais fif
INNER JOIN (SELECT lff.idfichefrais, SUM(frf.montantFraisForfait*lff.quantite) as montantTotalFraisForfait
FROM LigneFraisForfait AS lff
INNER JOIN FraisForfait AS frf ON frf.idfraisforfait = lff.idfraisforfait
GROUP BY lff.idfichefrais) a USING (idfichefrais)
INNER JOIN (SELECT e.idetat, e.libelleEtat
FROM Etat AS e) b USING (idetat)
LEFT JOIN (SELECT lfhf.idfichefrais, SUM(lfhf.montant) as montantTotalFraisHorsForfait, lfhf.libelle as libelleFraisHorsForfait
FROM LigneFraisHorsForfait AS lfhf
GROUP BY lfhf.idfichefrais) c USING (idfichefrais)
WHERE fif.idvisiteur = '?'
AND YEAR(fif.date) ='?'
ORDER BY MONTH(fif.date) ASC;
and here is my querybuilder that I'm trying to accomplish
public function findFicheFraisByIdVisiteurAndAnnee($idvisiteur, $annee): array
{
$qb = $this->getEntityManager()->createQueryBuilder();
$a = $this->getEntityManager()->createQueryBuilder();
$b = $this->getEntityManager()->createQueryBuilder();
$c = $this->getEntityManager()->createQueryBuilder();
$selectFraisForfait = $a->select(array('lff.idfichefrais, SUM(frf.montantFraisForfait*lff.quantite)'))
->from('App:Lignefraisforfait', 'lff')
->innerJoin('App:Fraisforfait', 'frf', Join::WITH, 'frf.idfraisforfait = lff.idfraisforfait')
->groupBy('lff.idfichefrais');
$selectEtat = $b->select(array('e.idetat, e.libelleEtat ad libelleFraisHorsForfait'))
->from('App:Etat', 'e');
$selectFraisHorsForfait = $c->select(array('lfhf.idfichefrais, SUM(lfhf.montant) as montantTotalFraisHorsForfait'))
->from('App:Lignefraishorsforfait','lfhf')
->groupBy('lfhf.idfichefrais');
return $FicheFraisByIdVisiteurAndAnnee = $qb
->select(array('fif.idfichefrais, identity(fif.idvisiteur) as idvisiteur, fif.date, fif.nbjustificatifs, fif.montantvalide, fif.datemodif, identity(fif.idetat) as idetat,montantTotalFraisForfait, montantTotalFraisHorsForfait, libelleFraisHorsForfait'))
->from('App:Fichefrais', 'fif')
->innerJoin('App:Lignefraisforfait', sprintf('($s)selectfraisforfait', $selectFraisForfait->getDQL()), Join::WITH, 'fif.idfichefrais = selectfraisforfait.idfichesfrais')
->innerJoin('App:Etat', sprintf('($s)selectetat', $selectEtat->getDQL()), Join::WITH, 'fif.idetat = selectetat.idetat')
->leftJoin('App:Lignefraishorsforfait', sprintf('($s)selectFraisHorsForfait', $selectFraisHorsForfait->getDQL()), Join::WITH, 'fif.idfichefrais = selectfraishorsforfait.idfichefrais')
->where('fif.idvisiteur = :idvisiteur')
->setParameter('idvisiteur' , $idvisiteur)
->andWhere('YEAR(fif.date) = :annee')
->setParameter('annee', $annee)
->groupBy('fif.idfichefrais')
->groupBy('lfhf.idfichefrais')
->orderBy('MONTH(fif.date)', 'ASC')
->setMaxResults(12)
->getQuery()
->getResult()
;
}

Related

Order by count of relational field

I have the following relations:
Member one-to-many Product
Product many-to-one Member
This is the query that I have that works:
$qb = $this->createQueryBuilder('m');
$qb
->where('m.Authority = :Authority')
->setParameter('Authority', Authority::CREATOR)
->innerJoin('m.Product', 'p')
->select('COUNT(p) AS HIDDEN item_count', 'm')
->groupBy('m')
->orderBy("item_count", "ASC");
However when I add additional parameters for product status
$qb = $this->createQueryBuilder('m');
$qb
->where('m.Authority = :Authority')
->setParameter('Authority', Authority::CREATOR)
->andWhere('p.Status = :Status')
->setParameter('Status', 1);
->innerJoin('m.Product', 'p')
->select('COUNT(p) AS HIDDEN item_count', 'm', 'p')
->groupBy('p')
->orderBy("item_count", "ASC");
The order by doesn't happen anymore. I dumped the value for item_count and it's alway 1. Has anyone encountered the same problem? The goal here is to get a list of Members with Products with status = 1 ordered by the number of Products.
Edit:
If I change groupBy('p') to groupBy('m'), it will only load one related Product for each Member
For anyone who struggled with the same problem, this is how I did it:
$qb = $this->createQueryBuilder('m');
$qb
->where('m.Authority = :Authority')
->setParameter('Authority', Authority::CREATOR)
->andWhere('p.Status = :Status')
->setParameter('Status', 1);
->select('p, m')
->addSelect('(SELECT COUNT(DISTINCT p1)
FROM Eccube\Entity\Member m1
INNER JOIN Eccube\Entity\Product p1
WHERE m1.Authority = ' . Authority::CREATOR . ' AND p1.Status = 1 AND p1.Creator = m.id
) AS hidden item_count')
->innerJoin('m.Product', 'p')
->groupBy('p')
->orderBy("item_count", "ASC");
It works but I don't understand why this is p1.Creator = m.id ? If I use m1.id it will just sum up the item count.
Edit: Added updated code after Jakumi's comment regarding the join. Even though both code works, the second one makes more sense.
$qb = $this->createQueryBuilder('m');
$qb
->where('m.Authority = :Authority')
->setParameter('Authority', Authority::CREATOR)
->andWhere('p.Status = :Status')
->setParameter('Status', 1);
->select('p, m')
->addSelect('(SELECT COUNT(DISTINCT p1)
FROM Eccube\Entity\Member m1
INNER JOIN m1.Product p1
WHERE m1.Authority = ' . Authority::CREATOR . ' AND p1.Status = 1 AND p1.Creator = m
) AS hidden item_count')
->innerJoin('m.Product', 'p')
->groupBy('p')
->orderBy("item_count", "ASC");

Doctrine - How do you use a subquery column (in the SELECT clause and having condition)

Hello i am trying to rewrite ZF query builder into doctrine ORM.
Here IS mine old Zend framework query.
$dependent = new Zend_Db_Expr('if(br.billing_rate = \'dependent\',(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id),0)');
$select = $this
->select()->setIntegrityCheck(false)
->from(array('brg' => 'childcare_billing_rule_group'), array())
->joinInner(array('br' => 'childcare_billing_rule'), 'br.id = brg.billing_rule_id', array('*', 'dependent' => $dependent))
->join(array('brp' => 'childcare_billing_rule_price'), 'br.id = brp.childcare_billing_rule_id', array('age_from', 'age_to', 'default_rate' => 'rate'))
->where('is_default != 1')
->where('br.billing_type in (\'' . implode('\',\'', array(self::BILLING_TYPE_PER_DAY, self::BILLING_TYPE_PER_HOUR, self::BILLING_TYPE_PER_UNLIMITED, self::BILLING_TYPE_PER_RESERVATION)) . '\')')
->where('brg.group_id IN (?)', $groupId)
->group('brp.id')
->having('dependent <= ?', $dependentLevel)
->order('dependent desc')
->order('brp.rate');
And here it is SQL row value
SELECT `br`.*,
if (br.billing_rate = 'dependent',
(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id),
0) AS `dependent`,
`brp`.`age_from`,
`brp`.`age_to`,
`brp`.`rate` AS `default_rate`
FROM `childcare_billing_rule_group` AS `brg`
INNER JOIN `childcare_billing_rule` AS `br` ON br.id = brg.billing_rule_id
INNER JOIN `childcare_billing_rule_price` AS `brp` ON br.id = brp.childcare_billing_rule_id
WHERE (is_default != 1)
AND (br.billing_type in ('Per Day','Per Hour','Unlimited','Per Reservation'))
AND (brg.group_id IN (103))
AND ((brp.age_from = 0 AND brp.age_to = 0) || 3.1666666666667
BETWEEN brp.age_from AND brp.age_to)
GROUP BY `brp`.`id` HAVING (dependent <= '0')
ORDER BY `dependentw` desc,
`brp`.`rate` ASC
I am not sure how to implement this sub query
new Zend_Db_Expr('if(br.billing_rate = \'dependent\',(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id),0)');
My code so far
$subQuery = $subQueryBuilder
->select('if (br.billing_rate = 'dependent',
(SELECT COUNT(*) FROM childcare_billing_rule_price AS p WHERE p.id < brp.id AND p.childcare_billing_rule_id = brp.childcare_billing_rule_id)')
->getSQL();
$queryBuilder->select($alias)
->addSelect($subQuery)
->innerJoin(ChildcareBillingRule::class, 'br', Join::WITH, 'br.id = childcare_billing_rule_group.childcareBillingRule')
->innerJoin(ChildcareBillingRulePrice::class, 'brp', Join::WITH, 'br.id = brp.childcareBillingRule')
->where("br.billingType in (:billingTypes)")
->andWhere( 'br.isDefault != :isDefault')
->andWhere( 'childcare_billing_rule_group.groupId in (:groupId)')
->setParameter('groupId', [103])
->setParameter('isDefault', 1)
->setParameter('billingTypes', ['Per Hour', 'Per Reservation', 'Per Day', 'Unlimited'])
->having('dependent <= ?', $dependentLevel')
->groupBy('brp.id')
->orderBy('brp.rate') ;
->getQuery()->getSingleResult();
so i am trying to add sub query with addSelect but not sure is this the right way? Any Help or idea is appreciated? Tnx
in short instead of ->getSQL() you should use ->getDQL().
Yours code will be like:
$dql = $this->createQueryBuilder()
->select('count(id)')
->from({Class::FQDN}, {ALIAS})
->getDQL();
$result = $this->createQueryBuilder()
->select({SECOND_ALIAS})
->from({SecondClass::FQDN}, {SECOND_ALIAS})
->where((new \Expr())->gt('{SECOND_ALIAS}.cnt', $dql))
->getQuery()
->getResult();

How can i resolve this PDOException for this update query? [duplicate]

Using Doctrine 2 I want to get some users that are contacts of another user. The table user contains the mapping between those users. The query in the function will return the following error:
Invalid parameter number: number of bound variables does not match number of tokens.
However to my best understanding $stris set to "b" and $ownerId is set to "2" and both are assigned by the setParameters function.
protected function getContactBySubstring($str, $ownerId) {
echo $str;
echo $ownerId;
$em = $this->getDoctrine()->getEntityManager();
$qb = $em->createQueryBuilder();
$qb->add('select', 'u')
->add('from', '\Paston\VerBundle\Entity\User u, \Paston\VerBundle\Entity\Contact c')
->add('where', "c.owner = ?1 AND c.contact = u.id AND u.username LIKE '?2'")
->add('orderBy', 'u.firstname ASC, u.lastname ASC')
->setParameters(array (1=> $ownerId, 2=> '%'.$str.'%'));
echo $qb->getDql();
$query = $qb->getQuery();
$users = $query->getResult();
foreach($users as $user)
echo $user->getUsername();
exit;
//return $contacts;
}
Don't surround any of the parameters in your query text with quotes!
->add('where', "c.owner = ?1 AND c.contact = u.id AND u.username LIKE '?2'")
should be
->add('where', "c.owner = ?1 AND c.contact = u.id AND u.username LIKE ?2")

How to implement subqueries in Symfony2 with Doctrine?

I need to count the number of items returned in the subquery. If I write the subquery how DQL - all good, but if I try to build a query via QueryBuilder - I get an error.
Subquery DQL:
$qb3 = $this->createQueryBuilder('c')
->select('COUNT(c.id)')
->where('c.id IN (SELECT cl.id FROM Acme\AppBundle\Entity\ClassC cl INNER JOIN Acme\AppBundle\Entity\ClassP p WHERE p.var1 = :var1 AND p.var2 = cl.id GROUP BY cl.id)')
->setParameter('var1', $var);
Subquery via QueryBuilder:
$qb = $this->createQueryBuilder('c');
$qb->select('COUNT(c.id)')
->where(
$qb->expr()->in(
'c.id',
$this->createQueryBuilder('cl')
->select('cl.id')
->innerJoin('Acme\AppBundle\Entity\ClassP', 'p')
->where('p.var1 = :var1')
->setParameter('var1', $var)
->andWhere('p.var2 = cl.id')
->groupBy('cl.id')
->getDQL()
)
);
Both versions return the same DQL.
Error:
screen
Try to move setParameter() to main level of query.
$qb = $this->createQueryBuilder('c');
$qb->select('COUNT(c.id)')
->where(
$qb->expr()->in(
'c.id',
$this->createQueryBuilder('cl')
->select('cl.id')
->innerJoin('Acme\AppBundle\Entity\ClassP', 'p')
->where('p.var1 = :var1')
->andWhere('p.var2 = cl.id')
->groupBy('cl.id')
->getDQL()
)
)
->setParameter('var1', $var);

How to build a Docterine Inner Join Query in Symfony

Im working on a Symfony project and I want to create Doctrine Query for this SQL.
USER table :
columns -
NICK_NAME
REVIEWS table:
columns -
USER_ID
, REVIEW,
CREATED_AT
Thank you
SELECT
`USER`.NICK_NAME,
REVIEWS.REVIEW,
REVIEWS.CREATED_AT
FROM
REVIEWS
INNER JOIN `USER` ON REVIEWS.USER_ID = `USER`.ID
WHERE
REVIEWS.MOVIE_ID = 625
GROUP BY
REVIEWS.USER_ID
I tried something like this
$q = Doctrine_Query::create()
->select("u.NICK_NAME,r.REVIEW,r.CREATED_AT")
->from('REVIEWS r')
->innerJoin('`USER` ON REVIEWS.USER_ID = `USER`.ID')
->where('REVIEWS.MOVIE_ID = 625')
->groupBy('REVIEWS.USER_ID');
and got this:
500 | Internal Server Error | Doctrine_Exception Couldn't find class `USER`
Without using complex DQL in Symfony you can use simple SQL.
Try this function in the controller where you want to run the DQL.
function getUserReviews($params) {
$query = "SELECT REVIEWS.REVIEW, `USER`.NICK_NAME, REVIEWS.CREATED_AT FROM REVIEWS INNER JOIN `USER` ON REVIEWS.USER_ID = `USER`.ID WHERE REVIEWS.MOVIE_ID ='".$params."'";
return Doctrine_Manager::getInstance()->getCurrentConnection()->fetchAssoc($query);
}
You Should Specify Entity name not table name in DQL like this YourBundleName:EntityName
Use it like this:
$q = Doctrine_Query::create()
->select("u.NICK_NAME,r.REVIEW,r.CREATED_AT")
->from('REVIEWS r')
->innerJoin('YourBundleName:EntityName ON REVIEWS.USER_ID = `USER`.ID')
->where('REVIEWS.MOVIE_ID = 625')
->groupBy('REVIEWS.USER_ID');
Alternative Solution if above solution doesn’t work:
$q = Doctrine_Query::create()
->select("u.NICK_NAME,r.REVIEW,r.CREATED_AT")
->from('REVIEWS r')
->innerJoin('YourBundleName:EntityName', 'USER_ID')
->where('REVIEWS.MOVIE_ID = 625')
->groupBy('REVIEWS.USER_ID');
If that doesn't work, use Inner join in following manner:
->InnerJoin('YourBundleName:Entity', '<alias>', Expr\Join::ON, $qb->expr()->eq('IDENTITY(<alias.<clummn_name>)', '<comapring_column>'))
inner Join from Doctrine2 Documentation:
Example -
$qb->innerJoin('u.Group', 'g', Expr\Join::WITH, qb->expr()->eq('u.status_id', '?1'))
$qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1')
$qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id')
innerJoin Method Prototype:
innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null);
Find More Here
Hope this will help you.

Categories