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");
Related
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()
;
}
I am using Symfony v3.4 branch with Doctrine.
I am having trouble translating SQL query to Doctrine ORM query.
I have 3 tables.
Shop
Firm
User
User --> 1:1 --> Firm --> 1:1 --> Shop
(Symfony developer tool bar reports that associations in tables are made correctly).
I want to get Shop data that corresponds to cretain User in one query.
My SQL, that get a result:
SELECT *
FROM mp2_fos_user as u
LEFT JOIN mp2_firm AS f ON u.id = f.firmUserId
LEFT JOIN mp2_shop AS s ON f.id = s.shopFirmId
WHERE u.id = 1
My Doctrine ORM query
$query = $em->createQueryBuilder()
->select('u, f, s')
->from('App:User', 'u')
->leftJoin('u.userFirm WITH u.id = f.firmUserId', 'f')
->leftJoin('f.firmShop WITH f.id = s.shopFirmId', 's')
->where('u.id = :user_id')
->setParameter('user_id', $user_id)
->getQuery();
at the moment running the code results in an error
[Syntax Error] line 0, col 57: Error: Expected end of string, got 'u'
What would be best practice for my issue?
Help would be much appreciated,
Thank you!
UPDATE
tried:
$query = $em->createQueryBuilder()
->select('s.id')
->from('App:User', 'u')
->leftJoin('u.userFirm WITH f.firmUser = u', 'f')
->leftJoin('f.firmShop WITH s.shopFirm = f', 's')
->where('u.id = :user_id')
->setParameter('user_id', $user_id)
->getQuery();
got [Syntax Error] line 0, col 54: Error: Expected end of string, got 'f'
There is no need to use WITH clause if you have defined mapping in your entities, WITH clause is used when you want to join your entities with additional matching criteria
class User
{
/**
* #ORM\YourRelationShipNature(targetEntity="App\Entity\Firm", mappedBy="user")
*/
private $userFirm;
}
class Firm
{
/**
* #ORM\YourRelationShipNature(targetEntity="App\Entity\Shop", mappedBy="firm")
*/
private $firmShop;
}
class Shop
{
//.....
}
And then your could simple use properties to join your entites
$query = $em->createQueryBuilder()
->select('u, f, s')
->from('App:User', 'u')
->leftJoin('u.userFirm', 'f')
->leftJoin('f.firmShop', 's')
->where('u.id = :user_id')
->setParameter('user_id', $user_id)
->getQuery();
I was thinking of something more along the lines of this (assuming the entity names are correct):
$query = $em->createQueryBuilder()
->select('u, f, s')
->from('App:User', 'u')
->leftJoin('App:UserFirm f WITH f.firmUser = u')
->leftJoin('App:FirmShop s WITH s.shopFirm = f')
->where('u.id = :user_id')
->setParameter('user_id', $user_id)
->getQuery();
I want to join multiple tables, as in my picture:
Here is my code:
$this->db->select('
pt2.turl as `p_img`,
p.title as `p_title`,
p.text as `p_text`,
p.create as `p_date`,
pt3.turl as `c_img`,
u.name as `c_name`,
c.text as `c_text`,
c.create as `c_date`
');
$this->db->from('posts as p, users as u, photos as pt2, photos as pt3');
$this->db->join('comments as c', 'p.id=c.pid AND u.id=c.uid');
$this->db->join('posts as p2', 'p2.pid=pt2.id', 'rihgt');
$this->db->join('users as u2', 'u2.photoid=pt3.id', 'right');
$this->db->order_by('c.id', 'DESC');
$this->db->limit('7');
$qry = $this->db->get();
return $qry->result();
If I understand you correctly, this is kind of what you're looking for. I haven't tried it out so it may not be exact, but you shouldn't need to make 2 table associations (pt2 and pt3) for the same table in the join. Just Include them in the select and join on the unique ID's
The "Left" is a join that is centered around you're left table so everything hangs off of that. Since you are joining the users table before the photo table, you should be able to join on its columns.
Hope this helps. Let me know if I missed something. :)
$select = array(
pt2.turl as `p_img`,
p.title as `p_title`,
p.text as `p_text`,
p.create as `p_date`,
pt2.turl as `c_img`,
u.name as `c_name`,
c.text as `c_text`,
c.create as `c_date`
);
//Set tables to variables. Just makes it easier for me
$postsTable = "posts as p"; //This will be your left table.
$userTable = "Users as u";
$commentsTable = "comments as c";
$photosTable = "photos as pt2";
$this
->db
->select($select)
->from($postsTable)
->join($userTable, "p.uid = u.id", "left")
->join($commentsTable, "p.cid = c.id", "left")
->join($photosTable, "u.photoid = pt2.id", "left")
->get();
I solved this problem myself
It would be like this:
$select= array (
//'*',
'pt.turl p_img',
'p.title p_title',
'p.text p_text',
'p.create p_date',
'pt2.turl c_img',
'c.text c_text',
'u.name c_name',
'c.create c_date'
);
$from = array (
'posts p'
);
$qry = $this
->db
->select($select)
->from($from)
->join('comments c', 'c.pid=p.id')
->join('photos pt', 'pt.id=p.pid')
->join('users u', 'u.id=c.uid')
->join('photos pt2', 'u.photoid=pt2.id')
->order_by('c.create', 'DESC')
->limit('7')
->get();
return $qry->result();
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);
I'd like to select members who are not in specific service. I have 3 tables :
membre
service
membre_service (relation between membre and service)
I'm using doctrine 2 and in SQL my query is :
SELECT m.* FROM membre m WHERE m.`id` NOT IN (
SELECT ms.membre_id FROM membre_service ms WHERE ms.service_id != 29
)
In Doctrine, I do :
$qb = $this->_em->createQueryBuilder();
$qb2 = $qb;
$qb2->select('m.id')
->from('Custom\Entity\MembreService', 'ms')
->leftJoin('ms.membre', 'm')
->where('ms.id != ?1')
->setParameter(1, $service);
$qb = $this->_em->createQueryBuilder();
$qb->select('m')
->from('Custom\Entity\Membre', 'm')
->where($qb->expr()->notIn('m.id', $qb2->getDQL())
);
$query = $qb->getQuery();
//$query->useResultCache(true, 1200, __FUNCTION__);
return $query->getResult();
I got the following error :
Semantical Error] line 0, col 123 near 'm WHERE ms.id': Error: 'm' is already defined.
The same alias cannot be defined 2 times in the same query
$qb = $this->_em->createQueryBuilder();
$qb2 = $qb;
$qb2->select('m.id')
->from('Custom\Entity\MembreService', 'ms')
->leftJoin('ms.membre', 'm')
->where('ms.id != ?1');
$qb = $this->_em->createQueryBuilder();
$qb->select('mm')
->from('Custom\Entity\Membre', 'mm')
->where($qb->expr()->notIn('mm.id', $qb2->getDQL())
);
$qb->setParameter(1, $service);
$query = $qb->getQuery();
return $query->getResult();
Ideally you should use many-to-many relation for your entity, in this case your query is going to be much simpler.
Actually if you are using the Symfony2 repository class you could also do the following:
$in = $this->getEntityManager()->getRepository('Custom:MembreService')
->createQueryBuilder('ms')
->select('identity(ms.m)')
->where(ms.id != ?1);
$q = $this->createQueryBuilder('m')
->where($q->expr()->notIn('m.id', $in->getDQL()))
->setParameter(1, $service);
return $q->getQuery()->execute();
You can use (NOT) MEMBER OF:
<?php
$query = $em->createQuery('SELECT m.id FROM Custom\Entity\Membre WHERE :service NOT MEMBER OF m.services');
$query->setParameter('service', $service);
$ids = $query->getResult();
See the documentation for more examples.