I am trying to place a variable into the field name in the query , so I have a schema:
id amazon - tesco - asda - happyshopper -
1 £5 - NULL - NULL - £4.99
2 NULL - £2.99 - NULL - NULL
and then
$store = 'amazon';
$qb = $em->createQueryBuilder();
$products = $qb->select('p')->from('MyBundle:Product', 'p')
->where('p.:store IS NOT NULL')
->setParameter('store', $store)
->add('orderBy', 'p.onSale DESC')
->setMaxResults(40)
->getQuery()
->getResult();
Would return row 1.
What I've done for :
->where('p.:store IS NOT NULL')
->setParameter('store', $store)
Is incorrect and it errors.
->where(':store IS NOT NULL')
->setParameter('store', $store)
does not error, but doesn't apply the store filter.
The short answer here is to just work the store name into the string manually:
->where("p.$store IS NOT NULL")
or
->where('p.' . $store . ' IS NOT NULL')
The long answer is that your database schema could use some work. What happens, for example, if/when you want to add a new store? Are you going to add a new column and re-code the whole thing? The better solution is to separate the concept of "store", put it in its own table, and join everything together in a different table. Something like this:
Product:
id | name | onSale
1 | foo | 1
2 | bar | 0
Store:
id | name
1 | amazon
2 | tesco
3 | asda
4 | happyshopper
Price:
id | productId | storeId | price
1 | 1 | 1 | 5
2 | 1 | 4 | 4.99
3 | 2 | 2 | 2.99
Once you configure your tables and your mappings properly, your query turns in to:
$qb = $em->createQueryBuilder();
$products = $qb
->select('product')
->from('MyBundle:Price', 'price')
->innerJoin('price.product', 'product')
->innerJoin('price.store', 'store')
->where('store.name = :store')
->setParameter('store', $store)
->add('orderBy', 'product.onSale DESC')
->setMaxResults(40)
->getQuery()
->getResult();
Related
A query load and hydrate a main entity with it's joined entities, which are filtered.
Same query with a different filtering did not update the joined entities.
Some data:
Tiers:
| id | name |
| 1 | alpha |
| 2 | beta |
Container:
| id | tiers_id | category |
| 10 | 1 | A |
| 20 | 1 | A |
| 30 | 1 | B |
| 40 | 1 | B |
Execute 2 queries to get some tiers with theirs containers joined, category A first, then category B:
$dql = "select t, c
from Tiers t
join t.containers c
where t.id in (?1) and c.category = (?2)";
$result = $em->createQuery($dql)
->setParameter(1, array(1))
->setParameter(2, 'A')
->getResult();
$tiers = $result[0];
$containers = $tiers->getContainers(); // tiers 1 with containers 10 and 20, that's fine !
$result = $em->createQuery($dql)
->setParameter(1, array(1))
->setParameter(2, 'B')
->getResult();
$tiers = $result[0];
$containers = $tiers->getContainers(); // BAD HERE: still get containers 10 and 20, looking for containers 30 and 40.
After the 2nd query, tiers 1 preserve its containers loaded during first query. That's not what is expected.
So is there a way to get the containers 30 and 40 after 2nd query ?
Maybe a kind of "reset/detach" the containers of tiers entities after the first query ?
Or anything else...
Mutiple select in queries are used to hydrate tiers with required containers joined.
'getContainers' method gives the expected containers from each tiers.
And cost to BDD is only 1 SQL query, whatever the quantity of tiers searched.
I suppose tiers cannot be detach/reload because they are updated before, between and after the queries, it throw this kind of exception when flushing:
Uncaught Exception: Multiple non-persisted new entities were found through the given association graph
* A new entity was found through the relationship 'XXX' that was not configured to cascade persist operations for entity: XXX\Entity\Tiers#00000000257b87500000000018499b62.
Reset the tiers's containers before 2nd query:
foreach($result as $tiers)
$tiers->nullContainers();
Add method to Entity\Tiers:
public function nullContainers()
{
this->containers = null;
}
Then the 2nd query "refresh" tiers's containers.
I Have two tables : Post and Comment in a Many to One relation.
table post table comment
p_id | name | u_id c_id | p_id | comment | date
1 | post 1 | 1 1 | 1 | blah blah blah | 2017-01-01
2 | post 2 | 2 2 | 1 | blah blah blah | 2017-01-04
3 | post 3 | 1 3 | 2 | blah blah blah | 2017-01-07
... ...
I would like to retrieve all Posts by u_id with their 3 Last comments ordered by date.
I would do :
SELECT p, c
FROM p
LEFT JOIN p.comments c WITH c.date IN (SELECT c2.date) FROM App\Bundle\Entity\Comment as c2 WHERE c2.post = p.p_id ORDER BY date ASC LIMIT 3)
WHERE p.user = 1
But doctrine doesn't allow LIMIT, how i can do that ?
In my opinion the easiest way to to that is by using the function
setMaxResults($limit);
I give you an example :
$qb = $this->createQueryBuilder('a');
$qb
->innerJoin('a.advert', 'adv')
->addSelect('adv')
;
$qb->setMaxResults($limit);
return $qb
->getQuery()
->getResult()
I hope it will help you
As I known QueryBuilder::setMaxResult() work only on primary table not on subquery.
IMHO it's best to write it in classic SQL, where you have better control on subquery and what is joining. In this case you get plain array not array of objects with relation. But, this is better when you need only to show lists of posts and not interact with single entity. In this case getting entire object hydrating one by one populating from DQL, is quite slower then using plain array from SQL.
You could do this like that:
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->prepare('SELECT ...');
$stmt->execute($parameters);
$results = $stmt->fetchAll(\PDO::FETCH_ASSOC);
I have a couple of tables:
table items: id, title
table properties: id_item, name, value
So an item has multiple properties (is an EAV).
Now i need to find which item has some properties, so i try to join the same relation multiple times:
$queryBuilder = $this->createQueryBuilder('i')
->join('i.properties', 'p');
$i = 0;
foreach ($properties as $name=>$value) {
$queryBuilder->join('i.properties', 'p'.$i)
->andWhere("p{$i}.name = :name".$i)
->setParameter(':name'.$i, $name)
->andWhere("p{$i}.value = :value".$i)
->setParameter(':value'.$i, $value);
$i = $i + 1;
}
return $queryBuilder->getQuery()->getResult();
But this doesn't work because the join is not repeated by doctrine, it uses always the same.
UPDATE:
To be more clear, if I join only once the table item and properties I get:
id | title | name | value
1 | t shirt | color| red
1 | t shirt | size | large
But if i need to search the items that have property color=red and size=large, i need to join it twice, so that i can make more where condition on different columns:
id | title | name1 | value1 | name2 | value2
1 | t shirt | color | red | size | large
The SQL generate actually is something like:
SELECT m0_.id AS id0
FROM item m0_
INNER JOIN properties m1_ ON m0_.id = m1_.item_id
WHERE m1_.name = 'Color' AND m1_.value = 'red' AND m1_.name = 'Size' AND m1_.value = 'Large'
But obviously m1_.name cannot be Color and Size at the same time.
I have a entity Article20000Information, with a few fields like: id, description, manufacturer_id, supplier_id
I have another entity, Organisation. It has a list of companies (both manufacturers & suppliers) each with an id.
I also have a page which renders a list of Article20000Information data. Currently, it simply displays the data in the table so:
| id | Description | Manufacturer | Supplier | Price |
|----|-------------|--------------|----------|--------|
| 1 | thing1 | 2 | 5 | 34 |
| 2 | thing2 | 5 | 2 | 23 |
| 3 | thing3 | 3 | 4 | 25 |
What I need is for the manufacturer and supplier column to display the name value from the organisation table, based on the id shown.
What is the best way to go about this?
Got it!
I needed multiple aliases, which I'd guessed, but I also needed to give them AS so that they come out with different column names. This in turn lets twig render the tags.
<?php
namespace Regenerys\QMSBundle\Entity;
use Doctrine\ORM\EntityRepository;
class Article20000InformationRepository extends EntityRepository
{
public function findStuff()
{
return $this->getEntityManager()
->createQuery(
'SELECT
A.id,
A.articleNumber,
A.description,
B.name as manufacturer,
C.name as supplier
FROM
RegenerysQMSBundle:Article20000Information A
LEFT OUTER JOIN RegenerysQMSBundle:Organisation B WITH B.id = A.manufacturer
LEFT OUTER JOIN RegenerysQMSBundle:Organisation C WITH C.id = A.supplier '
)
->getResult();
}
}
Thanks to #Alexandru for his DQL help.
You need to join the two tables based on id condition.
select A.id, A.Description, B.ManufacturName, B.supplierName
from Article20000Information A
left outer join Organisation B
ON B.id = A.id
More info on table joins.
If you are using doctrine, a proper way is to create a Repository class, and there write your joined code proposed by #K139 but in DQL:
class Article20000InformationRepository extends EntityRepository
{
public function findAll()
{
return $this->getEntityManager()
->createQuery(
'SELECT A.id, A.Description, B.ManufacturName, B.supplierName FROM AppBundle:Article20000Information A
LEFT OUTER JOIN AppBundle:Organisation B ON B.id = A.id '
)
->getResult();
}
}
Then in your controller you will use it:
$articles = $em->getRepository('AppBundle:Article20000Information')->findAll();
My action:
$matches_request = $em->getRepository('Bundle:ChanceMatch')->findByRequestUser(1);
$matches_reply = $em->getRepository('Bundle:ChanceMatch')->findByReplyUser(1);
Is it possible to join the querys with an or condition with getRepository, eg.
$matches_reply = $em->getRepository('FrontendChancesBundle:ChanceMatch')->findBy(array('requestUser' => 1, 'replyUser' => 1);
//this of course gives me the a result when `requestUser` and `replyUser` is `1`.
My table
id | requestUser | replyUser
....
12 | 1 | 2
13 | 5 | 1
My query should return the id 12 & 13.
Thanks for help!
You can use QueryBuilder or create a custom repository for that entity and create a function that internally use QueryBuilder.
$qb = $em->getRepository('FrontendChancesBundle:ChanceMatch')->createQueryBuilder('cm');
$qb
->select('cm')
->where($qb->expr()->orX(
$qb->expr()->eq('cm.requestUser', ':requestUser'),
$qb->expr()->eq('cm.replyUser', ':replyUser')
))
->setParameter('requestUser', $requestUserId)
->setParameter('replyUser', $replyUserId)
;
$matches_reply = $qb->getQuery()->getSingleResult();
// $matches_reply = $qb->getQuery()->getResult(); // use this if there can be more than one result
For more information on custom Repository see official documentation:
http://symfony.com/doc/2.0/book/doctrine.html#custom-repository-classes
It's possible to use Criteria for the complex queries with getRepository():
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria
->orWhere($criteria->expr()->contains('domains', 'a'))
->orWhere($criteria->expr()->contains('domains', 'b'));
$domain->ages = $em
->getRepository('Group')
->matching($criteria);