mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-21 07:03:49 +00:00
Merge branch '139_demandeur' into 'master'
139 Demandeur See merge request Chill-Projet/chill-bundles!43
This commit is contained in:
@@ -5,7 +5,7 @@ namespace Chill\MainBundle\CRUD\Controller;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||
|
||||
@@ -25,12 +25,19 @@ class AbstractCRUDController extends AbstractController
|
||||
*
|
||||
* @param string $id
|
||||
* @return object
|
||||
* @throw Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the object is not found
|
||||
*/
|
||||
protected function getEntity($action, $id, Request $request): ?object
|
||||
protected function getEntity($action, $id, Request $request): object
|
||||
{
|
||||
return $this->getDoctrine()
|
||||
$e = $this->getDoctrine()
|
||||
->getRepository($this->getEntityClass())
|
||||
->find($id);
|
||||
|
||||
if (NULL === $e) {
|
||||
throw $this->createNotFoundException(sprintf("The object %s for id %s is not found", $this->getEntityClass(), $id));
|
||||
}
|
||||
|
||||
return $e;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,4 +229,9 @@ class AbstractCRUDController extends AbstractController
|
||||
{
|
||||
return $this->container->get('chill_main.paginator_factory');
|
||||
}
|
||||
|
||||
protected function getValidator(): ValidatorInterface
|
||||
{
|
||||
return $this->get('validator');
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,10 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\MainBundle\Pagination\PaginatorInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Validator\ConstraintViolationListInterface;
|
||||
use Symfony\Component\Serializer\Exception\NotEncodableValueException;
|
||||
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
||||
|
||||
class ApiController extends AbstractCRUDController
|
||||
{
|
||||
@@ -38,11 +42,6 @@ class ApiController extends AbstractCRUDController
|
||||
return $postFetch;
|
||||
}
|
||||
|
||||
if (NULL === $entity) {
|
||||
throw $this->createNotFoundException(sprintf("The %s with id %s "
|
||||
. "is not found", $this->getCrudName(), $id));
|
||||
}
|
||||
|
||||
$response = $this->checkACL($action, $request, $_format, $entity);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
@@ -81,13 +80,123 @@ class ApiController extends AbstractCRUDController
|
||||
{
|
||||
switch ($request->getMethod()) {
|
||||
case Request::METHOD_GET:
|
||||
case REQUEST::METHOD_HEAD:
|
||||
case Request::METHOD_HEAD:
|
||||
return $this->entityGet('_entity', $request, $id, $_format);
|
||||
case Request::METHOD_PUT:
|
||||
case Request::METHOD_PATCH:
|
||||
return $this->entityPut('_entity', $request, $id, $_format);
|
||||
default:
|
||||
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
public function entityPut($action, Request $request, $id, string $_format): Response
|
||||
{
|
||||
$entity = $this->getEntity($action, $id, $request, $_format);
|
||||
|
||||
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
|
||||
if ($postFetch instanceof Response) {
|
||||
return $postFetch;
|
||||
}
|
||||
|
||||
if (NULL === $entity) {
|
||||
throw $this->createNotFoundException(sprintf("The %s with id %s "
|
||||
. "is not found", $this->getCrudName(), $id));
|
||||
}
|
||||
|
||||
$response = $this->checkACL($action, $request, $_format, $entity);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response = $this->onBeforeSerialize($action, $request, $_format, $entity);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
try {
|
||||
$entity = $this->deserialize($action, $request, $_format, $entity);
|
||||
} catch (NotEncodableValueException $e) {
|
||||
throw new BadRequestException("invalid json", 400, $e);
|
||||
}
|
||||
|
||||
$errors = $this->validate($action, $request, $_format, $entity);
|
||||
|
||||
$response = $this->onAfterValidation($action, $request, $_format, $entity, $errors);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
$response = $this->json($errors);
|
||||
$response->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
$response = $this->onAfterFlush($action, $request, $_format, $entity, $errors);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $this->json(
|
||||
$entity,
|
||||
Response::HTTP_OK,
|
||||
[],
|
||||
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
|
||||
);
|
||||
}
|
||||
|
||||
protected function onAfterValidation(string $action, Request $request, string $_format, $entity, ConstraintViolationListInterface $errors, array $more = []): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected function onAfterFlush(string $action, Request $request, string $_format, $entity, ConstraintViolationListInterface $errors, array $more = []): ?Response
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function getValidationGroups(string $action, Request $request, string $_format, $entity): ?array
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function validate(string $action, Request $request, string $_format, $entity, array $more = []): ConstraintViolationListInterface
|
||||
{
|
||||
$validationGroups = $this->getValidationGroups($action, $request, $_format, $entity);
|
||||
|
||||
return $this->getValidator()->validate($entity, null, $validationGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the content of the request into the class associated with the curd
|
||||
*/
|
||||
protected function deserialize(string $action, Request $request, string $_format, $entity = null): object
|
||||
{
|
||||
$default = [];
|
||||
|
||||
if (NULL !== $entity) {
|
||||
$default[AbstractNormalizer::OBJECT_TO_POPULATE] = $entity;
|
||||
}
|
||||
|
||||
$context = \array_merge(
|
||||
$default,
|
||||
$this->getContextForSerialization($action, $request, $_format, $entity)
|
||||
);
|
||||
|
||||
return $this->getSerializer()->deserialize($request->getContent(), $this->getEntityClass(), $_format, $context);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Base action for indexing entities
|
||||
*/
|
||||
@@ -172,6 +281,110 @@ class ApiController extends AbstractCRUDController
|
||||
|
||||
return $this->serializeCollection($action, $request, $_format, $paginator, $entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or remove an associated entity, using `add` and `remove` methods.
|
||||
*
|
||||
* This method:
|
||||
*
|
||||
* 1. Fetch the base entity (throw 404 if not found)
|
||||
* 2. checkACL,
|
||||
* 3. run onPostCheckACL, return response if any,
|
||||
* 4. deserialize posted data into the entity given by $postedDataType, with the context in $postedDataContext
|
||||
* 5. run 'add+$property' for POST method, or 'remove+$property' for DELETE method
|
||||
* 6. validate the base entity (not the deserialized one). Groups are fetched from getValidationGroups, validation is perform by `validate`
|
||||
* 7. run onAfterValidation
|
||||
* 8. if errors, return a 422 response with errors
|
||||
* 9. flush the data
|
||||
* 10. run onAfterFlush
|
||||
* 11. return a 202 response for DELETE with empty body, or HTTP 200 for post with serialized posted entity
|
||||
*
|
||||
* @param string action
|
||||
* @param mixed id
|
||||
* @param Request $request
|
||||
* @param string $_format
|
||||
* @param string $property the name of the property. This will be used to make a `add+$property` and `remove+$property` method
|
||||
* @param string $postedDataType the type of the posted data (the content)
|
||||
* @param string $postedDataContext a context to deserialize posted data (the content)
|
||||
* @throw BadRequestException if unable to deserialize the posted data
|
||||
* @throw BadRequestException if the method is not POST or DELETE
|
||||
*
|
||||
*/
|
||||
protected function addRemoveSomething(string $action, $id, Request $request, string $_format, string $property, string $postedDataType, $postedDataContext = []): Response
|
||||
{
|
||||
$entity = $this->getEntity($action, $id, $request);
|
||||
|
||||
$postFetch = $this->onPostFetchEntity($action, $request, $entity, $_format);
|
||||
if ($postFetch instanceof Response) {
|
||||
return $postFetch;
|
||||
}
|
||||
|
||||
$response = $this->checkACL($action, $request, $_format, $entity);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response = $this->onBeforeSerialize($action, $request, $_format, $entity);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
try {
|
||||
$postedData = $this->getSerializer()->deserialize($request->getContent(), $postedDataType, $_format, $postedDataContext);
|
||||
} catch (\Symfony\Component\Serializer\Exception\UnexpectedValueException $e) {
|
||||
throw new BadRequestException(sprintf("Unable to deserialize posted ".
|
||||
"data: %s", $e->getMessage()), 0, $e);
|
||||
}
|
||||
|
||||
switch ($request->getMethod()) {
|
||||
case Request::METHOD_DELETE:
|
||||
// oups... how to use property accessor to remove element ?
|
||||
$entity->{'remove'.\ucfirst($property)}($postedData);
|
||||
break;
|
||||
case Request::METHOD_POST:
|
||||
$entity->{'add'.\ucfirst($property)}($postedData);
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("this method is not supported");
|
||||
}
|
||||
|
||||
$errors = $this->validate($action, $request, $_format, $entity, [$postedData]);
|
||||
|
||||
$response = $this->onAfterValidation($action, $request, $_format, $entity, $errors, [$postedData]);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($errors->count() > 0) {
|
||||
// only format accepted
|
||||
return $this->json($errors, 422);
|
||||
}
|
||||
|
||||
$this->getDoctrine()->getManager()->flush();
|
||||
|
||||
|
||||
$response = $this->onAfterFlush($action, $request, $_format, $entity, $errors, [$postedData]);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
switch ($request->getMethod()) {
|
||||
case Request::METHOD_DELETE:
|
||||
return $this->json('', Response::HTTP_OK);
|
||||
case Request::METHOD_POST:
|
||||
return $this->json(
|
||||
$postedData,
|
||||
Response::HTTP_OK,
|
||||
[],
|
||||
$this->getContextForSerializationPostAlter($action, $request, $_format, $entity, [$postedData])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize collections
|
||||
@@ -189,7 +402,26 @@ class ApiController extends AbstractCRUDController
|
||||
|
||||
protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array
|
||||
{
|
||||
return [];
|
||||
switch ($request->getMethod()) {
|
||||
case Request::METHOD_GET:
|
||||
return [ 'groups' => [ 'read' ]];
|
||||
case Request::METHOD_PUT:
|
||||
case Request::METHOD_PATCH:
|
||||
return [ 'groups' => [ 'write' ]];
|
||||
default:
|
||||
throw new \LogicException("get context for serialization is not implemented for this method");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context for serialization post alter query (in case of
|
||||
* PATCH, PUT, or POST method)
|
||||
*
|
||||
* This is called **after** the entity was altered.
|
||||
*/
|
||||
protected function getContextForSerializationPostAlter(string $action, Request $request, string $_format, $entity, array $more = []): array
|
||||
{
|
||||
return [ 'groups' => [ 'read' ]];
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user