I have a query builder with the following select:
$starterRepository = $this->getDoctrine()
->getManager()
->getRepository('CatalogBundle:Starter');
$query = $starterRepository->createQueryBuilder('s')
->where('s.active = 1')
->orderBy('s.voltage, s.power, s.serial');
It selects the table "starters", but inside Starter.php I have an association "references" like this:
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="StarterReference", inversedBy="starters")
* #ORM\JoinTable(name="starters_references")
*/
protected $references;
So inside my query results I have both "starters" and "starters_references" tables. 1 starter has many starter references. Now the problem is that I need to select not all of the starter references, but only the ones which has some value in column named "ref_usage".
So I need to write where clause in my query, I am trying this:
->where('reference.ref_usage = 1')
But this way I get only one "starter" item with all the references it has included. And I need all the starter items, but with only the references with ref_usage 1.
Any ideas?
Here is the full files and functions I am using.
Controller function:
http://pastebin.com/0tTEcQbn
Entity "Starter.php":
http://pastebin.com/BFLpKtec
Entity "StarterReference.php":
http://pastebin.com/Kr9pEMEW
EDIT:
This is the query I get if I use:
->where('reference.ref_usage = 1')
SELECT COUNT(*) AS dctrn_count FROM (SELECT DISTINCT id0 FROM (SELECT s0_.id AS id0, s0_.serial AS serial1, s0_.voltage AS voltage2, s0_.power AS power3, s0_.rotation AS rotation4, s0_.teeth AS teeth5, s0_.module AS module6, s0_.b_terminal AS b_terminal7, s0_.comment AS comment8, s0_.commenten AS commenten9, s0_.commentru AS commentru10, s0_.commentpl AS commentpl11, s0_.commentde AS commentde12, s0_.commentes AS commentes13, s0_.type AS type14, s0_.adaptation AS adaptation15, s0_.alternative_product_1 AS alternative_product_116, s0_.alternative_product_2 AS alternative_product_217, s0_.alternative_product_3 AS alternative_product_318, s0_.alternative_product_4 AS alternative_product_419, s0_.active AS active20 FROM starters s0_ INNER JOIN starters_references s2_ ON s0_.id = s2_.starter_id INNER JOIN starter_reference s1_ ON s1_.id = s2_.starterreference_id WHERE s0_.active = 1 AND s1_.ref_usage = 1 ORDER BY s0_.voltage ASC, s0_.power ASC, s0_.serial ASC) dctrn_result) dctrn_table
As you can see it adds ref_usage = 1 to where clause. But the problem is that I don't need it here, I only need to check ref_usage when I inner join my references.
You should join the references in your query and then add the where clause.
$query = $starterRepository->createQueryBuilder('s')
->join('s.references', 'reference')
->where('s.active = 1')
->andwhere('reference.ref_usage = 1')
->orderBy('s.voltage, s.power, s.serial');
Related
I have native sql query with left join when have on with or condition, how to represent it in query builder ?
$query = " SELECT te.id
FROM task_executions AS te
INNER JOIN tasks AS t ON t.id = te.task_id
LEFT JOIN cost_objects AS co ON co.id = t.cost_object_id
LEFT JOIN cost_object_managers AS com ON com.cost_object_id = co.id OR com.cost_object_id = co.parent_id
and I need represent it in query builder. But in User entity I have ManyToMany relation, without separate table and when I try left join WITH condition this is not same what I need. I need change relation for ON
LEFT JOIN cost_object_managers AS com ON com.cost_object_id = co.id OR com.cost_object_id = co.parent_id
User entity
class User
{
...
/**
* #ORM\ManyToMany(targetEntity="CostObject", mappedBy="users")
*/
private $costObjects;
}
CostObject entity
class CostObject
{
/**
* #var CostObject
*
* #ORM\ManyToOne(targetEntity="CostObject", inversedBy="children")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
* })
*/
private $parent;
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="User", inversedBy="costObjects")
* #ORM\JoinTable(name="cost_object_managers",
* joinColumns={#ORM\JoinColumn(name="cost_object_id", referencedColumnName="id", onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")}
* )
*/
private $users;
and my query builder without condition
$qb->select('te')
->from('AppBundle:TaskExecution', 'te')
->innerJoin('te.task', 't')
->leftJoin('t.costObject', 'co')
->leftJoin('co.users', 'com')
this is $query->getSQL()
SELECT some_name FROM task_executions t0_ INNER JOIN tasks t1_ ON t0_.task_id = t1_.id LEFT JOIN cost_objects c2_ ON t1_.cost_object_id = c2_.id LEFT JOIN cost_object_managers c4_ ON c2_.id = c4_.cost_object_id LEFT JOIN users u3_ ON u3_.id = c4_.user_id ORDER BY t0_.execution_start DESC
In this example I see ON relation condition LEFT JOIN users u3_ ON u3_.id = c4_.user_id. And need change it like in native sql
Now I have
$qb->select('te')
->from('AppBundle:TaskExecution', 'te')
->innerJoin('te.task', 't')
->leftJoin('t.costObject', 'co')
->leftJoin(
'co.users',
'com',
Join::ON,
$qb->expr()->orX(
'co = com.costObjects',
'co.parent = com.costObjects'
)
)
but got error
[Syntax Error] line 0, col 112: Error: Expected end of string, got 'ON'
if I used WITH condition, in my sql represent I still have relation by id, I don't need that
->leftJoin(
'co.users',
'com',
Join::WITH,
$qb->expr()->orX(
'co MEMBER OF com.costObjects',
'co.parent MEMBER OF com.costObjects'
)
)
LEFT JOIN users u3_ ON u3_.id = c4_.user_id AND (EXISTS (SELECT 1 FROM cost_object_managers c5_ INNER JOIN cost_objects c6_ ON c5_.cost_object_id = c6_.id WHERE c5_.user_id = u3_.id AND c6_.id IN (c2_.id)) OR EXISTS (SELECT 1 FROM cost_object_managers c5_ INNER JOIN cost_objects c6_ ON c5_.cost_object_id = c6_.id WHERE c5_.user_id = u3_.id AND c6_.id IN (c2_.parent_id)))
I mean users u3_ ON u3_.id = c4_.user_id AND but in native query we have only LEFT JOIN cost_object_managers AS com ON com.cost_object_id = co.id OR com.cost_object_id = co.parent_id
How it's reproduce in Query Builder with ON condition type?
If you have the query and it works for you, you don't need to do all the work to transform it to DQL or the QueryBuilder programmatic syntax. You can just use Doctrine's Native Query and then - if needed - map the result to your object. Just create a custom repository and in it a new method that roughly looks like this:
public function findTaskExecutionBy...()
{
$query = $this->entityManager->createNativeQuery('SELECT te.id FROM ...');
return $query->getSingleScalarResult(); // If it's just one id you expect
}
You can also use $query->getResult() if you expect multiple id's to be returned. Or use the ResultSetMapping if you want the whole Task-object:
$rsm = new ResultSetMappingBuilder($this->entityManager);
$rsm->addRootEntityFromClassMetadata('App\Entity\Task', 'te');
$query = $this->entityManager->createNativeQuery(
'SELECT te.* FROM ...',
You can also check the Doctrine documentation for a more detailed explanation and some more examples: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html
$rsm
);
I'm new in Symfony and I have 2 entities (not me who created them):
1st entity: test1 (id,test2_id)
2nd entity: test2 (id,label)
I want to create the query that select from test where test2.label = 1.
$Websites = $this->_em
->createQuery("
SELECT w
FROM \Bundle\Entity\test1 t1
JOIN t1.test2 t2
WHERE t2.label=1")
->getResult();
But I got an error:
Bundle\Entity\test1 has no association named test2
Is there the solution or another way to make it work.
Try this:
$Websites = $this->_em
->createQuery("
SELECT w
FROM \Bundle\Entity\test1 t1
JOIN \Bundle\Entity\test2 t2
WHERE t2.id = t1.test2_id AND t2.label=1")
->getResult();
You can also use createQueryBuilder method
Using createQueryBuilder you can do with the following
$labelValue = 1;
/* Get the EntityManager Resource */
$em = $this->getDoctrine->getManager();
$em->getRepository('AppBundle:test1')
->createQueryBuilder('t1')
->select('t1.w') /*Here you can select respective table columns */
->innerJoin('t1.test2_id', 't2')
->where('t2.label = :label') /* if label value coming from external value else ->where('t2.label = 1') and omit next line */
->setParameter('label',$labelValue) /*In case if your label value coming for external value */
->getQuery()
->getResult();
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.
I have a query in a repository like :
$rsm = new ResultSetMapping;
$rsm->addEntityResult('\My\ProjectBundle\Entity\News', 't');
$rsm->addFieldResult('t', 'id', 'id');
$rsm->addMetaResult('t', 'account_id', 'account_id');
$qb = $this->_em->createNativeQuery(
'SELECT t.*
FROM news as t
LEFT JOIN
LEFT JOIN
WHERE
CONDITIONS CONDITIONS
',
$rsm
);
return $qb->getResult();
I simplified the above query which is used to retrieve the news that meet specific conditions.
I need to add a count() function to this query.
I have an other ManyToOne entity-relationship between Comment and News.
How to modify the query to get the comments number a given news has ?
I'm trying to add a left join to comment and add Count() in the select but I always get errors. How could I resolve this problem ?
Raw SQL with Doctrine is easier like this :
$em = $this->getDoctrine()->getManager()->getConnection();
$query = "
SELECT t.*
FROM news as t
LEFT JOIN
LEFT JOIN
WHERE
CONDITIONS CONDITIONS
";
$stmt = $em->prepare($query);
$stmt->execute();
$result = $stmt->fetchAll();
I try to get this mysql query to work with Yii model but i can't.
SELECT COUNT( qhc.countries_id) AS counter, q.question, co.name
FROM questions AS q , countries as co, questions_has_countries AS qhc
WHERE qhc.questions_id = q.id
AND co.id = qhc.countries_id
GROUP BY question
HAVING counter = 2
So far i have this, but somehow thou it seems ok, it doesnt work :
$criteria = new CDbCriteria();
$criteria->select = 'question, COUNT(countries_id) as counter';
$criteria->with = array('countries', 'categories');
$criteria->addInCondition('countries.id' , $_POST['Questions']['countries']);
$criteria->group = 'question';
$criteria->having = ('counter = 1');
$model = Questions::model()->findAll($criteria)
Pls help, I'am pretty new to Yii framework.
Thanks.
Sql from the log :
SELECT `t`.`question` AS `t0_c1`,
COUNT(countries_id) as counter, `t`.`id` AS `t0_c0`, `countries`.`id` AS
`t1_c0`, `countries`.`name` AS `t1_c1`, `categories`.`id` AS `t2_c0`,
`categories`.`name` AS `t2_c1` FROM `questions` `t` LEFT OUTER JOIN
`questions_has_countries` `countries_countries` ON
(`t`.`id`=`countries_countries`.`questions_id`) LEFT OUTER JOIN `countries`
`countries` ON (`countries`.`id`=`countries_countries`.`countries_id`)
LEFT OUTER JOIN `questions_has_categories` `categories_categories` ON
(`t`.`id`=`categories_categories`.`questions_id`) LEFT OUTER JOIN
`categories` `categories` ON
(`categories`.`id`=`categories_categories`.`categories_id`) WHERE
(countries.id=:ycp0) GROUP BY question HAVING (counter = 2). Bound with
:ycp0='1'
You have done most of work. Now you need to call the $criteria into model. Just like this
$rows = MODEL ::model()->findAll($criteria);
Where MODEL is model class of table which you want to apply criteria on.
To learn more about this you can follow this CActiveRecord Class.
Try to set together in CDbCriteria
...
$criteria->together = true;
$model = Question::model()->findAll($criteria);
when you use "as counter", your model must have a property named "counter" or it will not load it into your model.
if you don't have a property named "counter", try using another one of your models property that you are not selecting right now : "as someColumn"
and use condition or addCondition or .. instead of having
cheers