Doctrine : SELECT a relationned entity - symfony

I have 2 entities :
LinkServInfra
Serv
LinkServInfra entity :
id
serv (relation OneToOne to Serv)
infra (ManyToOne to another entity)
Now, I'd like to get a list of Serv based on infra.
So I tried in LinkServInfraRepository :
$qb = $this->createQueryBuilder('s')
->select('DISTINCT s.serv')
->where('s.infra = :infra')
->setParameter('infra', $infra);
return $qb->getQuery()->getResult();
And I get an error :
[Semantical Error] near 'serv FROM': Error: InvalidPathExpression.
Must be a StateFieldPathExpression.
(I tried first without DISTINCT and tried with it based on answer found here).
The query seems to be good because it's :
SELECT DISTINCT s.serv
FROM MyBundle\Entity\LinkServInfra s
WHERE s.infra = :infra
How would you do?
Thanks!

Since the relation is ONLY in LinkServInfra and not in Serv :
I used the relation in LinkServInfra to do it...
public function filter($type, $etat, $infra){
$qb = $this->createQueryBuilder('l');
$qb->where(' l.infra = :infra ')
->setParameter('infra', $infra);
if( $type || $etat ){
$qb->join('l.serv', 's')
//where & setParameter for type
//where & setParameter for detail
}
And then where I call the function filter :
$entities = $em->getRepository('linkServInfra')->filter(//...
$servs = array();
foreach($entities as $entity){
$servs[] = $entity->getServ();
}
I guess it's not the cleanest solution but it's working...

I think you can not use DISTINCT on a relation, only for the root entity alias.
You should use Serv entity as root and join with LinkServInfra and infra entity through the 2nd. Something like:
$qb = $this->getDoctrine()
->getRepository(Serv::class)->createQueryBuilder('s')
->select('DISTINCT s')
->leftJoin("s.linkServInfra", "lsi")
->leftJoin("lsi.infra", "infra")
->where('infra.id = :infra')
->setParameter('infra', 1);
Alternatively, if the relation is unidirectional and you do not want to make it bidirectional, you can use a custom join:
use Doctrine\ORM\Query\Expr\Join;
$qb = $this->getDoctrine()
->getRepository(Serv::class)->createQueryBuilder('s')
->select('DISTINCT s')
->leftJoin(LinkServInfra::class, "lsi", Join::WITH, 'lsi.serv = s')
->leftJoin("lsi.infra", "infra")
->where('infra.id = :infra')
->setParameter('infra', 1);
References
How to JOIN without a relationship in Doctrine?

Related

doctrine querybuilder $qb -> expr()-> all

I'm working with Symfony2. I have to list last $nbOpe entities of Entity OpeEntetes. The following script gives me error:
Blockquote
[Doctrine\ORM\Query\QueryException]
[Semantical Error] line 0, col 16 near 'ALL(SELECT FROM': Error: Class 'ALL' is not defined.
Blockquote
What to do ?
public function essai1($nbOpe){
$in = $this->_em->createQueryBuilder()
->from('GcRequetesBundle:OpeEntetes','ein')
->orderBy('ein.oeNumOpe', 'DESC')
->setMaxResults($nbOpe);
$qb = $this->_em->createQueryBuilder();
$qb
-> add("select",new Expr\Select(array('res')))
-> add('from', new Expr\From($qb -> expr()-> all($in -> getDql()), 'res'));
$query = $qb -> getQuery();
$result= $query -> getArrayResult();
return $result;
}
There are couple of errors in your code:
subqueries do no support setMaxResults Enabling LIMIT resp. setMaxResults on subquery
from field does not support subqueries Subquery into FROM
you have probably misunderstood what the ALL expression means and for what it is used for For example MySql documentation about ALL
In any case, the restriction that subqueries do not support the setMaxLimits probably means that you cannot build your code in this way at all. If that isn't a show-stopper for you, then you could try it with the following code for example (easiest would be to just have one query, but I assume you have some reason for doing this in two steps...):
$in = $em->createQueryBuilder()
->select('ein')
->from('GcRequetesBundle:OpeEntetes','ein')
->orderBy('ein.oeNumOpe', 'DESC')
->setMaxResults($nbOpe); // this does not affect the result at all
$qb = $em->createQueryBuilder();
$qb->add('select', 'ein2');
$qb->add('from', 'GcRequetesBundle:OpeEntetes ein2');
$qb->add('where', $qb->expr()->eq('ein2.id', $qb->expr()->any($in->getDql())));
$query = $qb -> getQuery();
$result= $query -> getArrayResult();
return $result;

Doctrine: ordering By Count on Associated entities

Goal: Ordering my entities on how ofter they are used on associated relations (oneToMany)
The problem: DQL, it somehow handles the FROM keyword is an entity or class.
The Exception thrown: QueryException: [Semantical Error] line 0 col 103 near 'FROM MyBundle:Entity Error: Class 'FROM' is not defined.
Here is a SQL query I wrote to test how to get the data. It works perfectly
SELECT en.id, (COUNT(DISTINCT ag.artist_id) + COUNT(DISTINCT rg.release_id) + COUNT(DISTINCT tg.track_id)) AS total
FROM myapp.entity AS en
LEFT JOIN myapp.other_one AS o1 ON o1.entity_id = en.id
LEFT JOIN myapp.other_two AS o2 ON o2.entity_id = en.id
GROUP BY en.id
ORDER BY total DESC ;
To get the data in symfony to hydrate to objects I try to use Doctrine Query Language in the EntityRepository like this:
/**
* Find Most Common Entities
*
* #return array
*/
public function findMostCommon()
{
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
$qb
->select('en, (COUNT(DISTINCT en.other1) + COUNT(DISTINCT en.other2)) AS count')
->from('MarulaBundle:Entity', 'en')
->leftJoin('MyBundle:Other1', 'o1', 'WITH', 'o1.entity = en.id')
->leftJoin('MyBundle:Other2', 'o2', 'WITH', 'o2.entity = en.id')
->groupBy('en')
->orderBy('count', 'DESC')
;
return $qb->getQuery()->getResult();
}
Since it is possible in a SQL query. I was hoping it would work just as fine with DQL.
Has anyone experienced this error before? Is it even possible to achieve this with Doctrine, or is doctrine limited relating this issue?
OOPS: I should not use the keyword "count"
Solution:
->select('en, (COUNT(DISTINCT en.other1) + COUNT(DISTINCT en.other2)) AS HIDDEN orderCount')
note: adding the HIDDEN keyword helps to get only the entities back, which makes the hydration fit right in

delete several rows where attribute = x

I need to delete all rows where the image_id = x applies
like this
DELETE FROM `ImageVoters` WHERE image_id =1
How do I do this using DQL?
Since im trying to delete several rows at once the remove() function won't work
EntityManager#remove() expects parameter 1 to be an entity object, array given.
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery('SELECT u FROM GabrielUploadBundle:ImageVoters u WHERE u.image_id = 1');
$db_imageactions = $query->getResult();
$em->remove($db_imageactions);
$em->flush();
This is the code that works
$qb = $em->createQueryBuilder();
$qb->delete('GabrielUploadBundle:ImageVoters', 'v')
->where($qb->expr()->eq('v.image_id', ':imageId'))
->setParameter('imageId', $current_imageId);
$qb->getQuery()->execute();
You could create an ORM queryBuilder, to create a clean/safe query object by using internal orm methods
$qb->delete('My\Image\Namespace\ImageVoters', 'ivoter')
->where($qb->expr()->eq('ivoter.image_id', ':imageId')
->setParameter('imageId', 1);
$qb->getQuery()->execute();
You can also execute any raw SQL like this:
$sql = "DELETE FROM ImageVoters WHERE image_id =".$image_id;
$q = $em->getConnection()->exec($sql);

Why is Doctrine joining this entity onto itself? (Not unique table/alias)

I have an entity named 'tile', with a manyToMany relationship with another entity named 'coins'.
/**
* #ORM\ManyToMany(targetEntity="Coin")
* #ORM\JoinTable(name="coin",
* joinColumns={#ORM\JoinColumn(name="tile_id", referencedColumnName="tile_id")},
* inverseJoinColumns={#ORM\JoinColumn(name="coin_id", referencedColumnName="coin_id")}
* )
**/
protected $coins;
I have a page that lists tiles and all of their associated coins. There can be a lot of coins and tiles on a page and using lazy loading can put 100-300 database queries on a single page. I am trying to avoid this by using a leftJoin.
public function getUserTiles($id)
{
$qb = $this->createQueryBuilder('b')
->select('b', 'z')
->leftJoin('b.coins', 'z')
->leftJoin('z.userInfo', 'u')
->add('where', "b.userId = ".$id." AND b.status = 'COMPLETED'")
->add('orderBy', "b.checkinDate DESC");
return $qb->getQuery()
->getResult();
}
This gives me the following error:
*[2/2] DBALException: An exception occurred while executing '
SELECT t0_.tile_id AS tile_id0, t0_.quilt_id AS quilt_id1, t0_.user_id AS user_id2, t0_.comment AS comment3, t0_.checkout_date AS checkout_date4, t0_.checkin_date AS checkin_date5, t0_.x AS x6, t0_.y AS y7, t0_.status AS status8, t0_.completed_neighbors AS completed_neighbors9, t0_.required_completed_neighbors AS required_completed_neighbors10, t0_.visible AS visible11, t0_.visible_date AS visible_date12, t0_.count_towards_coins AS count_towards_coins13, c1_.coin_id AS coin_id14, c1_.user_id AS user_id15, c1_.tile_id AS tile_id16, c1_.comment AS comment17, c1_.date_given AS date_given18, c1_.status AS status19, c1_.origin AS origin20, t0_.quilt_id AS quilt_id21, t0_.user_id AS user_id22, c1_.tile_id AS tile_id23, c1_.user_id AS user_id24
FROM tile t0_ **LEFT JOIN coin *c1_* ON t0_.tile_id = c1_.tile_id
LEFT JOIN coin *c1_* ON c1_.coin_id = c1_.coin_id**
LEFT JOIN user_info u2_ ON c1_.user_id = u2_.user_id
WHERE t0_.user_id = 14 AND t0_.status = 'COMPLETED'
ORDER BY t0_.checkin_date DESC':
SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'c1_'*
As you can see, it is trying to join the coin table twice, using the same alias. What am I doing wrong?
Try:
$this->createQueryBuilder('b')
->select('b')
->addSelect('z')
->leftJoin('b.coins', 'z')
->leftJoin('z.userInfo', 'u')
->where("b.userId = :id AND b.status = 'COMPLETED'")
->orderBy('b.checkinDate', 'DESC')
->setParameter('id', $id)
->getQuery('b.checkinDate DESC')
->getSingleResult();
I fixed this problem by changing the ManyToMany relationship to OneToMany as it should have been in the first place.

'where not in' query with doctrine query builder

Im trying to reproduce this query:
SELECT * FROM `request_lines`
where request_id not in(
select requestLine_id from `asset_request_lines` where asset_id = 1
)
in doctrine query builder,
I am stuck on the where request_id not in(select
I currently have:
$linked = $em->createQueryBuilder()
->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where()
->getQuery()
->getResult();
You need to use query builder expressions, and this means you need access to the query builder object. Also, the code is easier to write if you generate the subselect list ahead of time:
$qb = $em->createQueryBuilder();
$nots = $qb->select('arl')
->from('$MineMyBundle:MineAssetRequestLine', 'arl')
->where($qb->expr()->eq('arl.asset_id',1))
->getQuery()
->getResult();
$linked = $qb->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where($qb->expr()->notIn('rl.request_id', $nots))
->getQuery()
->getResult();
It is possible to do this in one Doctrine query:
$qb = $this->_em->createQueryBuilder();
$sub = $qb;
$sub = $qb->select('arl')
->from('$MineMyBundle:MineAssetRequestLine', 'arl')
->where($qb->expr()->eq('arl.asset_id',1));
$linked = $qb->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where($qb->expr()->notIn('rl.request_id', $sub->getDQL()))
->getQuery()
->getResult();
Check the reference in this answer here
Using Symfony 5, this solution might help those, who are trying to set parameters on a subquery, the notIn() 2nd argument accepts an array or you could pass a DQL instead and that's what we are doing here and keep in mind that the parameters should be added to the main query as below.
$main = $this->em->createQueryBuilder();
$sub = $main;
$sub = $sub->select('arl')
->from('$MineMyBundle:MineAssetRequestLine', 'arl')
->where($sub->expr()->eq('arl.asset_id',':id'));
$linked = $main->select('rl')
->from('MineMyBundle:MineRequestLine', 'rl')
->where($main->expr()->notIn('rl.request_id', $sub->getDQL()))
->setParameter('id', 1)
->getQuery()
->getResult();

Resources