DQL joining related entities - symfony

Hi have some entities relateds and need to define a dql query to obtain an entity.
MAIN ENTITY
class proyectosSubsecciones
{
...
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="app\SubseccionesBundle\Entity\Subsecciones")
* #ORM\JoinColumn(name="id_subseccion", referencedColumnName="id")
*/
private $subseccion;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="app\ProyectosBundle\Entity\Proyectos")
* #ORM\JoinColumn(name="id_proyecto", referencedColumnName="id")
*/
private $proyecto;
....
RELATED ENTITY
class subsecciones
{
...
/**
* #ORM\ManyToOne(targetEntity="app\SeccionesBundle\Entity\Secciones")
* #ORM\JoinColumn(name="id_seccion", referencedColumnName="id",nullable=false)
*/
private $seccion;
...
I need to obtain the distinct entities of type "app\SeccionesBundle\Entity\Secciones" from each "app\ProyectosBundle\Entity\Proyectos"
I´m trying a query like:
$consulta=$em->createQuery('
SELECT DISTINCT sc
FROM ProyectosSubseccionesBundle:ProyectosSubsecciones p
JOIN p.subseccion s WITH s.id=p.subseccion
JOIN s.seccion sc WITH sc.id=s.seccion
WHERE p.proyecto= :id
');
$consulta->setParameter('id', $id_proyecto);
$subsecciones=$consulta->getResult();
I get an error that says:
"Cannot select entity through identification variables without choosing at least one root entity alias"
But I only need the data from sc.Any idea??

Use query builder in ProyectosSubseccionesRepository:
return $this->createQueryBuilder('p')
->join('p.subseccion', 's', Join::WITH, 's = p.subseccion')
->join('s.seccion', 'sc', Join::WITH, 'sc = s.seccion')
->where('p.proyecto = :id')
->setParameter('id', $id)
->getQuery()
->execute()

For your problem i assume you have defined bidirectional relationship among your entities.
like
Entity RelationType ReferenceEntity Reference
==========================================================================================
ProyectosSubsecciones ManyToOne Subsecciones $subseccion
ProyectosSubsecciones ManyToOne Proyectos $proyecto
Proyectos OneToMany ProyectosSubsecciones $proyectosSubsecciones
Subsecciones OneToMany ProyectosSubsecciones $proyectosSubsecciones
Subsecciones ManyToOne Secciones $seccion
Secciones OneToMany Subsecciones $subsecciones
Considering above bidirectional definitions you can write your DQL as
SELECT DISTINCT s
FROM Secciones s
JOIN s.subsecciones ss
JOIN ss.proyectosSubsecciones pss
JOIN pss.proyecto
WHERE p.id = :id
The above query will select Secciones entity and join with Subsecciones entity using property $subsecciones defined in Secciones entity.
Then query will join Subsecciones with ProyectosSubsecciones using property $proyectosSubsecciones defined in Subsecciones entity.
Finally it will ProyectosSubsecciones with Proyectos entity using $proyecto property defined in ProyectosSubsecciones entity and lastly it will apply a filter according your WHERE clause.
Note there is no need to use WITH clause to join your entities because, In DQL, joining part will be covered by properties which you define as OneToMany/ManyToOne or ManyToMany WITH is used for if there is no relation mapping defined between entities or if you want to add another filter on joining criteria like ON(a.id = b.some_id AND/WITH some = some)

Related

How to get the join ID when using Doctrine?

I am working on a project of my company. However, I am not familiar with Doctrine. I am the old-styled query-guy.
Table-A and Table-B is in one-to-many relation, linking up by "a_id" on Table-B. In the Entity-B, $a_name is specified.
Table-A
a_id
a_name
a_attr
Table-B
b_id
a_id
b_name
b_attr
Entity-B
/**
* #ORM\ManyToOne(targetEntity="EntityA")
* #ORM\JoinColumn(name="a_id", referencedColumnName="a_id")
* #var Timezone
*/
protected $a_name;
Now I am writing a method to get a set of records using IN()
/**
* #param array $ids
*
* #return array
*/
public function getByIds($ids) {
$query = $this->getEntityManager()->createQuery('SELECT t FROM Entity-B t INDEX BY t.id WHERE t.id IN (:ids)');
}
The above line "INDEX" and "WHERE" with the Entity-B ID. How can I "INDEX" and "WHERE" with Entity-A's ID (a_id on Table-B)?
Thanks.
Try this or similar for your DQL:
SELECT a.a_id, t.b_id, t.b_name, t.b_attr FROM Entity-B t LEFT JOIN t.a_name a INDEX BY a.id WHERE a.a_id IN (:ids)
We just add a join to Entity-A, include a.id in the select and then index by a.id.
As we working with entities in doctrine, we need to join the entity to get at its id to then index by it. Also, the naming of your properties within each entity could be simpler and more intuitive. So rather than Entity-A (a) having properties a_id, a_attr, etc, just use id, attribute, etc. I assume you just generalised your code for the question and you probably have nicer property names in your project.
Let me know how that DQL works out for you.

Symfony Doctrine order oneToMany by specific order

I use Symfony 4, and I have a OneToMany relation. I want to order this relation by a specific order. For now, I order only by ASC :
/**
* #ORM\OneToMany(targetEntity="App\Entity\Ingredients", mappedBy="product", fetch="EAGER")
* #ORM\OrderBy({"family" = "ASC"})
*/
private $ingredients;
It works perfectly.
Now I would like to order by a specific order : "vegetables", "fruits" then "meat".
I tried :
/**
* #ORM\OneToMany(targetEntity="App\Entity\Ingredients", mappedBy="product", fetch="EAGER")
* #ORM\OrderBy({"family" = "vegetables, fruits, meat"})
*/
private $ingredients;
Which is of course doesn't work. Still trying, but is there an easy way to achieve it ?
As noted in https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html#annref_orderby
The DQL Snippet in OrderBy is only allowed to consist of unqualified, unquoted field names and of an optional ASC/DESC positional statement. Multiple Fields are separated by a comma (,). The referenced field names have to exist on the targetEntity class of the #ManyToMany or #OneToMany annotation.
However if you use DQL you should be able to accomplish this with something like:
$qb = $em->getRepository(Ingredients::class)->createQueryBuilder('i');
$results = $qb->orderBy('FIELD(family,vegetables,fruits,meat)')
->getQuery()
->getResults();
** I havent tested this code **

Join from inversed side

given the two following intities:
<?php
/**
* User
* #ORM\Entity()
*/
class User implements AdvancedUserInterface, \Serializable, EncoderAwareInterface
{
/**
* #var Vip
* #ORM\OneToOne(targetEntity="Vip", mappedBy="user", fetch="EAGER")
*/
protected $vip;
// …
<?php
/**
* Vip
* #ORM\Entity()
*/
class Vip
{
/**
* #ORM\id #ORM\OneToOne(targetEntity="User", inversedBy="vip", fetch="EAGER")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
protected $user;
// …
SHORT :
How can I do this SQL in DQL given above entities:
SELECT u.firstName, v.foo FROM User join Vip v ON v.user_id = u.id
In other words how can I retrieve 10 first users ( with their VIP infos if it exists), using DQL join in such a way that only one SQL query will be generated by Doctrine. Is that possible ?
Long story:
The owning side is the Vip entity because it holds the reference/foreign key to a User underneath in the database.
I am trying to retrieve all User with their Vip datas.
Using the knplabs/knp-paginator-bundle, I first set up a simple query:
$dql = "SELECT u, p FROM AppBundle:User u;
In spite of enforcing the fetch attribute as « EAGER », Vip infos where not part of the initial query. As a consequence, calling getter getVip() on each iteration from inside the twig for in loop like
{% for user in pagination %}
{% if user.getVip() %}
<span class="label label-warning">V.I.P</span>
{% endif %}
{{% endfor %}}
.. caused a query to be issued on each iteration !
The Symfony dev bar shows 6 queries:
DQL documentation and says that one can use JOIN keyword. So my query became:
$dql = "SELECT u, v FROM AppBundle:User u JOIN u.vip v;
But now I get this error:
Warning: spl_object_hash() expects parameter 1 to be object, null
given
Here I'm stuck, wondering how I could fetch Vip datas (or null) along with User datas, in a single query.
In other words how can I retrieve 10 first users ( with their VIP
infos if it exists), using DQL join in such a way that only one SQL
query will be generated by Doctrine. Is that possible ?
You should initialize all related entities using select clause to avoid additional queries when accessing to the related objects.
$repository = $em->getRepository(User::class);
$users = $repository->createQueryBuilder('u')
->addSelect('v') // Initialize Vip's
->join('u.vip', 'v')
->getQuery()
->setMaxResults(10)
->getResult();
Yes, one may add associated entities to the SELECT statement.
But more precisely, one should only add relations that are really involved in the expected result , in other words, entities fetched as "EAGER".
I realized that the vip entity had another relation (oneToMany with a vehicule entity). I just want to retrieve users with their vip metas. Adding another JOIN to the query would just bring more datas since I would not use vehicules anyway (and issue extra work behind the scenes).
-> So I simply changed the fetch attribute from "EAGER" to "LAZY" in the vip OneToMany declaration.
To conclude:
Ask yourself «what are involved intities ?», should it be part
of the result (do you simply need those infos).
if NO, you might turn fetch attribute to "[EXTRA_]LAZY" in the relation declaration like
/**
* #ORM\OneToMany(targetEntity="Vehicule", mappedBy="vip", fetch="LAZY", …)
*/
protected $vehicules;
if YES you will have to select those entities in your query.
Using DQL:
SELECT u, v, w FROM AppBundle:User u LEFT JOIN u.vip v LEFT JOIN v.vehicules w
Using queryBuilder:
$repository = $em->getRepository(User::class);
$users = $repository->createQueryBuilder('u')
->addSelect('v')
->join('u.vip', 'v')
->addSelect('w')
->join('v.vehicules', 'w')
// …

Doctrine Query Builder for entities with Join Table

User ---[OneToMany]---> AcquiredSkill ---[ManyToOne]---> Skill
I'm having problems with creating a query using query builder in doctrine. Can someone help me convert this mysql query to a doctrine query using query builder?
SELECT u.*, s.*
FROM `user` u
Join `acquired_skill` ac ON ac.user_id = u.user_id
Join `skill` s ON ac.skill_id = s.skill_id
Tables
user
- user_id
- name
skill
- skill_id
- skill_name
acquired_skill
- as_id
- skill_id
- user_id
So far, this is my query but it lacks the join between the skills.
createQueryBuilder('u')
->select('u.user_id', 'u.name')
->getQuery()
->getResult();
Your relation seems like you have many-to-many association between user and skills you can set your entities to setup this relation like user entity will point to skill entity in a many-to-many way
User Entity
/**
* #ORM\ManyToMany(targetEntity="Namespace\YourBundle\Entity\Skill", cascade={"persist"})
* #ORM\JoinTable(name="acquired_skill",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="skill_id", referencedColumnName="id")}
* )
*/
private $skills;
Skill Entity
/**
*
* #ORM\ManyToMany(targetEntity="Namespace\YourBundle\Entity\User", mappedBy="skills")
*/
private $user;
Now in query builder you can join your user entity with skill like below
$this->createQueryBuilder('u')
->select('u')
->innerJoin('u.skills','s')
->getQuery()
->getResult();
for further clarification see docs 22.2.19. #ManyToMany
$qb = $this->entity_manager->createQueryBuilder('u');
$qb->select("*");
$qb->innerJoin('Namespace\YourBundle\Entity\Aq_skill', 'ac', 'WITH', 'ac.user_id = u.user_id');
$qb->innerJoin('Namespace\YourBundle\Entity\skill', 's', 'WITH', 'ac.skill_id = s.skill_id');
$result = $qb->getQuery()->getArrayResult();

Doctrine says a field doesn't exist when it does - Symfony2

I have an entity:
...
class UserRole extends Role
{
/**
* #ORM\Column(name="user_role_id", type="integer")
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
But when I try to use user_role_id in a doctrine query I get this error:
[Semantical Error] line 0, col 42 near 'user_role_id': Error: Class
MyApp\Model\UserRole has no field or association named user_role_id
The query I am using is this:
$query = $this->getEntityManager()
->createQuery('SELECT r FROM Model:UserRole AS r WHERE r.user_role_id IN (:roles)')
->setParameter('roles', array_values($roles));
I've definitely got a user_role_id field as I can see it in phpMyAdmin.
Does anyone have any idea why doctrine is not recognising it?
This is doctrine and you want to write a DQL not a SQL, and you need to use the field names as defined in entities not the column names.
So in your query just use r.id instead of r.user_role_id
Take a look at DQL in Doctrine

Resources