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

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.

Related

Doctrine query works with SQLite, not with MySQL

I've created two entities called Project and ProjectTag using Doctrine (doctrine/orm v2.6.1) and Symfony. I'd like to select all projects that have all tags with the names given in $tags. The following query from my ProjectRepository.php works in my dev environment with SQLite:
public function findByAllTags($tags)
{
$queryBuilder = $this->createQueryBuilder('p');
for ($i = 0; $i < count($tags); $i++) {
$tagSubQueryBuilder = $this->createQueryBuilder("p$i")
->join('p.tags', "tags_joined_$i")
->andWhere("tags_joined_$i.name = ?$i");
$queryBuilder->andWhere(
$queryBuilder->expr()->exists($tagSubQueryBuilder->getDQL())
);
}
$queryBuilder->orderBy('p.timeCreated','DESC');
return $queryBuilder->getQuery()->setParameters($tags)->getResult();
}
This gets converted to
SELECT
p0_.id AS id_0,
p0_.name AS name_1
FROM
project p0_
WHERE
(
EXISTS (
SELECT
p1_.id
FROM
project p1_
INNER JOIN projects_tags p3_ ON p0_.id = p3_.project_id
INNER JOIN project_tag p2_ ON p2_.id = p3_.project_tag_id
WHERE
p2_.name = ?
)
)
ORDER BY
p0_.created DESC
However, in my production environment with MySQL I get the following error:
An exception occurred while executing 'SELECT p0_.id AS id_0, p0_.name AS name_1 FROM project p0_ WHERE (EXISTS (SELECT p1_.id FROM project p1_ INNER JOIN projects_tags p3_ ON p0_.id = p3_.project_id INNER JOIN project_tag p2_ ON p2_.id = p3_.project_tag_id WHERE p2_.name = ?)) ORDER BY p0_.created DESC' with params ["video"]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'p0_.id' in 'on clause'
In response to your comment, this is an untested approach to your problem without nested queries:
public function findAllByTags($tags)
{
$qb = $this->createQueryBuilder('p');
$qb->join('p.tags', 't');
$qb->where($qb->expr()->in('t.name', ':tags'));
$qb->groupBy('p.id');
$qb->having($qb->expr()->eq('COUNT(t.id)', ':count'));
$qb->setParameter('tags', $tags);
$qb->setParameter('count', count($tags));
return $qb->getQuery()->getResult();
}
This assumes you pass the tag names as an array (or that the Tag class implements a __toString() method that returns the tags name).

Doctrine : SELECT a relationned entity

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?

Using left join with query builder doctrine

Having some problems retrieving ManytoOne relationships when using left join.
Before was using this query to query for conferences
$qb = $this->createQueryBuilder('u')
->select('u.id,u.comment,
IDENTITY(u.place) AS place_id,
IDENTITY(u.sponsor) AS sponsor_id,
IDENTITY(u.tour) AS tour_id,
u.startat
');
Now I'm trying to left join with diffusion which is tied to the diffusion in a many to many relationship.
$qbt = $this->createQueryBuilder('u')
->select('u','c')
->from('AppBundle:Conference', 'p')
->leftJoin('p.diffusion', 'c');
However this query doesn't return the u.place, u.sponsor and u.tour which are ManyToOne relationships.
leftJoin must be followed by 'WITH'. So for example:
->leftJoin('p.diffusion', 'p', 'WITH', 'p.user=u.id', 'u.id');
But i think it's better to post both your entities so i can give you the exact answer.
Found the issue , I had to add
->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true)
to the getQuery , because getArrayResults by default doesn't return foreign keys( the place, sponsor and tour respectivly).
Here is my final query in the conference repository
$qbt = $this->_em->createQueryBuilder();
$qbt->select('conference','diffusion')
->from('AppBundle:Conference', 'conference')
->leftJoin('conference.diffusion', 'diffusion');
return $qbt
->getQuery()
->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true)
->useQueryCache(true)
->useResultCache(true,3600)
->getArrayResult();

Symfony custom repository query giving error

I am really new at Symfony and I am trying to get used to query builder. At the moment I am trying to join three tables and the following MySQL query gives me the results I need when I run it in PhpMyAdmin
SELECT * FROM pe_users u
LEFT JOIN pe_apply a ON u.id = a.user
LEFT JOIN pe_offer o ON a.id = o.application
However when I move this inside a Symfony Repository method
namespace Ache\AdminBundle\Repository;
use Doctrine\ORM\EntityRepository;
class AdminRepository extends EntityRepository{
public function findAllAppByStatus(){
$query = $this->getEntityManager()->createQuery(
'SELECT * FROM pe_users u
LEFT JOIN pe_apply a ON u.id = a.user
LEFT JOIN pe_offer o ON a.id = o.application
');
try {
return $query->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
}
I get the error
[Syntax Error] line 0, col 7: Error: Expected IdentificationVariable |
ScalarExpression | AggregateExpression | FunctionDeclaration |
PartialObjectExpression | "(" Subselect ")" | CaseExpression, got '*'
What does this error mean? what am I doing wrong?
UPDATE
The three entities I have are as following
UserBundle:User
CoreBundle:Apply
AdminBundle:Offer
User.id links with Apply.user and Offer.application links with Apply.id
You can still use raw sql with Symfony if you are comfortable with that
$conn = $this->getEntityManager()->getConnection();
$sql = "SELECT * FROM pe_users u LEFT JOIN pe_apply a ON u.id = a.user LEFT JOIN pe_offer o ON a.id = o.application WHERE u.id = a.user";
$stmt = $conn->prepare($sql);
$stmt->execute();
return $stmt->fetchAll();
I would do :
public function findAllAppByStatus(){
$qb = $this->createQueryBuilder('u')
->leftJoin('CoreBundle:Apply', 'a', 'WITH', 'a.user = u')
->leftJoin('AdminBundle:Offer', 'o', 'WITH', 'o.application = a')
->setMaxResults(1); // if you return only 1 result, you want to be sure only one (or none) result is fetched
return $qb->getQuery()->getOneOrNullResult();
if you want to return possibly many results, as the 'All' in the method name suggests, get rid of the ->setMaxResults() and use $qb->getQuery()->getResult();
see how the queryBuilder works with objects, not tables. Joins are built on entities and properties, not tables and field names.

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

Resources