Doctrine ORM Join for ManyToMany relationships - join

I have the User entity:
class User extends Entity implements UserInterface, ProviderInterface
{
/**
* #var int
* #ORM\Id
* #ORM\Column(name="user_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Application\Entity\Child", mappedBy="parents")
*/
protected $children;
...
}
And the Child entity:
class Child extends Entity
{
/**
* #var int
* #ORM\Id
* #ORM\Column(name="child_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var \Application\Entity\Classroom
*
* #ORM\ManyToOne(targetEntity="Application\Entity\Classroom")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="classroom_id", referencedColumnName="classroom_id")
* })
*/
protected $classroom;
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Application\Entity\User", inversedBy="children")
* #ORM\JoinTable(name="child_parent",
* joinColumns={#ORM\JoinColumn(name="child_id", referencedColumnName="child_id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="user_id")}
* )
*/
protected $parents;
...
}
What I need to do is to add in the UserRepository a condition for obtaining the users whose children are in the classroom X. I haven't seen any example on making join clauses for ManyToMany relationships. Have you got any idea or good example?
Thanks in advance.

The most straightforward way would be using a DQL query, I think this one should be close to what you want:
SELECT u
FROM Application\Entity\User u
JOIN u.children ch
JOIN c.classroom cr
WHERE cr.id = :classroom
GROUP BY u
Hope this helps!

Related

Swagger ui endpoints are duplicated

I am using the wonderful project api platform. I create my entities and endpoints are working like a charm.
Right now, I want to configure the documentation because endpoints are showed duplicated. For example, I have a relation 1 to N between two entities and on documentation which is automatically generated via swagger the endpoints are duplicated.
For example, in this relation between competition and event, I have the same endpoint [/competitions/{id}/events] for each entity.
Do you know if is there any way to show endpoints once? It is not a big deal, but I want to keep the documentation as clean as possible.
Edited
Competition:
/**
* Competitions able to request.
*
* #ApiResource(
* attributes={
* "normalization_context"={"groups"={"read"}}
* },
* collectionOperations={"get", "post"},
* itemOperations={"get"}
* )
* #ORM\Entity
*/
class Competition
{
/**
* #var int The competition Id
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read","write"})
*/
private $id;
/**
* #var string Name of the competition
*
* #ORM\Column
* #Assert\NotBlank
* #Groups({"read","write"})
*
*/
public $name = '';
/**
* #ORM\OneToMany(targetEntity="Event", mappedBy="competitions", cascade={"persist"})
* #ApiSubresource(maxDepth=1)
* #Groups("write")
*/
public $events;
Event:
/**
* Available event
*
* #ApiResource(
* attributes={
* "normalization_context"={"groups"={"read"}},
* "denormalization_context"={"groups"={"write"}}
* },
* collectionOperations={"get", "post"},
* itemOperations={
* "get"}
* )
* #ORM\Entity
*/
class Event
{
/**
* #var int The entity Id
*
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"read", "write"})
*/
private $id;
/**
* #var string The name of the event available.
*
* #ORM\Column(type="text")
* #Assert\NotBlank
* #Groups({"read","write"})
*/
public $name = '';
/**
* #var string Start date
*
* #ORM\Column(type="datetime")
* #Assert\NotBlank
* #Groups({"read","write"})
*/
public $start;
/**
* #ORM\ManyToOne(targetEntity="Competition", inversedBy="events")
* #Groups({"write"})
*/
public $competitions;
You could be able to disable some "operations":
https://api-platform.com/docs/core/operations
<?php
// api/src/Entity/Book.php
use ApiPlatform\Core\Annotation\ApiResource;
/**
* ...
* #ApiResource(
* collectionOperations={"get"},
* itemOperations={"get"}
* )
*/
class Book
{
// ...
}
To hide/show what you want.

2 How to join two entities in my project

I have a relation one-to-many between project and images and many-to-one between project and propriety. I would to select the procts ho have image.cover=1 and propriety.activated=1 but the code always give me all images for projects
class ProjectImage
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="BI\AdminBundle\Entity\Project", inversedBy="images")
* #ORM\JoinColumn(nullable=false, name="id_project", referencedColumnName="id")
*/
private $idProject;
******************
class Project
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="BI\AdminBundle\Entity\Promoteur", inversedBy="projects")
* #ORM\JoinColumn(nullable=false, name="id_promoteur", referencedColumnName="id")
*/
private $idPromoteur;
/**
*
* #ORM\ManyToOne(targetEntity="BI\AdminBundle\Entity\Governorate")
* #ORM\JoinColumn(nullable=false, name="id_governorate")
*/
private $idGovernorate;
/**
*
* #ORM\ManyToOne(targetEntity="BI\AdminBundle\Entity\Delegation")
* #ORM\JoinColumn(nullable=true, name="id_delegation")
*/
private $idDelegation;
/**
* #ORM\ManyToOne(targetEntity="BI\AdminBundle\Entity\Locality")
* #ORM\JoinColumn(nullable=true, name="id_locality")
*/
private $idLocality;
/**
* #ORM\OneToMany(targetEntity="BI\AdminBundle\Entity\ProjectImage", mappedBy="idProject",cascade={"persist", "remove", "merge"})
*/
private $images;
**********************************
class Promoteur
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity = "BI\AdminBundle\Entity\Project", mappedBy = "idPromoteur")
*/
private $projects;
On your OneToMany associations, you should use an ArrayCollection since you store can several instances of your target entity on this end. You should also import the ArrayCollection class and instanciate this collection in your constructor.
So in your Project class you may want to add something like this:
use Doctrine\Common\Collections\ArrayCollection;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="BI\AdminBundle\Entity\ProjectImage",
mappedBy="idProject",cascade={"persist", "remove", "merge"})
*/
private $images;
public function __construct() {
$this->images = new ArrayCollection();
}
Then you can generate the getters and setters with app/console doctrine:generate:entities. This command will also create two additional methods addImage and removeImage to manage your collection.
Hope this will helps

Doctrine2 - what DQL to use?

The first entity called BUNDLE
/**
* #var \Doctrine\Common\Collections\Collection of \Akademie\Course\Course
* #ManyToMany(targetEntity="Akademie\Course\Course", mappedBy="bundles")
*/
private $courses;
The second entity is called COURSE
/**
* #var \Doctrine\Common\Collections\Collection of \Akademie\Bundle\Bundle
* #ManyToMany(targetEntity="Akademie\Bundle\Bundle", inversedBy="courses")
*/
private $bundles;
/**
* #var \Doctrine\Common\Collections\Collection of \Akademie\Course\CourseDate
* #OneToMany(targetEntity="Akademie\Course\CourseDate", mappedBy="course")
*/
private $courseDates;
/**
* #var int
* #Column(type="boolean")
*/
private $hidden;
and the third is called COURSEDATE
/**
* #var \Akademie\Course\Course
* #ManyToOne(targetEntity="Akademie\Course\Course", inversedBy="courseDates")
* #JoinColumn(nullable=false)
*/
private $course;
/**
* #var \DateTime
* #Column(type="datetimetz", nullable=true)
*/
private $beginDate;
I have parameter course and I need to get all bundles, which contains this course. What's more, all other courses in that bundle has to have courseDate newer than current date and can't be hidden. Otherwise I don't want to get this bundle. I hope it is clear now...
In a repository (bundle of entity BUNDLE for example), you have to create a custom method :
public function myMethod($course){
$qb = $this->createQueryBuilder("b")
->join("b.course", "c")
->join("c.courseDates", "cd")
->Where("c = :course")
->andWhere(" cd.beginDate > :now ")
->andWhere("c.hidden = 1")// if 1 means not hidden
->setParameter(":course", $course)
->setParameter(":now", new \DateTime('now'));
And then, in your controller, you have to do something like that :
$bundles = $this->getDoctrine()->getManager()->getRepository('namespace of your BUNDLE entity')->myMethod($course);

Doctrine 2, how to get a property in a left join result set? [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
Symfony 2, Doctrine 2.1.
I've got 3 entities, one of them intermediate (join table). Let's say it's SomeObject, SomeProperty and ObjectProperties.
Problem: I can't get the value of SomeProperty 'name' property. Here's the code:
[...]
class SomeObject
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #var ObjectProperties $objectProperties
*
* #ORM\OneToMany(targetEntity="ObjectProperties", mappedBy="object_id", cascade={"all"})
*/
private $objectProperties;
[...]
[...]
class SomeProperty
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\OneToMany(targetEntity="ObjectProperties", mappedBy="property_id", cascade={"all"})
*/
private $id;
/**
* #var string $name
*
*/
private $name;//I NEED TO GET VALUE OF $name
[...]
[...]
class ObjectProperties
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="SomeObject", inversedBy="id", cascade={"all"})
* #ORM\JoinColumn(name="object_id", referencedColumnName="id")
*/
private $object_id;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="SomeProperty", inversedBy="id", cascade={"all"})
* #ORM\JoinColumn(name="property_id", referencedColumnName="id")
*/
private $property_id;
[...]
Setters and getters as usual.
In my controller I've got something along the lines of:
$entity = $em->getRepository('SomeTestBundle:SomeObject')->find($id);
[...]
$props = $entity->getObjectProperties();
foreach ($props as $prop){
echo '---------------------------<br>';
var_dump($prop->getPropertyId()->getName());
}
Now, getName() gives me null result, but if i replace it with getId it works as expected. Same if I try to use it in a form class. Is it because there is no doctrine association between SomeProperty->name and ObjectProperties?
I think I don't quite get the way doctrine is supposed to work. I thought that only property_id and object_id are needed in a junction table, otherwise it doesn't make much sense for me, because SomeProperty serves as a dictionary table, so I could change SomeProperty->name in one place.
I'm seriously stuck with it. Is it some sort of configuration option that is lacking or am I generally not getting the bigger picture?
Consider it a typo - I've made it work by adding missing #Column annotation in SomeProperty class:
/**
* #var string $name
* #ORM\Column(type="string")
*/
private $name;
Don't know why I missed it in the first place... I guess "Experience is something you get, after you need it."

doctrine2 ArrayCollection error

I am trying to follow the advice from the doctrine docs on this page - initialising a class member with the ArrayCollection. All works well for the example given in the docs. I'm trying to do it with an inherited class but get an error saying:
Class Doctrine\Common\Collections\ArrayCollection is not a valid entity or mapped super class
Inherited class:
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"user" = "App_User", "group" = "App_Group"})
*/
abstract class App_Acl_Role_Abstract implements Zend_Acl_Role_Interface {
/**
* #ManyToOne(targetEntity="App_Acl_Role_Abstract", inversedBy="children", cascade={"persist"})
*/
private $parents;
/**
* #OneToMany(targetEntity="App_Acl_Role_Abstract", mappedBy="parents", cascade={"persist"})
*/
private $children;
public function __construct()
{
$this->parents = new Doctrine\Common\Collections\ArrayCollection();
$this->children = new Doctrine\Common\Collections\ArrayCollection();
}
}
Inheriting class:
/**
* #Entity
* #Table(name="App_User")
*/
class App_User extends App_Acl_Role_Abstract
{
...
}
When I move the constructor to the inheriting class all works fine. But it would be much neater to have them in the inherited abstract class. Why does it not work? Is it possible?
My bad. I stuffed up the mapping. This is the mapping I'm now using:
/**
* #ManyToMany(targetEntity="App_Acl_Role_Abstract", cascade={"persist"})
* #JoinTable(name="role_parents",
* joinColumns={#JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="parent_id", referencedColumnName="id", unique=true)}
* )
*/
private $parents;
/**
* #ManyToMany(targetEntity="App_Acl_Role_Abstract", cascade={"persist"})
* #JoinTable(name="role_children",
* joinColumns={#JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="child_id", referencedColumnName="id", unique=true)}
* )
*/
private $children;
A role should be able to have many parents and many children

Resources