2 How to join two entities in my project - join

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

Related

API-platform POST operation swagger page empty

Symfony 3.4.10
Api-platform/core
I set up API-Platform/core on a Symfony 3.4.10 project, everything work well and I can access the swagger page for test via "/api" (url route to show api test interface).
For the GET operations everything is right.
But for the POST operation on an entity named "Trajet" (see code) the "example value" show only "{}" and nothing else. If I try the button "Try it out" I get a blank space where nothing is shew instead of the traditional form to test a post operation (see capture).
capture of the swagger page:
I don't understand what I missed. Could someone please give me ideas how to fix it and where the problem can come from. I'm out of ideas to find a solution.
Here is the code of my entity Trajet for which post operation is empty and seem to be not usable.
<?php
namespace Covoituragesimple\AnnonceBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* Trajet
* #ApiResource(
* collectionOperations={"get", "post"},
* itemOperations={"get"},
* attributes={
* "normalization_context"={"groups"={"read"}},
* "denormalization_context"={"groups"={"write"}}
* }
* )
* #ORM\Table(name="trajet")
* #ORM\Entity(repositoryClass="Covoituragesimple\AnnonceBundle\Repository\TrajetRepository")
*/
class Trajet
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Groups({"read"})
*/
private $id;
/**
* #var int
*
* #ORM\Column(name="numberofseats", type="integer")
* #Assert\NotBlank()
* #Groups({"read"})
*/
private $numberofseats;
/**
* #var \DateTime
*
* #ORM\Column(name="creationdate", type="datetime")
* #Assert\Type("\DateTime")
* #Groups({"read"})
*/
private $creationdate;
/**
* #var \DateTime
*
* #ORM\Column(name="rdvdatetime", type="datetime")
* #Assert\Type("\DateTime")
* #Groups({"read"})
*/
private $rdvdatetime;
/**
* #ORM\ManyToOne(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Commune")
*/
private $commune;
/**
* #var string
*
* #ORM\Column(name="chercheoupropose", type="text")
* #Groups({"read"})
*/
private $chercheoupropose;
/**
* #var string
*
* #ORM\Column(name="message", type="text")
* #Assert\NotBlank(
* message="Un petit message pour votre annonce"
* )
* #Groups({"read"})
*/
private $message;
/**
* #var string
*
* #ORM\Column(name="contrepartietype", type="text")
* #Groups({"read"})
*/
private $contrepartietype;
/**
* #ORM\ManyToOne(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Module")
* #Groups({"read"})
*/
protected $module;
/**
* #var string
*
* #ORM\Column(name="contrepartiemsg", nullable=true, type="text")
* #Groups({"read"})
*/
private $contrepartiemsg;
/**
* #ORM\ManyToOne(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Covoitureur",
inversedBy="trajets",
cascade={"persist"})
*/
protected $covoitureur;
/**
* #ORM\OneToMany(targetEntity="Covoituragesimple\AnnonceBundle\Entity\Contact", mappedBy="trajet", cascade={"remove"})
*/
private $contacts;
/**
* #var bool
*
* #ORM\Column(name="moderated", type="boolean")
*/
private $moderated = false;
/**
* #var bool
*
* #ORM\Column(name="active", type="boolean")
*/
private $active = true;
/**
* Constructor
*/
public function __construct()
{
$this->setNumberofseats(1);
$this->setMessage("");
$this->setRdvdatetime(new \DateTime('today'));
$this->setCreationdate(new \DateTime('now'));
$this->contacts = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set numberofseats.
*
* #param int $numberofseats
*
* #return Trajet
*/
public function setNumberofseats($numberofseats)
{
$this->numberofseats = $numberofseats;
return $this;
}
/**
* Get numberofseats.
*
* #return int
*/
public function getNumberofseats()
{
return $this->numberofseats;
}
/**
* Set creationdate.
*
* #param \DateTime $creationdate
*
* #return Trajet
*/
public function setCreationdate($creationdate)
{
$this->creationdate = $creationdate;
return $this;
}
/**
* Get creationdate.
*
* #return \DateTime
*/
public function getCreationdate()
{
return $this->creationdate;
}
/**
* Set rdvdatetime.
*
* #param \DateTime $rdvdatetime
*
* #return Trajet
*/
public function setRdvdatetime($rdvdatetime)
{
$this->rdvdatetime = $rdvdatetime;
return $this;
}
/**
* Get rdvdatetime.
*
* #return \DateTime
*/
public function getRdvdatetime()
{
return $this->rdvdatetime;
}
/**
* Set chercheoupropose.
*
* #param string $chercheoupropose
*
* #return Trajet
*/
public function setChercheoupropose($chercheoupropose)
{
$this->chercheoupropose = $chercheoupropose;
return $this;
}
/**
* Get chercheoupropose.
*
* #return string
*/
public function getChercheoupropose()
{
return $this->chercheoupropose;
}
/**
* Set message.
*
* #param string $message
*
* #return Trajet
*/
public function setMessage($message)
{
$this->message = $message;
return $this;
}
/**
* Get message.
*
* #return string
*/
public function getMessage()
{
return $this->message;
}
/**
* Set contrepartietype.
*
* #param string $contrepartietype
*
* #return Trajet
*/
public function setContrepartietype($contrepartietype)
{
$this->contrepartietype = $contrepartietype;
return $this;
}
/**
* Get contrepartietype.
*
* #return string
*/
public function getContrepartietype()
{
return $this->contrepartietype;
}
/**
* Set contrepartiemsg.
*
* #param string|null $contrepartiemsg
*
* #return Trajet
*/
public function setContrepartiemsg($contrepartiemsg = null)
{
$this->contrepartiemsg = $contrepartiemsg;
return $this;
}
/**
* Get contrepartiemsg.
*
* #return string|null
*/
public function getContrepartiemsg()
{
return $this->contrepartiemsg;
}
/**
* Set moderated.
*
* #param bool $moderated
*
* #return Trajet
*/
public function setModerated($moderated)
{
$this->moderated = $moderated;
return $this;
}
/**
* Get moderated.
*
* #return bool
*/
public function getModerated()
{
return $this->moderated;
}
/**
* Set active.
*
* #param bool $active
*
* #return Trajet
*/
public function setActive($active)
{
$this->active = $active;
return $this;
}
/**
* Get active.
*
* #return bool
*/
public function getActive()
{
return $this->active;
}
/**
* Set module.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Module|null $module
*
* #return Trajet
*/
public function setModule(\Covoituragesimple\AnnonceBundle\Entity\Module $module = null)
{
$this->module = $module;
return $this;
}
/**
* Get module.
*
* #return \Covoituragesimple\AnnonceBundle\Entity\Module|null
*/
public function getModule()
{
return $this->module;
}
/**
* Set covoitureur.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Covoitureur|null $covoitureur
*
* #return Trajet
*/
public function setCovoitureur(\Covoituragesimple\AnnonceBundle\Entity\Covoitureur $covoitureur = null)
{
$this->covoitureur = $covoitureur;
return $this;
}
/**
* Get covoitureur.
*
* #return \Covoituragesimple\AnnonceBundle\Entity\Covoitureur|null
*/
public function getCovoitureur()
{
return $this->covoitureur;
}
/**
* Add contact.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Contact $contact
*
* #return Trajet
*/
public function addContact(\Covoituragesimple\AnnonceBundle\Entity\Contact $contact)
{
$this->contacts[] = $contact;
return $this;
}
/**
* Remove contact.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Contact $contact
*
* #return boolean TRUE if this collection contained the specified element, FALSE otherwise.
*/
public function removeContact(\Covoituragesimple\AnnonceBundle\Entity\Contact $contact)
{
return $this->contacts->removeElement($contact);
}
/**
* Get contacts.
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getContacts()
{
return $this->contacts;
}
/**
* Set commune.
*
* #param \Covoituragesimple\AnnonceBundle\Entity\Commune|null $commune
*
* #return Trajet
*/
public function setCommune(\Covoituragesimple\AnnonceBundle\Entity\Commune $commune = null)
{
$this->commune = $commune;
return $this;
}
/**
* Get commune.
*
* #return \Covoituragesimple\AnnonceBundle\Entity\Commune|null
*/
public function getCommune()
{
return $this->commune;
}
}
I think it's because you forget to define "write" group in your entity attribute, if you rename it by "read" in denormalization_context (or add "write" group in your entity attributes), it should work.
Something like this:
* #ApiResource(
* collectionOperations={"get", "post"},
* itemOperations={"get"},
* attributes={
* "normalization_context"={"groups"={"read"}},
* "denormalization_context"={"groups"={"read"}} // <-- here
* }
* )
More detail here: https://api-platform.com/docs/core/serialization
I hope this can help you

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.

Doctrine ORM Join for ManyToMany relationships

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!

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);

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