mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-03 13:33:48 +00:00
Merge remote-tracking branch 'origin/master' into fix-person-tests
This commit is contained in:
@@ -13,6 +13,9 @@ API
|
||||
|
||||
Chill provides a basic framework to build REST api.
|
||||
|
||||
Basic configuration
|
||||
*******************
|
||||
|
||||
Configure a route
|
||||
=================
|
||||
|
||||
@@ -34,7 +37,7 @@ You can also:
|
||||
* `How to create your custom normalizer <https://symfony.com/doc/current/serializer/custom_normalizer.html>`_
|
||||
|
||||
Auto-loading the routes
|
||||
***********************
|
||||
=======================
|
||||
|
||||
Ensure that those lines are present in your file `app/config/routing.yml`:
|
||||
|
||||
@@ -47,7 +50,7 @@ Ensure that those lines are present in your file `app/config/routing.yml`:
|
||||
|
||||
|
||||
Create your model
|
||||
*****************
|
||||
=================
|
||||
|
||||
Create your model on the usual way:
|
||||
|
||||
@@ -87,7 +90,7 @@ Create your model on the usual way:
|
||||
|
||||
|
||||
Configure api
|
||||
*************
|
||||
=============
|
||||
|
||||
Configure the api using Yaml (see the full configuration: :ref:`api_full_configuration`):
|
||||
|
||||
@@ -171,7 +174,7 @@ Configure the api using Yaml (see the full configuration: :ref:`api_full_configu
|
||||
}
|
||||
|
||||
The :code:`_index` and :code:`_entity` action
|
||||
=============================================
|
||||
*********************************************
|
||||
|
||||
The :code:`_index` and :code:`_entity` action are default actions:
|
||||
|
||||
@@ -189,7 +192,7 @@ Entity:
|
||||
Path: :code:`/api/1.0/person/accompanying-period/origin/{id}.{_format}`
|
||||
|
||||
Role
|
||||
====
|
||||
****
|
||||
|
||||
By default, the key `base_role` is used to check ACL. Take care of creating the :code:`Voter` required to take that into account.
|
||||
|
||||
@@ -216,7 +219,7 @@ You can also define a role for each method. In this case, this role is used for
|
||||
HEAD: MY ROLE_SEE
|
||||
|
||||
Customize the controller
|
||||
========================
|
||||
************************
|
||||
|
||||
You can customize the controller by hooking into the default actions. Take care of extending :code:`Chill\MainBundle\CRUD\Controller\ApiController`.
|
||||
|
||||
@@ -264,7 +267,7 @@ And set your controller in configuration:
|
||||
HEAD: true
|
||||
|
||||
Create your own actions
|
||||
=======================
|
||||
***********************
|
||||
|
||||
You can add your own actions:
|
||||
|
||||
@@ -361,8 +364,297 @@ Then, create the corresponding action into your controller:
|
||||
}
|
||||
}
|
||||
|
||||
Managing association
|
||||
********************
|
||||
|
||||
ManyToOne association
|
||||
=====================
|
||||
|
||||
In ManyToOne association, you can add associated entities using the :code:`PATCH` request. By default, the serializer deserialize entities only with their id and discriminator type, if any.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -X 'PATCH' \
|
||||
'http://localhost:8001/api/1.0/person/accompanying-course/2668.json' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
# see the data sent to the server: \
|
||||
-d '{
|
||||
"type": "accompanying_period",
|
||||
"id": 2668,
|
||||
"origin": { "id": 11 }
|
||||
}'
|
||||
|
||||
ManyToMany associations
|
||||
=======================
|
||||
|
||||
In OneToMany association, you can easily create route for adding and removing entities, using :code:`POST` and :code:`DELETE` requests.
|
||||
|
||||
Prepare your entity, creating the methods :code:`addYourEntity` and :code:`removeYourEntity`:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\Entity;
|
||||
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
|
||||
|
||||
/**
|
||||
* AccompanyingPeriod Class
|
||||
*
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="chill_person_accompanying_period")
|
||||
* @DiscriminatorMap(typeProperty="type", mapping={
|
||||
* "accompanying_period"=AccompanyingPeriod::class
|
||||
* })
|
||||
*/
|
||||
class AccompanyingPeriod
|
||||
{
|
||||
/**
|
||||
* @var Collection
|
||||
* @ORM\ManyToMany(
|
||||
* targetEntity=Scope::class,
|
||||
* cascade={}
|
||||
* )
|
||||
* @Groups({"read"})
|
||||
*/
|
||||
private $scopes;
|
||||
|
||||
public function addScope(Scope $scope): self
|
||||
{
|
||||
$this->scopes[] = $scope;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeScope(Scope $scope): void
|
||||
{
|
||||
$this->scopes->removeElement($scope);
|
||||
}
|
||||
|
||||
|
||||
Create your route into the configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
chill_main:
|
||||
apis:
|
||||
-
|
||||
class: Chill\PersonBundle\Entity\AccompanyingPeriod
|
||||
name: accompanying_course
|
||||
base_path: /api/1.0/person/accompanying-course
|
||||
controller: Chill\PersonBundle\Controller\AccompanyingCourseApiController
|
||||
actions:
|
||||
scope:
|
||||
methods:
|
||||
POST: true
|
||||
DELETE: true
|
||||
GET: false
|
||||
HEAD: false
|
||||
PUT: false
|
||||
PATCH: false
|
||||
roles:
|
||||
POST: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
|
||||
DELETE: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
|
||||
GET: null
|
||||
HEAD: null
|
||||
PUT: null
|
||||
PATCH: null
|
||||
controller_action: null
|
||||
path: null
|
||||
single-collection: single
|
||||
|
||||
This will create a new route, which will accept two methods: DELETE and POST:
|
||||
|
||||
.. code-block:: raw
|
||||
|
||||
+--------------+---------------------------------------------------------------------------------------+
|
||||
| Property | Value |
|
||||
+--------------+---------------------------------------------------------------------------------------+
|
||||
| Route Name | chill_api_single_accompanying_course_scope |
|
||||
| Path | /api/1.0/person/accompanying-course/{id}/scope.{_format} |
|
||||
| Path Regex | {^/api/1\.0/person/accompanying\-course/(?P<id>[^/]++)/scope\.(?P<_format>[^/]++)$}sD |
|
||||
| Host | ANY |
|
||||
| Host Regex | |
|
||||
| Scheme | ANY |
|
||||
| Method | POST|DELETE |
|
||||
| Requirements | {id}: \d+ |
|
||||
| Class | Symfony\Component\Routing\Route |
|
||||
| Defaults | _controller: csapi_accompanying_course_controller:scopeApi |
|
||||
| Options | compiler_class: Symfony\Component\Routing\RouteCompiler |
|
||||
+--------------+---------------------------------------------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
Then, create the controller action. Call the method:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\CRUD\Controller\ApiController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Chill\MainBundle\Entity\Scope;
|
||||
|
||||
class MyController extends ApiController
|
||||
{
|
||||
public function scopeApi($id, Request $request, string $_format): Response
|
||||
{
|
||||
return $this->addRemoveSomething('scope', $id, $request, $_format, 'scope', Scope::class, [ 'groups' => [ 'read' ] ]);
|
||||
}
|
||||
}
|
||||
|
||||
This will allow to add a scope by his id, and delete them.
|
||||
|
||||
Curl requests:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# add a scope with id 5
|
||||
curl -X 'POST' \
|
||||
'http://localhost:8001/api/1.0/person/accompanying-course/2868/scope.json' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"type": "scope",
|
||||
"id": 5
|
||||
}'
|
||||
|
||||
# remove a scope with id 5
|
||||
curl -X 'DELETE' \
|
||||
'http://localhost:8001/api/1.0/person/accompanying-course/2868/scope.json' \
|
||||
-H 'accept: */*' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"id": 5,
|
||||
"type": "scope"
|
||||
}'
|
||||
|
||||
Deserializing an association where multiple types are allowed
|
||||
=============================================================
|
||||
|
||||
Sometimes, multiples types are allowed as association to one entity:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
class Resource
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=ThirdParty::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private $thirdParty;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=Person::class)
|
||||
* @ORM\JoinColumn(nullable=true)
|
||||
*/
|
||||
private $person;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param $resource Person|ThirdParty
|
||||
*/
|
||||
public function setResource($resource): self
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return ThirdParty|Person
|
||||
* @Groups({"read", "write"})
|
||||
*/
|
||||
public function getResource()
|
||||
{
|
||||
return $this->person ?? $this->thirdParty;
|
||||
}
|
||||
}
|
||||
|
||||
This is not well taken into account by the Symfony serializer natively.
|
||||
|
||||
You must, then, create your own CustomNormalizer. You can help yourself using this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace Chill\PersonBundle\Serializer\Normalizer;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\ThirdPartyBundle\Entity\ThirdParty;
|
||||
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
|
||||
use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
|
||||
use Symfony\Component\Serializer\Exception;
|
||||
use Chill\MainBundle\Serializer\Normalizer\DiscriminatedObjectDenormalizer;
|
||||
|
||||
|
||||
class AccompanyingPeriodResourceNormalizer implements DenormalizerInterface, DenormalizerAwareInterface
|
||||
{
|
||||
use DenormalizerAwareTrait;
|
||||
use ObjectToPopulateTrait;
|
||||
|
||||
public function __construct(ResourceRepository $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||
{
|
||||
// .. snipped for brevity
|
||||
|
||||
if ($resource === NULL) {
|
||||
$resource = new Resource();
|
||||
}
|
||||
|
||||
if (\array_key_exists('resource', $data)) {
|
||||
$res = $this->denormalizer->denormalize(
|
||||
$data['resource'],
|
||||
// call for a "multiple type"
|
||||
DiscriminatedObjectDenormalizer::TYPE,
|
||||
$format,
|
||||
// into the context, we add the list of allowed types:
|
||||
[
|
||||
DiscriminatedObjectDenormalizer::ALLOWED_TYPES =>
|
||||
[
|
||||
Person::class, ThirdParty::class
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
$resource->setResource($res);
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
|
||||
public function supportsDenormalization($data, string $type, string $format = null)
|
||||
{
|
||||
return $type === Resource::class;
|
||||
}
|
||||
}
|
||||
|
||||
Serialization for collection
|
||||
============================
|
||||
****************************
|
||||
|
||||
A specific model has been defined for returning collection:
|
||||
|
||||
@@ -381,8 +673,9 @@ A specific model has been defined for returning collection:
|
||||
}
|
||||
}
|
||||
|
||||
Where this is relevant, this model should be re-used in custom controller actions.
|
||||
|
||||
This can be achieved quickly by assembling results into a :code:`Chill\MainBundle\Serializer\Model\Collection`. The pagination information is given by using :code:`Paginator` (see :ref:`Pagination <pagination-ref>`).
|
||||
In custom actions, this can be achieved quickly by assembling results into a :code:`Chill\MainBundle\Serializer\Model\Collection`. The pagination information is given by using :code:`Paginator` (see :ref:`Pagination <pagination-ref>`).
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@@ -400,10 +693,11 @@ This can be achieved quickly by assembling results into a :code:`Chill\MainBundl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.. _api_full_configuration:
|
||||
|
||||
Full configuration example
|
||||
==========================
|
||||
**************************
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
|
@@ -82,7 +82,7 @@ Chill will be available at ``http://localhost:8001.`` Currently, there isn't any
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load
|
||||
docker-compose exec --user $(id -u) php bin/console doctrine:fixtures:load --purge-with-truncate
|
||||
|
||||
There are several users available:
|
||||
|
||||
|
Reference in New Issue
Block a user