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.
Related
I struggle to create a query to get information from two tables. I want to count rows and group it by category and type.
My normal PostgresSQL query looks like this:
SELECT c.name AS category_name, i.type, count(i) AS number_of_items
FROM item i
INNER JOIN category c
ON i.category_id = c.id
GROUP BY c.name, i.type
How do I build this query using Doctrine Query Builder?
What I have tried:
$qb->select(['c.name', 'i.type', 'count(i)'])
->from('AppBundle:Item', 'i')
->innerJoin('i', 'AppBundle:Category', 'c', 'i.category_id = c.id')
->groupBy('c.name')
->groupBy('i.type');
return $qb->getQuery()->getResult();
But this give me an error:
[Semantical Error] line 0, col 80 near 'i AppBundle:Category':
Error: Class 'i' is not defined.
I'm trying to follow the principle in the documentation found here: http://doctrine-orm.readthedocs.io/projects/doctrine-dbal/en/latest/reference/query-builder.html#join-clauses
Any help would be appreciated.
Using doctrine and without defining any mapping between your related entities is not a good practice you should start from Association Mapping
Once you have defined mappings in your entities you can simply join your main entity using the properties which holds the reference of linked entities, doctrine will automatically detects the join criteria you don't need to specify in query builder, sample mapping for your entities can be defined as
class Item
{
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="items")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
}
-
use Doctrine\Common\Collections\ArrayCollection;
class Category
{
/**
* #ORM\OneToMany(targetEntity="Item", mappedBy="category")
*/
private $items;
public function __construct()
{
$this->items = new ArrayCollection();
}
}
And then your query builder will look like
$qb->select('c.name', 'i.type', 'count(i)'])
->from('AppBundle:Category', 'c')
->innerJoin('c.items','i')
->groupBy('c.name')
->addGroupBy('i.type');
Relationship Mapping Metadata
Or if you still don't want to have mappings and use the other approach you have to use WITH clause in doctrine
$qb->select(['c.name', 'i.type', 'count(i)'])
->from('AppBundle:Item', 'i')
->innerJoin('AppBundle:Category', 'c', 'WITH' , 'i.category_id = c.id')
->groupBy('c.name')
->addGroupBy('i.type');
you don't need to use array inside select try this:
$qb->select('c.name', 'i.type', 'count(i)')
->from('AppBundle:Item', 'i')
->innerJoin('i', 'AppBundle:Category', 'c', 'i.category_id = c.id')
->groupBy('c.name')
->groupBy('i.type');
return $qb->getQuery()->getResult();
Tried to use full expression for your entity Like this :
->from('AppBundle\Entity\Item','i')
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.
A working application uses a native SQL query to get items from a ManyToMany Entity relationship. Attempts to translate that query end up in a myriad of error messages. The Household entity has a ManyToMany relationship with Reason. Both entities & their relationships are properly defined. The joining table (household_reason) is not defined in .../Entity. The part that eludes me is how to join the Contact entity, with which the Household entity has a OneToMany relationship.
The working SQL:
select distinct r.reason
from reason r
join household_reason hr on hr.reason_id = r.id
join household h on h.id = hr.household_id
join contact c on c.household_id = h.id...
For starters, here's the beginning of a query builder in the Reason repository:
$query = $this->createQueryBuilder('r')->select('distinct r.reason')
->innerJoin('r.households', 'h')
Now that there's the plural households, how to I specify a relationship to a singular household?
Took a while, but the following works:
$query = $this->createQueryBuilder('r')->select('distinct r.reason')
->innerJoin('r.households', 'h')
->innerJoin('TruckeeProjectmanaBundle:Contact', 'c', 'WITH c.household = h.household')
->where('c.contactDate >= :startDate')
->andWhere('c.contactDate <= :endDate')
->andWhere('r.enabled = TRUE')
->orderBy('r.id')
->setParameters($dateCriteria)
->getQuery()
->getResult();
I'm trying to build a innerJoin query using Doctrine2/QueryBuilder.
$repo = $this->getDoctrine()
->getRepository('MyBundle:Models');
$query = $repo->createQueryBuilder('m')
->where('m.id = :id')
->setParameter('id', $id);
Doctrine says:
A join always belongs to one part of the from clause. This is why you
have to specify the alias of the FROM part the join belongs to as the
first argument.
As a second and third argument you can then specify the name and alias
of the join-table and the fourth argument contains the ON clause.
Ex.
$queryBuilder
->innerJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
What I can't understand is that 'phonenumbers' table is referencing to Entity Name or DB Table Name.
What I actually want is, is there any way to explicitly refer to entity like
innerJoin('u', 'MyBundle:phonenumbers', 'p', 'u.id = p.user_id')?
It's a bit confusing when it joins just like that. Can please someone explain that to me?
Help!!
You are working on a DQL level with tables, which means that you actually joining a table, so you just need the table name, not the entity name. The table "phonenumbers might not even had an entity to begin with, this is why Doctrine requests a table name and not an entity name.
Edit
It is actually possible to work with entity names as well as follows (taken from my own code which is working as a charm):
$builder = $this->createQueryBuilder('m');
$builder->innerJoin(
'YourBundle:Category',
'c',
Join::WITH,
$builder->expr()->eq('m.id', 'c.mdl_id')
);
To use the constants from Join you should first:
use Doctrine\ORM\Query\Expr\Join;
But this should also work (taken from documentation which means should work like a charm):
$queryBuilder
->select('id', 'name')
->from('users', 'u')
->innerJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
This is taken form: http://doctrine-orm.readthedocs.org/projects/doctrine-dbal/en/latest/reference/query-builder.html#join-clauses
In fact, you're already referencing explicitely your entity phonenumbers in your second argument of your innerJoin function.
You read the doc and i'll retake it point by point with your request :
Argument 1 : alias of the FROM part, here your m (alias of your models)
Argument 2 : The full name of your entity you want join with your models
Argument 3 : an alias to access it (just like the 'm' of models)
Argument 4 : The join condition. (like the ON part of a sql request)
But with symfony and if you have a relation between your two entities like that :
//AppBundle/Entity/Models
class Models {
/**
* ...
**/
private $id;
/**
* ...
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Phone", inversedBy="models")
**/
private $phonenumbers;
...
}
you want to join you just can do :
$repo = $this->getDoctrine()
->getRepository('MyBundle:Models');
$query = $repo->createQueryBuilder('m')
->innerJoin('m.phonenumbers', 'p')
->where('m.id = :id')
->setParameter('id', $id);
To explain that : You just have to pass as first argument, the entity property you want to join (here the phone number of your model) and define it as alias (p for phonenumber to access it in a select)
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.