I have 2 classes that I want to select from DB using 1 query in Symfony2 Doctrine.
The first entity is Calculation and the second is Polynomial. They have 1:1 relationship:
/**
* Acme\UserBundle\Entity\Calculation
*
* #ORM\Table(name="Calculation")
* #ORM\Entity(repositoryClass="AppBundle\Entity\CalculationRepository")
*/
class Calculation {
//...
/**
* #ORM\OneToOne(targetEntity="Polynomial")
* #ORM\JoinColumn(name="result_polynomial_id", referencedColumnName="id", nullable=false)
**/
private $resultPolynomial;
//...
}
I have a query, that returns me all Calculations of one user:
public function findByUser( $user ) {
return $this->getEntityManager()->createQuery(
'SELECT c
FROM AppBundle:User u
JOIN AppBundle:Polynomial p WITH u = p.user
JOIN AppBundle:Calculation c WITH p = c.resultPolynomial
WHERE u = :user
ORDER BY c.id'
)
->setParameter('user', $user)
->getResult();
}
And to the question... Is there a way to get resultPolynomials of the calculations in one query? If I use something like SELECT c, c.resultPolynomial I get error:
[Semantical Error] line 0, col 12 near 'resultPolynomial
': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
And if I use foreach cycle all calculations to get the their resultPolynomials there are many queries to the DB (1 for each calculation) and it is not good for performance if I have many calculations.
I making a guess because you did not post your User definition nor your Polynomial definition.
I think you can make your DQL this way:
SELECT c, p
FROM AppBundle:User u
JOIN u.polynomial p
JOIN p.calculation c
WHERE u = :user
ORDER BY c.id
I guess you already defined your relations in your model so you don't need to repeat it here.
Related
I have three tables that I need to join to extract a result set, and I am not sure if I should use createQueryBuilder, createNativeQuery, or some other approach.
My three tables are
Email (joined to member via field: member)
Member (joined to company via field: current_company)
Company
The Entities are properly annotated in the code. For example
In the Email Entity:
/**
* #ORM\ManyToOne(targetEntity="Member")
* #ORM\JoinColumn(name="member", referencedColumnName="id", nullable=true)
*/
protected $member;
In the Member Entity:
/**
* #ORM\ManyToOne(targetEntity="Company")
* #ORM\JoinColumn(name="current_company", referencedColumnName="id", nullable=true)
*/
protected $current_company;
And in the Company Entity:
/**
* #ORM\ManyToMany(targetEntity="Member", mappedBy="companies")
*/
protected $members;
What I need to do is extract a set of records from Email where the Members are all associated with one Company.
The following SQL in MySQL returns what I need, but I am new to Doctrine and do not know how best to translate this query into something Doctrine can use to extract the same results:
select e.* from email e
join member m on m.id = e.member
join company c on c.id = m.current_company
where m.current_company = '95f1b5a4-03c9-11e9-85b1-989096db2d5f';
Can anyone help, and which approach should be employed createQueryBuilder, createNativeQuery, etc?
Update, I was able to get it working using plain old SQL, but I am not sure if this is an acceptable workaround in the Doctrine world. Thoughts and suggestions from those with more experience would be greatly welcome!
$conn = $this->em->getConnection();
$sql = "select e.* from fitchek.email e join fitchek.member m on m.id = e.member join fitchek.company c on c.id = m.current_company where m.current_company = ?";
$stmt = $conn->prepare($sql);
$stmt->bindValue(1, $company_uuid);
$stmt->execute();
Each Disque (d) has an associated collection of Note (n).
My querybuilder is to get every Disque based on how many Note are associated.
Here's a peak on the classes
class Disque
{
...
/**
* #ORM\ManyToMany(targetEntity="Note", cascade={"persist"})
*/
private $notes;
...
}
class Note
{
...
/**
* #ORM\ManyToOne(targetEntity="NoteValeur", cascade={"persist"})
* #ORM\JoinColumn(nullable=true)
*/
private $noteValeur;
...
}
class NoteValeur
{
...
/**
* #Gedmo\Slug(fields={"titre"})
* #ORM\Column(name="slug", unique=true, length=32)
*/
private $slug;
}
I'm using Doctrine Querybuilder to fetch the results, using a count for setting the threshold.
<?php
...
class DisqueRepository extends \Doctrine\ORM\EntityRepository
{
public function getDisquesByNotesAndAllInfo($slug, $seuil, $page, $nbPerPage)
{
$q = $this->createQueryBuilder('d');
$q
->select('d as infosDisque')
->leftJoin('d.pochettes', 'p')
->addSelect('p')
->leftJoin('d.groupes','g')
->addSelect('g')
->leftJoin('d.labelDisque', 'l')
->addSelect('l')
->leftJoin('d.chroniques','c')
->addSelect('c')
->leftJoin('d.notes', 'n')
->addSelect('n')
->innerJoin('n.noteValeur','nv')
->addSelect('nv')
->groupBy('d')
->addSelect('COUNT(d) as NbNotes')
->where('nv.slug LIKE :slug')
->setParameter('slug', $slug)
->having($q->expr()->gte('NbNotes',':seuil'))
->setParameter('seuil', $seuil)
->setFirstResult( ($page-1) * $nbPerPage)
->setMaxResults($nbPerPage)
->orderBy('d.dateSortieDisque', 'desc')
;
return new Paginator($q, true);
}
}
The result set is perfectly fine. The problem is that when I change the $page value, the offset in the SQL is not increased. I'm getting the same range of results no matter what.
The Symfony profiler gives me the following executable query
SELECT DISTINCT id_44
FROM (
SELECT
COUNT(s0_.id) AS sclr_0, s0_.id AS id_1, s0_.titre AS titre_2, s0_.datesortiedisque AS datesortiedisque_3, s0_.nombreDisque AS nombreDisque_4, s0_.remarque AS remarque_5, s0_.tracklist AS tracklist_6, s0_.lineup AS lineup_7, s0_.slug AS slug_8, s0_.coderef AS coderef_9,
s1_.id AS id_10, s1_.alt AS alt_11, s1_.url AS url_12, s1_.coderef AS coderef_13, s1_.ordre AS ordre_14, s1_.fichier AS fichier_15,
s2_.id AS id_16, s2_.nom AS nom_17, s2_.site AS site_18, s2_.motto AS motto_19, s2_.popularite AS popularite_20, s2_.slug AS slug_21, s2_.coderef AS coderef_22,
s3_.id AS id_23, s3_.titre AS titre_24, s3_.slug AS slug_25, s3_.coderef AS coderef_26,
s4_.id AS id_27, s4_.texte AS texte_28, s4_.resume AS resume_29, s4_.dateSaisie AS dateSaisie_30, s4_.lectures AS lectures_31, s4_.slug AS slug_32, s4_.coderef AS coderef_33,
s5_.id AS id_34, s5_.dateSaisie AS dateSaisie_35, s5_.coderef AS coderef_36, s6_.id AS id_37,
s6_.titre AS titre_38, s6_.valeur AS valeur_39, s6_.image AS image_40, s6_.coderef AS coderef_41, s6_.octal AS octal_42, s6_.slug AS slug_43,
s0_.id AS id_44, s0_.titre AS titre_45, s0_.datesortiedisque AS datesortiedisque_46, s0_.nombreDisque AS nombreDisque_47, s0_.remarque AS remarque_48, s0_.tracklist AS tracklist_49, s0_.lineup AS lineup_50, s0_.slug AS slug_51, s0_.coderef AS coderef_52
FROM
se_disque s0_
LEFT JOIN disque_pochette d7_ ON s0_.id = d7_.disque_id
LEFT JOIN se_pochette s1_ ON s1_.id = d7_.pochette_id
LEFT JOIN disque_groupe d8_ ON s0_.id = d8_.disque_id
LEFT JOIN se_groupe s2_ ON s2_.id = d8_.groupe_id
LEFT JOIN se_labeldisque s3_ ON s0_.label_disque_id = s3_.id
LEFT JOIN disque_chronique d9_ ON s0_.id = d9_.disque_id
LEFT JOIN se_chronique s4_ ON s4_.id = d9_.chronique_id
LEFT JOIN disque_note d10_ ON s0_.id = d10_.disque_id
LEFT JOIN se_note s5_ ON s5_.id = d10_.note_id
INNER JOIN se_notevaleur s6_ ON s5_.note_valeur_id = s6_.id
WHERE
s6_.slug LIKE 'classique'
GROUP BY
s0_.id, s0_.titre, s0_.datesortiedisque, s0_.nombreDisque, s0_.remarque, s0_.tracklist, s0_.lineup, s0_.slug, s0_.coderef, s0_.label_disque_id, s0_.format_id, s0_.format_discographique_id
HAVING
sclr_0 >= 2
) dctrn_result
ORDER BY datesortiedisque_3 DESC LIMIT 10 OFFSET 10
I can't seem to change the offset in this query no matter what I pass to the $q->setFirstResult() method.
Does anybody have a clue ?
Well, guess what, I was using a debug value in my controller and I had forgotten about it :
$disques = $dr->getDisquesByNotesAndAllInfo($slug, $seuil, 2, $nbPerPage);
All works fine now that I changed back this messy "2" value.
Sorry about that. Thanks to both of you for your help !
Given this entity
class SystemRecord
{
/**
* #ORM\Id
* #ORM\Column(type="integer", name="ID")
* #ORM\GeneratedValue
* #var int
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Application\Entity\User")
* #ORM\JoinColumn(name="USER_SERIAL", referencedColumnName="SERIAL", nullable=false)
* #var User
*/
private $user;
/**
* #ORM\Column(type="utcdatetime", name="DATE_DATA_WAS_FETCHED", nullable=false)
* #var DateTimeInterface
*/
private $dateDataWasFetched;
}
...and this dql
$dql = "
select r
from Application\\Entity\\SystemRecord r
join Application\\Entity\\User u
where r.dateDataWasFetched = (
select max(r2.dateDataWasFetched)
from Application\\Entity\\SystemRecord r2
)
and u.serial = :serial
";
$query = $this->getEntityManager()->createQuery($dql);
$query->setParameter('serial', $user->getSerial());
$sql = $query->getSql();
... I'm hoping to get "the SystemRecords for the user with the specified serial, but only those with the most recent date out of any SystemRecord". In other words, procedurally, "find the most recent date of any SystemRecord for any user. Then find records for the specified user which occurred on that date."
If I were writing sql, I would write
select *
from SYSTEM_RECORDS r
join USER u
on r.USER_SERIAL = u.SERIAL
where DATE_DATA_WAS_FETCHED = (select max(DATE_DATA_WAS_FETCHED) from SYSTEM_RECORDS)
and u.SERIAL = ?
But, doctrine is giving me the following sql
SELECT ...fields from s0_ ...
FROM SYSTEM_RECORDS s0_
INNER
JOIN
USER u1_
ON (s0_.DATE_DATA_WAS_FETCHED = (SELECT max(s2_.DATE_DATA_WAS_FETCHED) AS dctrn__1
FROM SYSTEM_RECORDS s2_) AND u1_.SERIAL = ?)
Which isn't what I want. That gives me "SystemRecords for all users whose SystemRecords have the same date as the most recent SystemRecords for the user with the specified serial".
How do I formulate my query using dql?
If I understand you correctly you need to use a sub query like you did but I think you are missing the in expression. With QueryBuilder you would built the query to get your result like this (I always write my queries with QueryBuilder):
$qb->select(r)
->from('SystemRecord', 'r')
->join('r.user', 'u')
->where(
$qb->expr()->in(
'r.dateDataWasFetched',
"SELECT max(r2.dateDataWasFetched) FROM Application\\Entity\\SystemRecord r2"
)
)
->andWhere('u.serial' = :user_serial)
->setParameter('user_serial', $user->getSerial())
->getQuery()
->getResult();
This answer is based on this answer to similar question on stackoverflow.
EDIT:
If you really want the DQL then you can easily get it from your QueryBuilder instance after building the query using the getDQL method like this:
$dql = $qb->getQuery()->getDQL();
I was able to solve/avoid my problem by avoiding a join
$dql = "
select r
from Application\\Entity\\SystemRecord r
where r.dateDataWasFetched = (
select max(r2.dateDataWasFetched)
from Application\\Entity\\SystemRecord r2
)
and r.user = :user
";
$query = $this->getEntityManager()->createQuery($dql);
$query->setParameter('user', $user);
Resulting sql(correct)
SELECT ...fields from s0_ ...
FROM SYSTEM_RECORDS s0_
WHERE s0_.DATE_DATA_WAS_FETCHED = (SELECT max(s1_.DATE_DATA_WAS_FETCHED) AS dctrn__1
FROM SYSTEM_RECORDS s1_) AND s0_.USER_SERIAL = ?
The notable difference is that instead of specifying the id for the associated entity(via u.serial = :serial, I'm now specifying the entity itself(via r.user = :user). This allows me to omit the join, too. btw - The serial field is tagged with #ORM\Id in my User entity.
However, this is just avoiding the problem. I'm still perplexed by how doctrine interprets the query when a join is present.
Edit - real solution found
Thanks to Wilt, after using the query builder and then using the getDQL() method I found the missing detail. The working dql is
select r
from Application\Entity\SystemRecord r
join r.user u
where r.dateDataWasFetched = (
select max(r2.dateDataWasFetched)
from Application\\Entity\\SystemRecord r2
)
and u.serial = :serial
Note that the difference between the DQL in my original question, and this working solution is join Application\\Entity\\User u vs join r.user u, respectively.
Hey guys i have three table
1) expert_class
2) expert_location
3) expert
i want to fetch data form all three table i know query in mysql i.e
select * from expert_class class
join expert_location loc
on loc.expert_id = class.expert_id
join expert e
on class.expert_id = e.id
where e.is_delete=0
using this query i got all data whatever i want but the problem is i have to write this query using doctrine query bulider i tried like this
$classes = $this->qb
->add('select', 'exp_cls,exp_loc.id as location_id')
->from('Entity\expert_class','exp_cls')
->join('Entity\expert_location', 'exp_loc')
->join('Entity\expert', 'exp')
->where('exp_cls.expert_id = exp.id')
->AndWhere('exp_cls.expert_id = exp_loc.expert_id')
->AndWhere('exp.is_delete = 0')
->getQuery()
->getArrayResult();
when i try to run this query i got this Fatal error
Fatal error: Uncaught exception 'Doctrine\ORM\Query\QueryException' with message 'SELECT exp_cls,exp_loc.id as location_id FROM Entity\expert_class exp_cls INNER JOIN Entity\expert_location exp_loc INNER JOIN Entity\expert exp WHERE exp_cls.expert_id = exp.id AND exp_cls.expert_id = exp_loc.expert_id AND exp.is_delete = 0' in C:\xampp\htdocs\projectname\application\libraries\Doctrine\ORM\Query\QueryException.php:39 Stack trace: #0 C:\xampp\htdocs\projectname\application\libraries\Doctrine\ORM\Query\Parser.php(396): Doctrine\ORM\Query\QueryException::dqlError('SELECT exp_cls,...') #1 C:\xampp\htdocs\projectname\application\libraries\Doctrine\ORM\Query\Parser.php(2363): Doctrine\ORM\Query\Parser->syntaxError('Literal') #2 C:\xampp\htdocs\projectname\application\libraries\Doctrine\ORM\Query\Parser.php(2550): Doctrine\ORM\Query\Parser->Literal() #3 C:\xampp\htdocs\projectname\application\libraries\Doctrine\ORM\Query\Parser.php(2485): Doctrine\ORM\Query\Parser-> in C:\xampp\htdocs\projectname\application\libraries\Doctrine\ORM\Query\QueryException.php on line 44
i also looked up this link Doctrine query builder using inner join with conditions but its not helped
Have you defined relations in your entities? E.g.:
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="expert_location", mappedBy="locations")
*/
private $location;
If you did, then you have to use those connections in your join, e.g.:
->join('exp_cls.locations', 'exp_loc') // This joins the expert_location entity
You you didn't, then you should add an ON condition:
use Doctrine\ORM\Query\Expr\Join;
...
->join('Entity\expert_location', 'exp_loc', Join::ON, $qb->expr()->eq('exp_loc.expert_id', 'exp_cls.expert_id '))
I've got an SQL query that returns all the rows in one table (country) which have a related entry in another table (ducks) but I'm struggling to turn this into DQL. This is a standard one-many relationship as each country can have multiple ducks, I believe it is all set up correctly as I can return ducks within a country and return the country a duck is in using standard code.
The working query is:
SELECT c.* FROM country c
INNER JOIN ducks d
ON c.id = d.country_id
GROUP BY c.country
ORDER BY c.country ASC
I've tried converting this to:
SELECT c FROM WfukDuckBundle:Country c
INNER JOIN WfukDuckBundle:Ducks d
ON c.id = d.country_id
GROUP BY c.country
ORDER BY c.country ASC
which produces the following error:
[Semantical Error] line 0, col 79 near 'd ON': Error: Identification Variable
WfukDuckBundle:Ducks used in join path expression but was not defined before.
I'm quite new to Symfony/Doctrine so I suspect it's probably something obvious!
I'm using Symfony 2.0.11 with doctrine
Update:
I've Also tried:
SELECT c FROM WfukDuckBundle:Country c
INNER JOIN c.ducks d
ON c.id = d.country_id
GROUP BY c.country
ORDER BY c.country ASC
where 'ducks' is defined in the Country class as:
/**
* #ORM\OneToMany(targetEntity="Ducks", mappedBy="country")
*/
protected $ducks;
public function __construct()
{
$this->ducks = new ArrayCollection();
}
the definition for country in the ducks class is:
/**
* #ORM\ManyToOne(targetEntity="Country", inversedBy="ducks")
* #ORM\JoinColumn(name="country_id", referencedColumnName="id")
*/
private $country;
Do yourself a favour and use the query builder. Easier to read and update and reuse your queries
<?php
namespace Vendor\Prefix\Repository;
use Doctrine\ORM\EntityRepository;
class SomeRepository extends EntityRepository
{
public function countryDucks()
{
// $em is the entity manager
$qb = $em->createQueryBuilder();
$qb
->select('country', 'duck')
->from('WfukDuckBundle:Country', 'country')
->innerJoin('country.ducks', 'duck')
->groupBy('country.country')
->orderBy('country.country', 'ASC')
;
$query = $qb->getQuery();
// Potential Hydration Modes
// --------------------------------
// Doctrine\ORM\Query::HYDRATE_OBJECT
// Will give you an array of your object entities
// --------------------------------
// Doctrine\ORM\Query::HYDRATE_ARRAY
// Will give you an array mimicking
// your object graph
// --------------------------------
return $query->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
}
}