Swagger ui endpoints are duplicated - swagger

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.

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

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

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

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."

Resources