API Platform: Hide properties in /api/contexts/<entity> - symfony

When working with the API platform (Symfony) I only expose some of my entity's properties to the API. That works pretty good with groups, the output DTO and DataTransformer. When opening the API under /api/<entity> I get the results with only the defined/grouped properties.
However when having a opening the contexts under /api/contexts/<entity> ALL properties are listed. How can I reduce those properties as well?
Symfony Version: 6
Api Platform Version: 2.6.8
Entity:
/**
* #ORM\Entity(repositoryClass=EntityRepository::class)
* #ORM\HasLifecycleCallbacks()
* #ApiResource(
* normalizationContext={"groups" = {"api:read"}},
* collectionOperations={"get"},
* itemOperations={"get"},
* order={"title"="ASC"},
* paginationEnabled=false,
* output=EntityApiOutput::class
* )
*/
class Entity
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"api:read"}) */
private $id;
// other properties without '#Groups'
EntityApiOutput:
class EntityApiOutput
{
/** #Groups({"api:read"}) */
public int $id;
/** #Groups({"api:read"}) */
public string $title;
}

Related

SYMFONY, API PLATFORM how to add edit and show links to the serialized object

I'm working with SYMFONY and API PLATFORM to create REST API.
I have a Project Entity as an API Resource :
class Project
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $reference;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Gedmo\Slug(fields={"reference"})
*/
private $slug;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="create")
*/
private $createdAt;
/**
* #ORM\Column(type="datetime")
* #Gedmo\Timestampable(on="update")
*/
private $updatedAt;
/**
* #ORM\ManyToOne(targetEntity=User::class, inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity=Type::class, inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity=Status::class, inversedBy="projects")
* #ORM\JoinColumn(nullable=false)
*/
private $status;
With postman i get :
How can i add edit and show route to get a serialized object like this :
"hydra:member": [
{
...
"status": "/api/statuses/6",
"edit": "<a href='link_to_edit'>edit</a>", // add a link to edit
"show": "<a href='link_to_show'>show</a>" // add a link to show
},
knowing that i don't want to add edit and show to the entity properties or mapped them
Thanks for the help
Technically, you already have your edit and show routes (if you didn't customize them) : you only have to make a PUT or GET request to the value of the #id field of each object.
If you want to add an extra property to your entity, that isn't mapped you can do something like this :
/**
* #SerializedName("edit_route")
*
* #Groups({"projects:read"}))
*
* #return string
*/
public function getEditRoute()
{
return 'your_edit_route';
}
I wouldn't return HTML in this kind of field though, especially if your route is anything else than GET, and apps that use you API might not use HTML, so you're better off returning the simplest value and letting them do their thing with it.

Api Platform + Symfony define differents groups for get all and get single

I have an object control with a lot of properties encapsuled. When i make a GET request /controls/{id} to get just one control i want more properties than GET request /controls who return me all my controls.
/**
* #ApiResource(
* formats={"json"},
* forceEager=false,
* itemOperations={
* "get"={
* "normalizationContext"={
* "groups"={"control:read"}
* }
* }
* },
* normalizationContext={"groups"={"fullcontrol:read"}, "enable_max_depth"=true})
* #ORM\Entity(repositoryClass=ControlRepository::class)
*/
class Control
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #Groups({"control:read"})
*/
private $id;
/**
* #ORM\OneToMany(targetEntity=Appointment::class, mappedBy="control", orphanRemoval=true)
* #Groups({"control:read"})
*/
private $appointments;
/**
* #ORM\OneToMany(targetEntity=ObjectEntity::class, mappedBy="control", cascade={"persist", "remove"})
* #Groups({"fullcontrol:read"})
*/
private $objectEntity;
Here my #ApiRessource header to do that. In the doc they explains the most specific normalizationContext will be used but it always use the control:read group and never the fullcontrol:read group
Anyone know how to make it working ?

Symfony + JMSSerializer throw 500 - handleCircularReference

I'm trying to use the JMSSerializer with Symfony to build a simple json api.
So i have 2 simple Entities (1 User can have many Cars, each Car belongs to one User):
class Car
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="cars")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
}
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Car", mappedBy="user", orphanRemoval=true)
*/
private $cars;
}
Now i want to get all Cars with their User.
My Controller:
class CarController extends AbstractController
{
/**
* #param CarRepository $carRepository
*
* #Route("/", name="car_index", methods="GET")
*
* #return Response
*/
public function index(CarRepository $carRepository)
{
$cars = $carRepository->findAll();
$serializedEntity = $this->container->get('serializer')->serialize($cars, 'json');
return new Response($serializedEntity);
}
}
This will throw a 500 error:
A circular reference has been detected when serializing the object of
class \"App\Entity\Car\" (configured limit: 1)
Ok, sounds clear. JMS is trying to get each car with the user, and go to the cars and user ....
So my question is: How to prevent this behaviour? I just want all cars with their user, and after this, the iteration should be stopped.
You need to add max depth checks to prevent circular references.
This can be found in the documentation here
Basically you add the #MaxDepth(1) annotation or configure max_depth if you're using XML/YML configuration. Then serialize like this:
use JMS\Serializer\SerializationContext;
$serializer->serialize(
$data,
'json',
SerializationContext::create()->enableMaxDepthChecks()
);
Example Car class with MaxDepth annotation:
class Car
{
/**
* #\JMS\Serializer\Annotation\MaxDepth(1)
*
* [..]
*/
private $user;

Symfony 2 - Get additional fields in Many-to-Many relationship

I have a Many to Many relationship in Symfony 2. Diagram below.
http://i.stack.imgur.com/x6AYs.png
From Tema Entity i can get all Topico records related using this ORM definition
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="Application\RiesgoBundle\Entity\Topico", inversedBy="tema")
* #ORM\JoinTable(name="tema_topico",
* joinColumns={
* #ORM\JoinColumn(name="tema", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="topico", referencedColumnName="id")
* }
* )
*/
private $topico;
And using this method
/**
* Get topico
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTopico()
{
return $this->topico;
}
However, i don't know how to access to "impacto" and "ocurrencia" values stored on tema_topico table. Is there a way to do this using the Entity Manager?
On a ManyToMany association, you cant store extra fields. To achieve a "ManyToMany" with extra fields, you will have to do a OneToMany - ManyToOne. It will give you something like.
class Tema {
/*
* #ORM\OneToMany(targetEntity="temaTopico", mappedBy="tema")
*/
private $temaTopico;
}
class Topico {
/*
* #ORM\OneToMany(targetEntity="temaTopico", mappedBy="topico")
*/
private $temaTopico;
}
class TemaTopico {
/*
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="Tema", inversedBy="temaTopico")
*/
private $tema;
/*
* #ORM\Id()
* #ORM\ManyToOne(targetEntity="Topico", inversedBy="temaTopico")
*/
private $topico;
/*
* #ORM\Column(name="impacto", type="string")
*/
private $impacto;
}
see this and this

How to avoid dependency with entities from different bundles

I have several bundles in my app and I would like to have relations between tables.
One is my User(StoreOwner) which is in UserBundle, and the second is Store in StoreBundle.
The relation between them is OneToMany (User -> is owner of -> Store).
Store
/**
* Description of Store
*
* #ORM\Table(name="Store")
* #ORM\Entity(repositoryClass="Traffic\StoreBundle\Repository\StoreRepository")
* #author bart
*/
class Store extends StoreModel {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $name
*
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(
* message="Please provide your shop name"
* )
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\StoreOwner", inversedBy="stores")
*
*/
protected $owner;
}
StoreOwner
/**
* #ORM\Entity
*
*/
class StoreOwner extends User implements StoreOwnerInterface {
/**
* #var type ArrayCollection()
*
* #ORM\OneToMany(targetEntity="Traffic\StoreBundle\Entity\Store", mappedBy="owner", cascade={"persist"})
*/
protected $stores;
}
My question is:
Is there any solution to avoid dependency between StoreBundle and UserBundle and keep relations between Entities in Doctrine?
This is a valid concern in my opinion. Two-way dependencies between bundles are a smell.
One way of solving the dependency issue is moving your entities out of the bundles into a more general namespace. This way both bundles will depend on the same "library" but won't depend on each other directly.
I recently wrote a blog post on how to do it: How to store Doctrine entities outside of a Symfony bundle?

Resources