Conditionnally join table on Doctrine2 - php

I seeked for an answer but anything in my particular case.
I need to join contintionnally table with Doctrine 2, depends on value of a field I have to join on two different foreign keys, here my code :
$qb = $this->entityManager->createQueryBuilder();
$qb ->select('s')
->from('AppBundle:MyTable', 's')
->join('s.firstJoin', 'o')
->join('s.secondJoin', 'd')
->join('AppBundle:joinedView', 'view', Join::WITH,
"(CASE WHEN (d.secondJoinFK = 3)
THEN view.did = d.secondJoinFK
WHEN (d.secondJoinFK = 2)
THEN view.dvid = d.secondJoinFK END)")
->addSelect('d')
->where('s.endDate IS NULL');
However, with this request, Symfony tells me : [Syntax Error] line 0, col 203: Error: Expected Doctrine\ORM\Query\Lexer::T_ELSE, got '='
Moreover, I cannot use native query because I use PagerFanta for rendering template, and PagerFanta needs to have a ORM\Query on input and not ORM\NativeQuery or other.
Unfortunately, I do not have choice and must implement this request with these prerequisites.
Thanks by advance for your help,

It is not possible to do the something like conditional join. You always need to join both tables and specify different join condition.
You can do something like this:
->join('AppBundle:joinedView', 'view1', Join::WITH,
"view1.did = 3")
->join('AppBundle:joinedView', 'view2', Join::WITH,
"view2.dvid = 2")
but honestly I am not sure what are you trying to achieve. Joining tables without a condition which uses some joining column seems a bit pointless.

Related

Doctrine DQL - nested subqueries in WHERE clause throw Syntax Error

I'm trying to query data from one table based on the SUMed and COALESCEd result of two associated tables.
The query works fine when executed as native SQL, unfortunately my application is restricted to only using Doctrine's DQL on a QueryBuilder object here.
This is how I build my QueryBuilder:
$resUnitQuery = $queryBuilder->getEntityManager()->createQueryBuilder();
$resUnitQuery->select('COALESCE(SUM(pc.residentialUnits), 0)')
->from(PropertyConnection::class, 'pc')
->where("$rootAlias.id = pc.contract");
$comUnitQuery = $queryBuilder->getEntityManager()->createQueryBuilder();
$comUnitQuery->select('COALESCE(SUM(pwa.residential_units), 0)')
->from(PropertyWithoutAddress::class, 'pwa')
->where("$rootAlias.id = pwa.contract");
$queryBuilder = $this->getEntityManager(Contract::class)->createQueryBuilder();
$queryBuilder
->select('id')
->from(Contract::class, 'o')
->andWhere(
':residentialUnits <= ' .
sprintf(
'((%s) + (%s))',
$resUnitQuery->getQuery()->getDQL(),
$comUnitQuery->getQuery()->getDQL()
)
)
->setParameter('residentialUnits', $params['rangeFrom']);
The generated DQL code is as follows:
SELECT id
FROM App\Entity\Contract o
WHERE :residentialUnits <= ((SELECT COALESCE(SUM(pc.residentialUnits), 0) FROM App\Entity\PropertyConnection pc WHERE o.id = pc.contract) + (SELECT COALESCE(SUM(pwa.residential_units), 0) FROM App\Entity\PropertyWithoutAddress pwa WHERE o.id = pwa.contract))
This results in the following error:
[Syntax Error] line 0, col 66: Error: Expected Literal, got 'SELECT'
I found an old Doctrine GitHub issue stating that double parenthetis can cause issues, which is why I skipped them in my WHERE clause and replaced
((%s) + (%s))
with
(%s) + (%s)
The surrounding parenthesis are required for native SQL to work, and it also results in an error from Doctrine:
[Syntax Error] line 0, col 174: Error: Expected end of string, got '+'
May I be running into restrictions of Doctrine's DQL here?
According to the docs, both SUM and COALESCE are supported, as well as nested queries.
Thanks a lot in advance,
Rene
Correct, the DBAL does not support arithmetic operations on subqueries. Additional the DBAL does not support joined subqueries, JOIN (SELECT FROM...) ON without using a native query.
Another issue is the WHERE statement of your subqueries, being dependant on the root query, will cause your root query to perform a full table scan. Executing each of the subqueries per-row, unless a criteria is added to the root query (WHERE o.id ...).
As the subquery SUM values are dependant on the root query id. You can rewrite the query to use a subquery as a hidden column and a HAVING clause, to filter the id result set from the added column results.
$em = $queryBuilder->getEntityManager();
$expr = $em->getExpressionBuilder();
$qbPC = $em->createQueryBuilder()
->select('COALESCE(SUM(pc.residentialUnits), 0)')
->from(App\Entity\PropertyConnection::class, 'pc')
->where($expr->eq('pc.contract', "$rootAlias.id"));
$qbPWA = $em->createQueryBuilder()
->select('COALESCE(SUM(pwa.residential_units), 0)')
->from(App\Entity\PropertyWithoutAddress::class, 'pwa')
->where($expr->eq('pwa.contract', "$rootAlias.id"));
$qb = $this->getEntityManager(Contract::class)
->createQueryBuilder()
->select('o.id')
->from(App\Entity\Contract::class, 'o')
->addSelect('(' . $qbPC->getDQL() . ') AS HIDDEN pc_ru')
->addSelect('(' . $qbPWA->getDQL() . ') AS HIDDEN pwa_ru')
->having($expr->lte(':v', 'pc_ru + pwa_ru'))
->setParameter('v', $params['rangeFrom']);
dump($qb->getDQL());
Resulting DQL
SELECT
o.id,
(SELECT
COALESCE(SUM(pc.residentialUnits), 0)
FROM App\Entity\PropertyConnection pc
WHERE pc.contract = o.id
) AS HIDDEN pc_ru,
(SELECT
COALESCE(SUM(pwa.residential_units), 0)
FROM App\Entity\PropertyWithoutAddress pwa
WHERE pwa.contract = o.id
) AS HIDDEN pwa_ru
FROM App\Entity\Contract o
HAVING :v <= pc_ru + pwa_ru

How to JOIN without a relationship in Doctrine?

I have an entity Log (table log) with members resourceType and resourceId (with columns resource_log and resource_id). The resourceType can e.g. Order (for actions on orders, e.g. status changes ) or Whatever (for Whatever related actions). There is no relationships (neither in Doctrine, nor in the database) between the Log and the "resource" entities/tables.
Now I want to select only the Logs, that are related to Orders with myOrderProperty = "someValue". That means:
SELECT
*
FROM
`log`
JOIN
`order` ON `order`.`id` = `log`.`resource_id` AND `log`.`resource_type` = 'order'
WHERE
`order`.`my_order_property` LIKE '%my_order_property_value%'
But the code
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('l')->from(Log::class, 'l');
$queryBuilder->join('l.order', 'o');
...
$queryBuilder
->where('o.myOrderProperty = :myOrderProperty')
->setParameter('myOrderProperty', $myOrderProperty);
doesn't work, since the entity Log doesn't have any relationship with the Order (though it has a property order):
[Semantical Error] line 0, col 102 near 'o WHERE o.myOrderProperty': Error: Class MyNamespace\Log has no association named order
How to JOIN without a relationship defined between two entities?
I know, I could use inheritance. But semantically it's not an inheritance case. So is there another way for solving the problem?
The JOIN without relationship can be implemented like this:
$queryBuilder = $this->entityManager->createQueryBuilder();
$queryBuilder->select('l')->from(Log::class, 'l');
$queryBuilder->join(
Order::class,
'o',
\Doctrine\ORM\Query\Expr\Join::WITH,
'o.id = l.resourceId'
);
...
$queryBuilder
->where('o.myOrderProperty = :myOrderProperty')
->setParameter('myOrderProperty', $myOrderProperty);
The only problem is, that the joined entities are not added to the main entity, so that $myLog->getOrder(...) returns null.

Symfony Join Multiple tables

iam trying to join multiple tables in my bundle using DQL and
Error:
[Syntax Error] line 0, col 610: Error: Expected =, <, <=, <>, >, >=, !=, got 'CarparkFueltext'
Entity:
https://gist.github.com/anonymous/9fc7bfe89bb54427f89c
Code:
https://gist.github.com/anonymous/63680019a3f260733dca
I have also tried with createQueryBuilder() method
Code:
https://gist.github.com/anonymous/92012697fc99fcf02da5
ERROR:
[Syntax Error] line 0, col 423: Error: Expected Literal, got 'JOIN'
However if i remove either of the join statements
JOIN MyBundle:SpareParts\CarparkAgestext CarparkAgestext
OR
JOIN MyBundle:SpareParts\CarFueltext CarFueltext
I am getting the data.
The error seems to be that I cannt join multiple tables and i need to join atleast 5 tables to it. Any Idea how can i acheive it.
Join syntax is here.
General example:
...
->select(['e1'])
->from('AcmeDemoBundle:Entity1', 'e1')
->innerJoin('AcmeDemoBundle:Entity2', 'e2', 'WITH', 'e2.e1 = e1')
->innerJoin('AcmeDemoBundle:Entity3', 'e3', 'WITH', 'e3.e2 = e2')
...
You can join multiple tables, but you need to use the right association of MyBundle:SpareParts\Carparktype.
You should use a query like the following one:
$query = $this->getEntityManager()->createQueryBuilder()
->select($fields)
->from('MyBundle:SpareParts\Carparktype Carparktype')
->innerJoin('Carparktype.agesText CarparkAgestext')
->where('Carparktype.makename =:makename')
->andWhere('CarparkAgestext.id =:agesid')
->setParameter('makename',$makename)
->setParameter('agesid',$param)
->getQuery()
->getResult();
As you see you will build a JOIN statement starting from a single property of SpareParts\Carparktype, which will be mapped as an association (a foreign key) for SpareParts\CarparkAgestext.
Maybe you're trying to JOIN two tables with no relationship defined. If that's the case you can JOIN them using WITH
FROM FooBundle:Entity1 e1 JOIN FooBundle:Entity2 e2 WITH e2.e1_id = e1.id

Doctrine query builder, select foreign key

I have this query :
$qb = $this->_em->createQueryBuilder();
$qb->select('DISTINCT c.account')
->from('ThanksWhoProjectBundle:Comment', 'c')
->leftjoin('c.account', 'a')
->where('c.conversation = ?1')
->setParameters(array(1 => $conversation));
return $qb->getQuery()->getResult();
So, the field Comment.account is a foreign key with my entity Account. I just need to retrieve all differents accounts who are in the conversation.
So, i want to only select the field c.account, but with this query have this error :
[Semantical Error] line 0, col 18 near 'account FROM': Error: Invalid PathExpression. Must be a StateFieldPathExpression. (500 Internal Server Error)
How can i do that ?
You need to use DISTINCT on the joined account id:
$qb = $this->_em->createQueryBuilder();
$qb->select('DISTINCT a.id') // note 'a.id'
->from('ThanksWhoProjectBundle:Comment', 'c')
->leftjoin('c.account', 'a')
->where('c.conversation = ?1')
->setParameters(array(1 => $conversation));
return $qb->getQuery()->getResult();
From the official documentation: http://docs.doctrine-project.org/en/latest/reference/dql-doctrine-query-language.html
"Making this possible is very simple. Just add the property shopId to the product entity as any other field mapping property, and so that it corresponds with the actual column in the database table. Make sure you put this property ABOVE the association property “shop”, otherwise it will not work as expected."
http://pietervogelaar.nl/doctrine-2-use-foreign-key-as-field-in-dql/
Note: I am using a custom hydrator. I did run into issues when using this with the default hydrator.
Great, no more unnecessary joins for read only queries! I'm not sure what the effects would be when trying to update the objects in a result set.

Joining with OR in Kohana

I'm trying to combine two tables based on if they match a given pair in a many-to-many relation. I already know the SQL statement I'm trying to produce, which is functionally equivalent to the following:
SELECT columnA, columnB, ...
...
JOIN matching_table
ON ( (matching_table.id1 = table_a.id AND matching_table.id2 = table_b.id) OR
(matching_table.id1 = table_b.id AND matching_table.id2 = table_a.id) )
...
But I want to produce it using Kohana's query builder for consistency. The problem is I can't seem to find a way of creating a complex ON query. So far all I've got is
DB::select('columnA', 'columnB', ...)
...
->join('matching_table')
->on('matching_table.id1', '=', 'table_a.id')
->on('matching_table.id2', '=', 'table_b.id')
...
and this generates the first AND sequence but I can't seem to put it together with an OR.
Any suggestions?
You should be able to use where instead of on (not tested though):
->join('matching_table')
->where_open()
->where('matching_table.id1', '=', 'table_a.id')
->and_where('matching_table.id2', '=', 'table_b.id')
->where_close()
->or_where_open()
->where('matching_table.id1', '=', 'table_b.id')
->and_where('matching_table.id2', '=', 'table_a.id')
->or_where_close()

Categories