diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
index df7d065cb..71476fb78 100644
--- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
+++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
@@ -40,6 +40,20 @@ class AbstractCRUDController extends AbstractController
return $e;
}
+ /**
+ * Create an entity.
+ *
+ * @param string $action
+ * @param Request $request
+ * @return object
+ */
+ protected function createEntity(string $action, Request $request): object
+ {
+ $type = $this->getEntityClass();
+
+ return new $type;
+ }
+
/**
* Count the number of entities
*
diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
index 14b0473da..9686a7b98 100644
--- a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
+++ b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
@@ -85,11 +85,75 @@ class ApiController extends AbstractCRUDController
case Request::METHOD_PUT:
case Request::METHOD_PATCH:
return $this->entityPut('_entity', $request, $id, $_format);
+ case Request::METHOD_POST:
+ return $this->entityPostAction('_entity', $request, $id, $_format);
+ default:
+ throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
+ }
+ }
+ public function entityPost(Request $request, $_format): Response
+ {
+ switch($request->getMethod()) {
+ case Request::METHOD_POST:
+ return $this->entityPostAction('_entity', $request, $_format);
default:
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
}
}
+ protected function entityPostAction($action, Request $request, string $_format): Response
+ {
+ $entity = $this->createEntity($action, $request);
+
+ 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;
+ }
+
+ $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;
+ }
+
+ $this->getDoctrine()->getManager()->persist($entity);
+ $this->getDoctrine()->getManager()->flush();
+
+ $response = $this->onAfterFlush($action, $request, $_format, $entity, $errors);
+ if ($response instanceof Response) {
+ return $response;
+ }
+ $response = $this->onBeforeSerialize($action, $request, $_format, $entity);
+ if ($response instanceof Response) {
+ return $response;
+ }
+
+ return $this->json(
+ $entity,
+ Response::HTTP_OK,
+ [],
+ $this->getContextForSerializationPostAlter($action, $request, $_format, $entity)
+ );
+ }
public function entityPut($action, Request $request, $id, string $_format): Response
{
$entity = $this->getEntity($action, $id, $request, $_format);
@@ -407,6 +471,7 @@ class ApiController extends AbstractCRUDController
return [ 'groups' => [ 'read' ]];
case Request::METHOD_PUT:
case Request::METHOD_PATCH:
+ case Request::METHOD_POST:
return [ 'groups' => [ 'write' ]];
default:
throw new \LogicException("get context for serialization is not implemented for this method");
diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
index 8338af8c6..25b7e5860 100644
--- a/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
+++ b/src/Bundle/ChillMainBundle/CRUD/Controller/CRUDController.php
@@ -43,16 +43,16 @@ use Symfony\Component\Serializer\SerializerInterface;
*/
class CRUDController extends AbstractController
{
-
+
/**
- * The crud configuration
+ * The crud configuration
*
* This configuration si defined by `chill_main['crud']`.
*
* @var array
*/
protected $crudConfig;
-
+
/**
* @param array $config
*/
@@ -60,7 +60,7 @@ class CRUDController extends AbstractController
{
$this->crudConfig = $config;
}
-
+
/**
* @param $parameter
* @return Response
@@ -69,7 +69,7 @@ class CRUDController extends AbstractController
{
return new Response($parameter);
}
-
+
/**
* @param Request $request
* @param $id
@@ -79,7 +79,7 @@ class CRUDController extends AbstractController
{
return $this->deleteAction('delete', $request, $id);
}
-
+
/**
* @param string $action
* @param Request $request
@@ -90,78 +90,78 @@ class CRUDController extends AbstractController
protected function deleteAction(string $action, Request $request, $id, $formClass = null)
{
$this->onPreDelete($action, $request, $id);
-
+
$entity = $this->getEntity($action, $id, $request);
-
+
$postFetch = $this->onPostFetchEntity($action, $request, $entity);
-
+
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, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$form = $this->createFormFor($action, $entity, $formClass);
-
+
$form->handleRequest($request);
-
+
if ($form->isSubmitted() && $form->isValid()) {
$this->onFormValid($entity, $form, $request);
$em = $this->getDoctrine()->getManager();
-
+
$this->onPreRemove($action, $entity, $form, $request);
$this->removeEntity($action, $entity, $form, $request);
$this->onPostRemove($action, $entity, $form, $request);
-
+
$this->onPreFlush($action, $entity, $form, $request);
$em->flush();
$this->onPostFlush($action, $entity, $form, $request);
-
+
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
-
+
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
-
+
if ($result instanceof Response) {
return $result;
}
-
+
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]);
-
+
} elseif ($form->isSubmitted()) {
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
}
-
+
$defaultTemplateParameters = [
'form' => $form->createView(),
'entity' => $entity,
'crud_name' => $this->getCrudName()
];
-
+
return $this->render(
- $this->getTemplateFor($action, $entity, $request),
+ $this->getTemplateFor($action, $entity, $request),
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
);
}
-
+
/**
* @param string $action
* @param Request $request
*/
protected function onPreDelete(string $action, Request $request) {}
-
+
/**
* @param string $action
* @param $entity
@@ -169,7 +169,7 @@ class CRUDController extends AbstractController
* @param Request $request
*/
protected function onPreRemove(string $action, $entity, FormInterface $form, Request $request) {}
-
+
/**
* @param string $action
* @param $entity
@@ -177,7 +177,7 @@ class CRUDController extends AbstractController
* @param Request $request
*/
protected function onPostRemove(string $action, $entity, FormInterface $form, Request $request) {}
-
+
/**
* @param string $action
* @param $entity
@@ -190,10 +190,10 @@ class CRUDController extends AbstractController
->getManager()
->remove($entity);
}
-
+
/**
* Base method called by index action.
- *
+ *
* @param Request $request
* @return type
*/
@@ -201,14 +201,14 @@ class CRUDController extends AbstractController
{
return $this->indexEntityAction('index', $request);
}
-
+
/**
* Build an index page.
- *
+ *
* Some steps may be overriden during this process of rendering.
- *
+ *
* This method:
- *
+ *
* 1. Launch `onPreIndex`
* x. check acl. If it does return a response instance, return it
* x. launch `onPostCheckACL`. If it does return a response instance, return it
@@ -219,15 +219,15 @@ class CRUDController extends AbstractController
* x. fetch the results, using `getQueryResult`
* x. Launch `onPostIndexFetchQuery`. If it does return a response instance, return it
* 4. create default parameters:
- *
- * The default parameters are:
- *
+ *
+ * The default parameters are:
+ *
* * entities: the list en entities ;
* * crud_name: the name of the crud ;
* * paginator: a paginator element ;
- * 5. Launch rendering, the parameter is fetch using `getTemplateFor`
+ * 5. Launch rendering, the parameter is fetch using `getTemplateFor`
* The parameters may be personnalized using `generateTemplateParameter`.
- *
+ *
* @param string $action
* @param Request $request
* @return type
@@ -235,80 +235,80 @@ class CRUDController extends AbstractController
protected function indexEntityAction($action, Request $request)
{
$this->onPreIndex($action, $request);
-
+
$response = $this->checkACL($action, null);
if ($response instanceof Response) {
return $response;
}
-
+
if (!isset($entity)) {
$entity = '';
}
-
+
$response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$totalItems = $this->countEntities($action, $request);
$paginator = $this->getPaginatorFactory()->create($totalItems);
-
- $response = $this->onPreIndexBuildQuery($action, $request, $totalItems,
+
+ $response = $this->onPreIndexBuildQuery($action, $request, $totalItems,
$paginator);
-
+
if ($response instanceof Response) {
return $response;
}
-
+
$query = $this->queryEntities($action, $request, $paginator);
-
- $response = $this->onPostIndexBuildQuery($action, $request, $totalItems,
+
+ $response = $this->onPostIndexBuildQuery($action, $request, $totalItems,
$paginator, $query);
-
+
if ($response instanceof Response) {
return $response;
}
-
+
$entities = $this->getQueryResult($action, $request, $totalItems, $paginator, $query);
-
- $response = $this->onPostIndexFetchQuery($action, $request, $totalItems,
+
+ $response = $this->onPostIndexFetchQuery($action, $request, $totalItems,
$paginator, $entities);
-
+
if ($response instanceof Response) {
return $response;
}
-
+
$defaultTemplateParameters = [
'entities' => $entities,
'crud_name' => $this->getCrudName(),
'paginator' => $paginator
];
-
+
return $this->render(
- $this->getTemplateFor($action, $entities, $request),
+ $this->getTemplateFor($action, $entities, $request),
$this->generateTemplateParameter($action, $entities, $request, $defaultTemplateParameters)
);
}
-
+
/**
* @param string $action
* @param Request $request
*/
protected function onPreIndex(string $action, Request $request) { }
-
+
/**
* method used by indexAction
- *
+ *
* @param string $action
* @param Request $request
* @param int $totalItems
* @param PaginatorInterface $paginator
*/
protected function onPreIndexBuildQuery(string $action, Request $request, int $totalItems, PaginatorInterface $paginator) { }
-
+
/**
* method used by indexAction
- *
+ *
* @param string $action
* @param Request $request
* @param int $totalItems
@@ -316,10 +316,10 @@ class CRUDController extends AbstractController
* @param mixed $query
*/
protected function onPostIndexBuildQuery(string $action, Request $request, int $totalItems, PaginatorInterface $paginator, $query) { }
-
+
/**
* method used by indexAction
- *
+ *
* @param string $action
* @param Request $request
* @param int $totalItems
@@ -327,14 +327,14 @@ class CRUDController extends AbstractController
* @param mixed $entities
*/
protected function onPostIndexFetchQuery(string $action, Request $request, int $totalItems, PaginatorInterface $paginator, $entities) { }
-
+
/**
* Build the base query for listing all entities, normally use in a listing
* page.
- *
+ *
* This base query does not contains any `WHERE` or `SELECT` clauses. Those
* are added by other methods, like `queryEntities` and `countQueries`.
- *
+ *
* @param string $action
* @param Request $request
* @return QueryBuilder
@@ -347,16 +347,16 @@ class CRUDController extends AbstractController
->from($this->getEntityClass(), 'e')
;
}
-
+
/**
* Query the entity.
- *
+ *
* By default, get all entities.
- *
+ *
* The method `orderEntity` is called internally to order entities.
- *
+ *
* It returns, by default, a query builder.
- *
+ *
* @param string $action
* @param Request $request
* @param PaginatorInterface $paginator
@@ -367,15 +367,15 @@ class CRUDController extends AbstractController
$query = $this->buildQueryEntities($action, $request)
->setFirstResult($paginator->getCurrentPage()->getFirstItemNumber())
->setMaxResults($paginator->getItemsPerPage());
-
+
// allow to order queries and return the new query
return $this->orderQuery($action, $query, $request, $paginator);
}
-
-
+
+
/**
* Add ordering fields in the query build by self::queryEntities
- *
+ *
* @param string $action
* @param QueryBuilder|mixed $query by default, an instance of QueryBuilder
* @param Request $request
@@ -386,10 +386,10 @@ class CRUDController extends AbstractController
{
return $query;
}
-
+
/**
* Get the result of the query
- *
+ *
* @param string $action
* @param Request $request
* @param int $totalItems
@@ -397,14 +397,14 @@ class CRUDController extends AbstractController
* @param mixed $query
* @return mixed
*/
- protected function getQueryResult(string $action, Request $request, int $totalItems, PaginatorInterface $paginator, $query)
+ protected function getQueryResult(string $action, Request $request, int $totalItems, PaginatorInterface $paginator, $query)
{
return $query->getQuery()->getResult();
}
-
+
/**
* Count the number of entities
- *
+ *
* @param string $action
* @param Request $request
* @return int
@@ -417,12 +417,12 @@ class CRUDController extends AbstractController
->getSingleScalarResult()
;
}
-
+
/**
* BAse method for edit action
- *
+ *
* IMplemented by the method formEditAction, with action as 'edit'
- *
+ *
* @param Request $request
* @param mixed $id
* @return Response
@@ -431,12 +431,12 @@ class CRUDController extends AbstractController
{
return $this->formEditAction('edit', $request, $id);
}
-
+
/**
* Base method for new action
- *
+ *
* Implemented by the method formNewAction, with action as 'new'
- *
+ *
* @param Request $request
* @return Response
*/
@@ -444,12 +444,12 @@ class CRUDController extends AbstractController
{
return $this->formCreateAction('new', $request);
}
-
+
/**
* Base method for the view action.
- *
+ *
* Implemented by the method viewAction, with action as 'view'
- *
+ *
* @param Request $request
* @param mixed $id
* @return Response
@@ -458,28 +458,28 @@ class CRUDController extends AbstractController
{
return $this->viewAction('view', $request, $id);
}
-
+
/**
* The view action.
- *
+ *
* Some steps may be overriden during this process of rendering:
- *
+ *
* This method:
- *
+ *
* 1. fetch the entity, using `getEntity`
* 2. launch `onPostFetchEntity`. If postfetch is an instance of Response,
* this response is returned.
* 2. throw an HttpNotFoundException if entity is null
* 3. check ACL using `checkACL` ;
- * 4. launch `onPostCheckACL`. If the result is an instance of Response,
+ * 4. launch `onPostCheckACL`. If the result is an instance of Response,
* this response is returned ;
* 5. generate default template parameters:
- *
+ *
* * `entity`: the fetched entity ;
* * `crud_name`: the crud name
- * 6. Launch rendering, the parameter is fetch using `getTemplateFor`
+ * 6. Launch rendering, the parameter is fetch using `getTemplateFor`
* The parameters may be personnalized using `generateTemplateParameter`.
- *
+ *
* @param string $action
* @param Request $request
* @param mixed $id
@@ -488,23 +488,23 @@ class CRUDController extends AbstractController
protected function viewAction(string $action, Request $request, $id, $_format = 'html'): Response
{
$entity = $this->getEntity($action, $id, $request);
-
+
$postFetch = $this->onPostFetchEntity($action, $request, $entity);
-
+
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, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) {
return $response;
@@ -515,9 +515,9 @@ class CRUDController extends AbstractController
'entity' => $entity,
'crud_name' => $this->getCrudName()
];
-
+
return $this->render(
- $this->getTemplateFor($action, $entity, $request),
+ $this->getTemplateFor($action, $entity, $request),
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
);
} elseif ($_format === 'json') {
@@ -537,45 +537,45 @@ class CRUDController extends AbstractController
{
return [];
}
-
-
+
+
/**
* The edit action.
- *
+ *
* Some steps may be overriden during this process of rendering:
- *
+ *
* This method:
- *
+ *
* 1. fetch the entity, using `getEntity`
* 2. launch `onPostFetchEntity`. If postfetch is an instance of Response,
* this response is returned.
* 2. throw an HttpNotFoundException if entity is null
* 3. check ACL using `checkACL` ;
- * 4. launch `onPostCheckACL`. If the result is an instance of Response,
+ * 4. launch `onPostCheckACL`. If the result is an instance of Response,
* this response is returned ;
* 5. generate a form using `createFormFor`, and handle request on this form;
- *
+ *
* If the form is valid, the entity is stored and flushed, and a redirection
* is returned.
- *
+ *
* In this case, those hooks are available:
- *
+ *
* * onFormValid
* * onPreFlush
* * onPostFlush
- * * onBeforeRedirectAfterSubmission. If this method return an instance of
+ * * onBeforeRedirectAfterSubmission. If this method return an instance of
* Response, this response is returned.
- *
+ *
* 5. generate default template parameters:
- *
+ *
* * `entity`: the fetched entity ;
* * `crud_name`: the crud name ;
* * `form`: the formview instance.
- *
- * 6. Launch rendering, the parameter is fetch using `getTemplateFor`
+ *
+ * 6. Launch rendering, the parameter is fetch using `getTemplateFor`
* The parameters may be personnalized using `generateTemplateParameter`.
- *
+ *
* @param string $action
* @param Request $request
* @param mixed $id
@@ -587,98 +587,98 @@ class CRUDController extends AbstractController
protected function formEditAction(string $action, Request $request, $id, string $formClass = null, array $formOptions = []): Response
{
$entity = $this->getEntity($action, $id, $request);
-
+
if (NULL === $entity) {
throw $this->createNotFoundException(sprintf("The %s with id %s "
- . "is not found"), $this->getCrudName(), $id);
+ . "is not found", $this->getCrudName(), $id));
}
-
+
$response = $this->checkACL($action, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$form = $this->createFormFor($action, $entity, $formClass, $formOptions);
-
+
$form->handleRequest($request);
-
+
if ($form->isSubmitted() && $form->isValid()) {
$this->onFormValid($entity, $form, $request);
$em = $this->getDoctrine()->getManager();
-
+
$this->onPreFlush($action, $entity, $form, $request);
$em->flush();
$this->onPostFlush($action, $entity, $form, $request);
-
+
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
-
+
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
-
+
if ($result instanceof Response) {
return $result;
}
-
+
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_index');
-
+
} elseif ($form->isSubmitted()) {
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
}
-
+
$defaultTemplateParameters = [
'form' => $form->createView(),
'entity' => $entity,
'crud_name' => $this->getCrudName()
];
-
+
return $this->render(
- $this->getTemplateFor($action, $entity, $request),
+ $this->getTemplateFor($action, $entity, $request),
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
);
}
-
+
/**
* The new (or creation) action.
- *
+ *
* Some steps may be overriden during this process of rendering:
- *
+ *
* This method:
- *
+ *
* 1. Create or duplicate an entity:
- *
- * If the `duplicate` parameter is present, the entity is duplicated
+ *
+ * If the `duplicate` parameter is present, the entity is duplicated
* using the `duplicate` method.
- *
+ *
* If not, the entity is created using the `create` method.
* 3. check ACL using `checkACL` ;
- * 4. launch `onPostCheckACL`. If the result is an instance of Response,
+ * 4. launch `onPostCheckACL`. If the result is an instance of Response,
* this response is returned ;
* 5. generate a form using `createFormFor`, and handle request on this form;
- *
+ *
* If the form is valid, the entity is stored and flushed, and a redirection
* is returned.
- *
+ *
* In this case, those hooks are available:
- *
+ *
* * onFormValid
* * onPreFlush
* * onPostFlush
- * * onBeforeRedirectAfterSubmission. If this method return an instance of
+ * * onBeforeRedirectAfterSubmission. If this method return an instance of
* Response, this response is returned.
- *
+ *
* 5. generate default template parameters:
- *
+ *
* * `entity`: the fetched entity ;
* * `crud_name`: the crud name ;
* * `form`: the formview instance.
- *
- * 6. Launch rendering, the parameter is fetch using `getTemplateFor`
+ *
+ * 6. Launch rendering, the parameter is fetch using `getTemplateFor`
* The parameters may be personnalized using `generateTemplateParameter`.
- *
+ *
* @param string $action
* @param Request $request
* @param type $formClass
@@ -691,62 +691,62 @@ class CRUDController extends AbstractController
} else {
$entity = $this->createEntity($action, $request);
}
-
+
$response = $this->checkACL($action, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) {
return $response;
}
-
+
$form = $this->createFormFor($action, $entity, $formClass);
-
+
$form->handleRequest($request);
-
+
if ($form->isSubmitted() && $form->isValid()) {
$this->onFormValid($entity, $form, $request);
$em = $this->getDoctrine()->getManager();
-
+
$this->onPrePersist($action, $entity, $form, $request);
$em->persist($entity);
$this->onPostPersist($action, $entity, $form, $request);
-
+
$this->onPreFlush($action, $entity, $form, $request);
$em->flush();
$this->onPostFlush($action, $entity, $form, $request);
$this->getPaginatorFactory();
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
-
+
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
-
+
if ($result instanceof Response) {
return $result;
}
-
+
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_view', ['id' => $entity->getId()]);
-
+
} elseif ($form->isSubmitted()) {
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
}
-
+
$defaultTemplateParameters = [
'form' => $form->createView(),
'entity' => $entity,
'crud_name' => $this->getCrudName()
];
-
+
return $this->render(
- $this->getTemplateFor($action, $entity, $request),
+ $this->getTemplateFor($action, $entity, $request),
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
);
}
-
+
/**
* get the instance of the entity with the given id
- *
+ *
* @param string $id
* @return object
*/
@@ -756,10 +756,10 @@ class CRUDController extends AbstractController
->getRepository($this->getEntityClass())
->find($id);
}
-
+
/**
* Duplicate an entity
- *
+ *
* @param string $action
* @param Request $request
* @return mixed
@@ -768,24 +768,24 @@ class CRUDController extends AbstractController
{
$id = $request->query->get('duplicate_id', 0);
$originalEntity = $this->getEntity($action, $id, $request);
-
+
$this->getDoctrine()->getManager()
->detach($originalEntity);
-
+
return $originalEntity;
}
-
+
/**
- *
+ *
* @return string the complete fqdn of the class
*/
protected function getEntityClass(): string
{
return $this->crudConfig['class'];
}
-
+
/**
- *
+ *
* @return string the crud name
*/
protected function getCrudName(): string
@@ -795,13 +795,13 @@ class CRUDController extends AbstractController
/**
* check the acl. Called by every action.
- *
- * By default, check the role given by `getRoleFor` for the value given in
+ *
+ * By default, check the role given by `getRoleFor` for the value given in
* $entity.
- *
+ *
* Throw an \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
* if not accessible.
- *
+ *
* @param string $action
* @param mixed $entity
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
@@ -810,10 +810,10 @@ class CRUDController extends AbstractController
{
$this->denyAccessUnlessGranted($this->getRoleFor($action), $entity);
}
-
+
/**
* get the role given from the config.
- *
+ *
* @param string $action
* @return string
*/
@@ -822,27 +822,27 @@ class CRUDController extends AbstractController
if (\array_key_exists('role', $this->getActionConfig($action))) {
return $this->getActionConfig($action)['role'];
}
-
+
return $this->buildDefaultRole($action);
}
/**
* build a default role name, using the crud resolver.
- *
+ *
* This method should not be overriden. Override `getRoleFor` instead.
- *
+ *
* @param string $action
* @return string
*/
protected function buildDefaultRole($action)
{
- return $this->getCrudResolver()->buildDefaultRole($this->getCrudName(),
+ return $this->getCrudResolver()->buildDefaultRole($this->getCrudName(),
$action);
}
-
+
/**
* get the default form class from config
- *
+ *
* @param string $action
* @return string the FQDN of the form class
*/
@@ -852,27 +852,27 @@ class CRUDController extends AbstractController
return $this->crudConfig[$action]['form_class']
?? $this->getDefaultDeleteFormClass($action);
}
-
+
return $this->crudConfig[$action]['form_class']
?? $this->crudConfig['form_class'];
}
-
+
protected function getDefaultDeleteFormClass($action)
{
return CRUDDeleteEntityForm::class;
}
-
+
/**
* Create a form
- *
+ *
* use the method `getFormClassFor`
- *
+ *
* A hook is available: `customizeForm` allow you to customize the form
* if needed.
- *
+ *
* It is preferable to override customizeForm instead of overriding
* this method.
- *
+ *
* @param string $action
* @param mixed $entity
* @param string $formClass
@@ -882,17 +882,17 @@ class CRUDController extends AbstractController
protected function createFormFor(string $action, $entity, string $formClass = null, array $formOptions = []): FormInterface
{
$formClass = $formClass ?? $this->getFormClassFor($action);
-
+
$form = $this->createForm($formClass, $entity, $formOptions);
-
+
$this->customizeForm($action, $form);
-
+
return $form;
}
-
+
/**
* Customize the form created by createFormFor.
- *
+ *
* @param string $action
* @param FormInterface $form
*/
@@ -900,12 +900,12 @@ class CRUDController extends AbstractController
{
}
-
+
/**
* Generate a message which explains an error about the form.
- *
+ *
* Used in form actions
- *
+ *
* @param string $action
* @param FormInterface $form
* @return string
@@ -913,13 +913,13 @@ class CRUDController extends AbstractController
protected function generateFormErrorMessage(string $action, FormInterface $form): string
{
$msg = 'This form contains errors';
-
+
return $this->getTranslator()->trans($msg);
}
-
+
/**
* Generate a success message when a form could be flushed successfully
- *
+ *
* @param string $action
* @param mixed $entity
* @return string
@@ -939,13 +939,13 @@ class CRUDController extends AbstractController
default:
$msg = "crud.default.success";
}
-
+
return $this->getTranslator()->trans($msg);
}
-
+
/**
* Customize template parameters.
- *
+ *
* @param string $action
* @param mixed $entity
* @param Request $request
@@ -953,9 +953,9 @@ class CRUDController extends AbstractController
* @return array
*/
protected function generateTemplateParameter(
- string $action,
- $entity,
- Request $request,
+ string $action,
+ $entity,
+ Request $request,
array $defaultTemplateParameters = []
) {
return $defaultTemplateParameters;
@@ -963,7 +963,7 @@ class CRUDController extends AbstractController
/**
* Create an entity.
- *
+ *
* @param string $action
* @param Request $request
* @return object
@@ -971,17 +971,17 @@ class CRUDController extends AbstractController
protected function createEntity(string $action, Request $request): object
{
$type = $this->getEntityClass();
-
+
return new $type;
}
-
+
/**
* Get the template for the current crud.
- *
- * This template may be defined in configuration. If any template are
- * defined, return the default template for the actions new, edit, index,
+ *
+ * This template may be defined in configuration. If any template are
+ * defined, return the default template for the actions new, edit, index,
* and view.
- *
+ *
* @param string $action
* @param mixed $entity the entity for the current request, or an array of entities
* @param Request $request
@@ -993,11 +993,11 @@ class CRUDController extends AbstractController
if ($this->hasCustomTemplate($action, $entity, $request)) {
return $this->getActionConfig($action)['template'];
}
-
+
switch ($action) {
case 'new':
return '@ChillMain/CRUD/new.html.twig';
- case 'edit':
+ case 'edit':
return '@ChillMain/CRUD/edit.html.twig';
case 'index':
return '@ChillMain/CRUD/index.html.twig';
@@ -1011,7 +1011,7 @@ class CRUDController extends AbstractController
. "action");
}
}
-
+
/**
* @param $action
* @param $entity
@@ -1022,7 +1022,7 @@ class CRUDController extends AbstractController
{
return !empty($this->getActionConfig($action)['template']);
}
-
+
/**
* @param string $action
* @param $entity
@@ -1032,7 +1032,7 @@ class CRUDController extends AbstractController
protected function onPreFlush(string $action, $entity, FormInterface $form, Request $request)
{
}
-
+
/**
* @param string $action
* @param $entity
@@ -1042,7 +1042,7 @@ class CRUDController extends AbstractController
protected function onPostFlush(string $action, $entity, FormInterface $form, Request $request)
{
}
-
+
/**
* @param string $action
* @param $entity
@@ -1052,7 +1052,7 @@ class CRUDController extends AbstractController
protected function onPrePersist(string $action, $entity, FormInterface $form, Request $request)
{
}
-
+
/**
* @param string $action
* @param $entity
@@ -1062,7 +1062,7 @@ class CRUDController extends AbstractController
protected function onPostPersist(string $action, $entity, FormInterface $form, Request $request)
{
}
-
+
/**
* @param $action
* @param Request $request
@@ -1073,7 +1073,7 @@ class CRUDController extends AbstractController
{
return null;
}
-
+
/**
* @param $action
* @param Request $request
@@ -1084,7 +1084,7 @@ class CRUDController extends AbstractController
{
return null;
}
-
+
/**
* @param object $entity
* @param FormInterface $form
@@ -1093,16 +1093,16 @@ class CRUDController extends AbstractController
protected function onFormValid(object $entity, FormInterface $form, Request $request)
{
}
-
+
/**
* Return a redirect response depending on the value of submit button.
- *
+ *
* The handled values are :
- *
+ *
* * save-and-close: return to index of current crud ;
* * save-and-new: return to new page of current crud ;
* * save-and-view: return to view page of current crud ;
- *
+ *
* @param string $action
* @param mixed $entity
* @param FormInterface $form
@@ -1124,7 +1124,7 @@ class CRUDController extends AbstractController
]);
}
}
-
+
/**
* Include services
*
@@ -1135,7 +1135,7 @@ class CRUDController extends AbstractController
{
return $this->crudConfig['actions'][$action];
}
-
+
/**
* @return PaginatorFactory
*/
@@ -1143,7 +1143,7 @@ class CRUDController extends AbstractController
{
return $this->container->get('chill_main.paginator_factory');
}
-
+
/**
* @return TranslatorInterface
*/
@@ -1151,7 +1151,7 @@ class CRUDController extends AbstractController
{
return $this->container->get('translator');
}
-
+
/**
* @return AuthorizationHelper
*/
@@ -1159,19 +1159,19 @@ class CRUDController extends AbstractController
{
return $this->container->get(AuthorizationHelper::class);
}
-
+
/**
* @param Role $role
* @param Scope|null $scope
* @return \Chill\MainBundle\Entity\Center[]
*/
- protected function getReachableCenters(Role $role, Scope $scope = null)
+ protected function getReachableCenters(Role $role, Scope $scope = null)
{
return $this->getAuthorizationHelper()
->getReachableCenters($this->getUser(), $role, $scope)
;
}
-
+
/**
* @return EventDispatcherInterface
*/
@@ -1179,7 +1179,7 @@ class CRUDController extends AbstractController
{
return $this->get(EventDispatcherInterface::class);
}
-
+
/**
* @return Resolver
*/
@@ -1187,7 +1187,7 @@ class CRUDController extends AbstractController
{
return $this->get(Resolver::class);
}
-
+
/**
* @return array
*/
diff --git a/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php b/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
index 32068e518..9d61d6233 100644
--- a/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
+++ b/src/Bundle/ChillMainBundle/CRUD/Routing/CRUDRoutesLoader.php
@@ -183,48 +183,26 @@ class CRUDRoutesLoader extends Loader
$methods = \array_keys(\array_filter($action['methods'], function($value, $key) { return $value; },
ARRAY_FILTER_USE_BOTH));
- $route = new Route($path, $defaults, $requirements);
- $route->setMethods($methods);
-
- $collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
- }
+ if (count($methods) === 0) {
+ throw new \RuntimeException("The api configuration named \"{$crudConfig['name']}\", action \"{$name}\", ".
+ "does not have any allowed methods. You should remove this action from the config ".
+ "or allow, at least, one method");
+ }
- return $collection;
- }
+ if ('_entity' === $name && \in_array(Request::METHOD_POST, $methods)) {
+ unset($methods[\array_search(Request::METHOD_POST, $methods)]);
+ $entityPostRoute = $this->createEntityPostRoute($name, $crudConfig, $action,
+ $controller);
+ $collection->add("chill_api_single_{$crudConfig['name']}_{$name}_create",
+ $entityPostRoute);
+ }
- /**
- * Load routes for api multi
- *
- * @param $crudConfig
- * @return RouteCollection
- */
- protected function loadApiMultiConfig(array $crudConfig): RouteCollection
- {
- $collection = new RouteCollection();
- $controller ='csapi_'.$crudConfig['name'].'_controller';
-
- foreach ($crudConfig['actions'] as $name => $action) {
- // filter only on single actions
- $singleCollection = $action['single-collection'] ?? $name === '_index' ? 'collection' : NULL;
- if ('single' === $singleCollection) {
+ if (count($methods) === 0) {
+ // the only method was POST,
+ // continue to next
continue;
}
- $defaults = [
- '_controller' => $controller.':'.($action['controller_action'] ?? '_entity' === $name ? 'entityApi' : $name.'Api')
- ];
-
- // path are rewritten
- // if name === 'default', we rewrite it to nothing :-)
- $localName = '_entity' === $name ? '' : '/'.$name;
- $localPath = $action['path'] ?? '/{id}'.$localName.'.{_format}';
- $path = $crudConfig['base_path'].$localPath;
-
- $requirements = $action['requirements'] ?? [ '{id}' => '\d+' ];
-
- $methods = \array_keys(\array_filter($action['methods'], function($value, $key) { return $value; },
- ARRAY_FILTER_USE_BOTH));
-
$route = new Route($path, $defaults, $requirements);
$route->setMethods($methods);
@@ -233,4 +211,18 @@ class CRUDRoutesLoader extends Loader
return $collection;
}
+
+ private function createEntityPostRoute(string $name, $crudConfig, array $action, $controller): Route
+ {
+ $localPath = $action['path'].'.{_format}';
+ $defaults = [
+ '_controller' => $controller.':'.($action['controller_action'] ?? 'entityPost')
+ ];
+ $path = $crudConfig['base_path'].$localPath;
+ $requirements = $action['requirements'] ?? [];
+ $route = new Route($path, $defaults, $requirements);
+ $route->setMethods([ Request::METHOD_POST ]);
+
+ return $route;
+ }
}
diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
index 277e916a8..5644138e6 100644
--- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
+++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
@@ -35,6 +35,8 @@ use Chill\MainBundle\Doctrine\DQL\OverlapsI;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Chill\MainBundle\Doctrine\DQL\Replace;
+use Chill\MainBundle\Doctrine\Type\NativeDateIntervalType;
+use Chill\MainBundle\Doctrine\Type\PointType;
use Symfony\Component\HttpFoundation\Request;
/**
@@ -167,37 +169,49 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
$container->prependExtensionConfig('twig', $twigConfig);
//add DQL function to ORM (default entity_manager)
- $container->prependExtensionConfig('doctrine', array(
- 'orm' => array(
- 'dql' => array(
- 'string_functions' => array(
- 'unaccent' => Unaccent::class,
- 'GET_JSON_FIELD_BY_KEY' => GetJsonFieldByKey::class,
- 'AGGREGATE' => JsonAggregate::class,
- 'REPLACE' => Replace::class,
- ),
- 'numeric_functions' => [
- 'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,
- 'SIMILARITY' => Similarity::class,
- 'OVERLAPSI' => OverlapsI::class
- ]
- )
- )
- ));
+ $container
+ ->prependExtensionConfig(
+ 'doctrine',
+ [
+ 'orm' => [
+ 'dql' => [
+ 'string_functions' => [
+ 'unaccent' => Unaccent::class,
+ 'GET_JSON_FIELD_BY_KEY' => GetJsonFieldByKey::class,
+ 'AGGREGATE' => JsonAggregate::class,
+ 'REPLACE' => Replace::class,
+ ],
+ 'numeric_functions' => [
+ 'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class,
+ 'SIMILARITY' => Similarity::class,
+ 'OVERLAPSI' => OverlapsI::class,
+ ],
+ ],
+ ],
+ ],
+ );
//add dbal types (default entity_manager)
- $container->prependExtensionConfig('doctrine', array(
- 'dbal' => [
- 'types' => [
- 'dateinterval' => [
- 'class' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class
- ],
- 'point' => [
- 'class' => \Chill\MainBundle\Doctrine\Type\PointType::class
- ]
- ]
- ]
- ));
+ $container
+ ->prependExtensionConfig(
+ 'doctrine',
+ [
+ 'dbal' => [
+ // This is mandatory since we are using postgis as database.
+ 'mapping_types' => [
+ 'geometry' => 'string',
+ ],
+ 'types' => [
+ 'dateinterval' => [
+ 'class' => NativeDateIntervalType::class
+ ],
+ 'point' => [
+ 'class' => PointType::class
+ ]
+ ]
+ ]
+ ]
+ );
//add current route to chill main
$container->prependExtensionConfig('chill_main', array(
diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/CenterNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/CenterNormalizer.php
index 26182d936..f1681c60a 100644
--- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/CenterNormalizer.php
+++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/CenterNormalizer.php
@@ -20,19 +20,32 @@
namespace Chill\MainBundle\Serializer\Normalizer;
use Chill\MainBundle\Entity\Center;
+use Chill\MainBundle\Repository\CenterRepository;
+use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use Symfony\Component\Serializer\Exception\InvalidArgumentException;
+use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
*
*
*/
-class CenterNormalizer implements NormalizerInterface
+class CenterNormalizer implements NormalizerInterface, DenormalizerInterface
{
+ private CenterRepository $repository;
+
+
+ public function __construct(CenterRepository $repository)
+ {
+ $this->repository = $repository;
+ }
+
public function normalize($center, string $format = null, array $context = array())
{
/** @var Center $center */
return [
'id' => $center->getId(),
+ 'type' => 'center',
'name' => $center->getName()
];
}
@@ -41,4 +54,30 @@ class CenterNormalizer implements NormalizerInterface
{
return $data instanceof Center;
}
+
+ public function denormalize($data, string $type, string $format = null, array $context = [])
+ {
+ if (FALSE === \array_key_exists('type', $data)) {
+ throw new InvalidArgumentException('missing "type" key in data');
+ }
+ if ('center' !== $data['type']) {
+ throw new InvalidArgumentException('type should be equal to "center"');
+ }
+ if (FALSE === \array_key_exists('id', $data)) {
+ throw new InvalidArgumentException('missing "id" key in data');
+ }
+
+ $center = $this->repository->find($data['id']);
+
+ if (null === $center) {
+ throw new UnexpectedValueException("The type with id {$data['id']} does not exists");
+ }
+
+ return $center;
+ }
+
+ public function supportsDenormalization($data, string $type, string $format = null)
+ {
+ return $type === Center::class;
+ }
}
diff --git a/src/Bundle/ChillMainBundle/chill.api.specs.yaml b/src/Bundle/ChillMainBundle/chill.api.specs.yaml
index ada65b08a..68a3eb764 100644
--- a/src/Bundle/ChillMainBundle/chill.api.specs.yaml
+++ b/src/Bundle/ChillMainBundle/chill.api.specs.yaml
@@ -9,15 +9,14 @@ servers:
description: "Your current dev server"
components:
- parameters:
- _format:
- name: _format
- in: path
- required: true
- schema:
- type: string
- enum:
- - json
+ schemas:
+ Center:
+ type: object
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
paths:
/1.0/search.json:
diff --git a/src/Bundle/ChillPersonBundle/Controller/ApiPersonController.php b/src/Bundle/ChillPersonBundle/Controller/ApiPersonController.php
deleted file mode 100644
index bfaf22d7b..000000000
--- a/src/Bundle/ChillPersonBundle/Controller/ApiPersonController.php
+++ /dev/null
@@ -1,30 +0,0 @@
-
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-namespace Chill\PersonBundle\Controller;
-
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-use Symfony\Component\HttpFoundation\JsonResponse;
-
-
-class ApiPersonController extends Controller
-{
- public function viewAction($id, $_format)
- {
-
- }
-}
diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonApiController.php b/src/Bundle/ChillPersonBundle/Controller/PersonApiController.php
new file mode 100644
index 000000000..84f1ebf66
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Controller/PersonApiController.php
@@ -0,0 +1,50 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+namespace Chill\PersonBundle\Controller;
+
+use Chill\PersonBundle\Security\Authorization\PersonVoter;
+use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
+use Symfony\Component\Security\Core\Role\Role;
+use Chill\MainBundle\CRUD\Controller\ApiController;
+use Symfony\Component\HttpFoundation\Request;
+
+
+class PersonApiController extends ApiController
+{
+ private AuthorizationHelper $authorizationHelper;
+
+ /**
+ * @param AuthorizationHelper $authorizationHelper
+ */
+ public function __construct(AuthorizationHelper $authorizationHelper)
+ {
+ $this->authorizationHelper = $authorizationHelper;
+ }
+
+ protected function createEntity(string $action, Request $request): object
+ {
+ $person = parent::createEntity($action, $request);
+
+ // TODO temporary hack to allow creation of person with fake center
+ $centers = $this->authorizationHelper->getReachableCenters($this->getUser(),
+ new Role(PersonVoter::CREATE));
+ $person->setCenter($centers[0]);
+
+ return $person;
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialActions.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialActions.php
index c12156fc7..ec47c6e0b 100644
--- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialActions.php
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialActions.php
@@ -37,32 +37,32 @@ class LoadSocialActions extends AbstractFixture implements OrderedFixtureInterfa
return 10020;
}
- public static $socialActions = array(
- 'social_action_info_conseil' => array(
- 'title' => array(
+ public static $socialActions = [
+ 'social_action_info_conseil' => [
+ 'title' => [
'fr' => 'Informer, conseiller'
- ),
+ ],
'issue' => 'social_issue_prev_prot'
- ),
- 'social_action_instruire' => array(
- 'title' => array(
+ ],
+ 'social_action_instruire' => [
+ 'title' => [
'fr' => 'Instruire l\'imprime unique pour des impayés'
- ),
+ ],
'issue' => 'social_issue_prev_prot'
- ),
- 'social_action_MASP' => array(
- 'title' => array(
+ ],
+ 'social_action_MASP' => [
+ 'title' => [
'fr' => 'MASP'
- ),
+ ],
'issue' => 'social_issue_diff_fin'
- ),
- 'social_action_protection_enfant' => array(
- 'title' => array(
+ ],
+ 'social_action_protection_enfant' => [
+ 'title' => [
'fr' => 'Protection Enfant confié dans le cadre judiciaire'
- ),
+ ],
'issue' => 'social_issue_enfant_protection'
- ),
- );
+ ],
+ ];
public function load(ObjectManager $manager)
{
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialGoals.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialGoals.php
index f71d6cf3a..a6c2b0ef0 100644
--- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialGoals.php
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialGoals.php
@@ -38,20 +38,20 @@ class LoadSocialGoals extends AbstractFixture implements OrderedFixtureInterface
return 10030;
}
- public static $socialGoals = array(
- 'social_goal_instuire_dossier' => array(
- 'title' => array(
+ public static $socialGoals = [
+ 'social_goal_instuire_dossier' => [
+ 'title' => [
'fr' => 'Instruire le dossier de surendettement'
- ),
+ ],
'action' => 'social_action_MASP'
- ),
- 'social_goal_proteger' => array(
- 'title' => array(
+ ],
+ 'social_goal_proteger' => [
+ 'title' => [
'fr' => 'Protéger via une assistance educative placement'
- ),
+ ],
'action' => 'social_action_protection_enfant'
- ),
- );
+ ],
+ ];
public function load(ObjectManager $manager)
{
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialIssues.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialIssues.php
index c5b635f5b..eb9c303ed 100644
--- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialIssues.php
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialIssues.php
@@ -37,36 +37,36 @@ class LoadSocialIssues extends AbstractFixture implements OrderedFixtureInterfac
return 10010;
}
- public static $socialIssues = array(
- 'social_issue_diff_fin_or_admin' => array(
- 'title' => array(
+ public static $socialIssues = [
+ 'social_issue_diff_fin_or_admin' => [
+ 'title' => [
'fr' => 'ADULTE - DIFFICULTES FINANCIERES ET/OU ADMINISTRATIVES'
- )
- ),
- 'social_issue_prev_prot' => array(
- 'title' => array(
+ ]
+ ],
+ 'social_issue_prev_prot' => [
+ 'title' => [
'fr' => 'ADULTE PREVENTION/PROTECTION'
- ),
+ ],
'parent' => 'social_issue_diff_fin_or_admin'
- ),
- 'social_issue_diff_fin' => array(
- 'title' => array(
+ ],
+ 'social_issue_diff_fin' => [
+ 'title' => [
'fr' => 'Difficulté financière'
- ),
+ ],
'parent' => 'social_issue_diff_fin_or_admin'
- ),
- 'social_issue_enfant_famille' => array(
- 'title' => array(
+ ],
+ 'social_issue_enfant_famille' => [
+ 'title' => [
'fr' => 'Enfant / famille'
- )
- ),
- 'social_issue_enfant_protection' => array(
- 'title' => array(
+ ]
+ ],
+ 'social_issue_enfant_protection' => [
+ 'title' => [
'fr' => 'enfant - protection'
- ),
+ ],
'parent' => 'social_issue_enfant_famille'
- ),
- );
+ ],
+ ];
public function load(ObjectManager $manager)
{
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialResults.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialResults.php
index 33cff5eea..573313573 100644
--- a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialResults.php
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialResults.php
@@ -38,34 +38,34 @@ class LoadSocialResults extends AbstractFixture implements OrderedFixtureInterfa
return 10040;
}
- public static $socialResults = array(
- 'social_result_FSL_acces' => array(
- 'title' => array(
+ public static $socialResults = [
+ 'social_result_FSL_acces' => [
+ 'title' => [
'fr' => 'FSL - accès cautionnement'
- ),
+ ],
'action' => 'social_action_instruire'
- ),
- 'social_result_FSL_maintien' => array(
- 'title' => array(
+ ],
+ 'social_result_FSL_maintien' => [
+ 'title' => [
'fr' => 'FSL maintien - impayés de loyer'
- ),
+ ],
'action' => 'social_action_MASP'
- ),
- 'social_result_soutien_parental' => array(
- 'title' => array(
+ ],
+ 'social_result_soutien_parental' => [
+ 'title' => [
'fr' => 'Soutien parental'
- ),
+ ],
// 'action' => 'social_action_protection_enfant', (via le goal)
'goal' => 'social_goal_proteger'
- ),
- 'social_result_accompagnement_mineur' => array(
- 'title' => array(
+ ],
+ 'social_result_accompagnement_mineur' => [
+ 'title' => [
'fr' => 'Accompagnement du mineur'
- ),
+ ],
// 'action' => 'social_action_protection_enfant', (via le goal)
'goal' => 'social_goal_proteger',
- ),
- );
+ ],
+ ];
public function load(ObjectManager $manager)
{
diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
index 0d6346c01..27721012d 100644
--- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
+++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
@@ -476,7 +476,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
'class' => \Chill\PersonBundle\Entity\SocialWork\SocialIssue::class,
'name' => 'social_work_social_issue',
'base_path' => '/api/1.0/person/social-work/social-issue',
-// 'controller' => \Chill\PersonBundle\Controller\OpeningApiController::class,
'base_role' => 'ROLE_USER',
'actions' => [
'_index' => [
@@ -493,6 +492,28 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
],
]
],
+ [
+ 'class' => \Chill\PersonBundle\Entity\Person::class,
+ 'name' => 'person',
+ 'base_path' => '/api/1.0/person/person',
+ 'base_role' => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
+ 'controller' => \Chill\PersonBundle\Controller\PersonApiController::class,
+ 'actions' => [
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true,
+ Request::METHOD_POST=> true,
+ ],
+ 'roles' => [
+ Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
+ Request::METHOD_HEAD => \Chill\PersonBundle\Security\Authorization\PersonVoter::SEE,
+ Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\PersonVoter::CREATE,
+
+ ]
+ ],
+ ]
+ ],
]
]);
}
diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php
index 4a9f874de..98edcec14 100644
--- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php
+++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonNormalizer.php
@@ -18,7 +18,11 @@
*/
namespace Chill\PersonBundle\Serializer\Normalizer;
+use Chill\MainBundle\Entity\Center;
use Chill\PersonBundle\Entity\Person;
+use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
+use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait;
+use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
@@ -27,6 +31,7 @@ use Symfony\Component\Serializer\Exception\RuntimeException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
+use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait;
/**
* Serialize a Person entity
@@ -34,16 +39,24 @@ use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
*/
class PersonNormalizer implements
NormalizerInterface,
- NormalizerAwareInterface
+ NormalizerAwareInterface,
+ DenormalizerInterface,
+ DenormalizerAwareInterface
{
-
- protected NormalizerInterface $normalizer;
-
private ChillEntityRenderExtension $render;
- public function __construct(ChillEntityRenderExtension $render)
+ private PersonRepository $repository;
+
+ use NormalizerAwareTrait;
+
+ use ObjectToPopulateTrait;
+
+ use DenormalizerAwareTrait;
+
+ public function __construct(ChillEntityRenderExtension $render, PersonRepository $repository)
{
$this->render = $render;
+ $this->repository = $repository;
}
public function normalize($person, string $format = null, array $context = array())
@@ -59,7 +72,9 @@ class PersonNormalizer implements
'center' => $this->normalizer->normalize($person->getCenter()),
'phonenumber' => $person->getPhonenumber(),
'mobilenumber' => $person->getMobilenumber(),
- 'altNames' => $this->normalizeAltNames($person->getAltNames())
+ 'altNames' => $this->normalizeAltNames($person->getAltNames()),
+ 'gender' => $person->getGender(),
+ 'gender_numeric' => $person->getGenderNumeric(),
];
}
@@ -80,9 +95,50 @@ class PersonNormalizer implements
return $data instanceof Person;
}
-
- public function setNormalizer(NormalizerInterface $normalizer)
+ public function denormalize($data, string $type, string $format = null, array $context = [])
{
- $this->normalizer = $normalizer;
+ $person = $this->extractObjectToPopulate($type, $context);
+
+ if (\array_key_exists('id', $data)) {
+ $person = $this->repository->find($data['id']);
+
+ if (null === $person) {
+ throw new UnexpectedValueException("The person with id \"{$data['id']}\" does ".
+ "not exists");
+ }
+ // currently, not allowed to update a person through api
+ // if instantiated with id
+ return $person;
+ }
+
+ if (null === $person) {
+ $person = new Person();
+ }
+
+ foreach (['firstName', 'lastName', 'phonenumber', 'mobilenumber', 'gender']
+ as $item) {
+ if (\array_key_exists($item, $data)) {
+ $person->{'set'.\ucfirst($item)}($data[$item]);
+ }
+ }
+
+ foreach ([
+ 'birthdate' => \DateTime::class,
+ 'center' => Center::class
+ ] as $item => $class) {
+ if (\array_key_exists($item, $data)) {
+ $object = $this->denormalizer->denormalize($data[$item], $class, $format, $context);
+ if ($object instanceof $class) {
+ $person->{'set'.\ucfirst($item)}($object);
+ }
+ }
+ }
+
+ return $person;
+ }
+
+ public function supportsDenormalization($data, string $type, string $format = null)
+ {
+ return $type === Person::class && ($data['type'] ?? NULL) === 'person';
}
}
diff --git a/src/Bundle/ChillPersonBundle/Tests/Controller/PersonApiControllerTest.php b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonApiControllerTest.php
new file mode 100644
index 000000000..d261f4294
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Tests/Controller/PersonApiControllerTest.php
@@ -0,0 +1,83 @@
+getClientAuthenticated();
+
+ $client->request(Request::METHOD_GET, "/api/1.0/person/person/{$personId}.json");
+ $response = $client->getResponse();
+
+ $this->assertEquals(403, $response->getStatusCode());
+ }
+
+ /**
+ * @dataProvider dataGetPersonFromCenterA
+ */
+ public function testPersonGet($personId): void
+ {
+ $client = $this->getClientAuthenticated();
+
+ $client->request(Request::METHOD_GET, "/api/1.0/person/person/{$personId}.json");
+ $response = $client->getResponse();
+
+ $this->assertResponseIsSuccessful();
+
+ $data = \json_decode($client->getResponse()->getContent(), true);
+
+ $this->assertArrayHasKey('type', $data);
+ $this->assertArrayHasKey('id', $data);
+ $this->assertEquals('person', $data['type']);
+ $this->assertEquals($personId, $data['id']);
+ }
+
+ public function dataGetPersonFromCenterA(): \Iterator
+ {
+ self::bootKernel();
+ $em = self::$container->get(EntityManagerInterface::class);
+ $personIds= $em->createQuery("SELECT p.id FROM ".Person::class." p ".
+ "JOIN p.center c ".
+ "WHERE c.name = :center")
+ ->setParameter('center', 'Center A')
+ ->setMaxResults(100)
+ ->getScalarResult()
+ ;
+
+ \shuffle($personIds);
+
+ yield \array_pop($personIds);
+ yield \array_pop($personIds);
+ }
+
+ public function dataGetPersonFromCenterB(): \Iterator
+ {
+ self::bootKernel();
+ $em = self::$container->get(EntityManagerInterface::class);
+ $personIds= $em->createQuery("SELECT p.id FROM ".Person::class." p ".
+ "JOIN p.center c ".
+ "WHERE c.name = :center")
+ ->setParameter('center', 'Center B')
+ ->setMaxResults(100)
+ ->getScalarResult()
+ ;
+
+ \shuffle($personIds);
+
+ yield \array_pop($personIds);
+ yield \array_pop($personIds);
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml
index a5636d93e..c3067be55 100644
--- a/src/Bundle/ChillPersonBundle/chill.api.specs.yaml
+++ b/src/Bundle/ChillPersonBundle/chill.api.specs.yaml
@@ -41,6 +41,11 @@ components:
properties:
id:
type: integer
+ readOnly: true
+ type:
+ type: string
+ enum:
+ - 'person'
firstName:
type: string
lastName:
@@ -48,12 +53,23 @@ components:
text:
type: string
description: a canonical representation for the person name
+ readOnly: true
birthdate:
$ref: '#/components/schemas/Date'
phonenumber:
type: string
mobilenumber:
type: string
+ gender:
+ type: string
+ enum:
+ - man
+ - woman
+ - both
+ gender_numeric:
+ type: integer
+ description: a numerical representation of gender
+ readOnly: true
PersonById:
type: object
properties:
@@ -178,6 +194,53 @@ components:
readOnly: true
paths:
+ /1.0/person/person/{id}.json:
+ get:
+ tags:
+ - person
+ summary: Get a single person
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: The person's id
+ schema:
+ type: integer
+ format: integer
+ minimum: 1
+ responses:
+ 200:
+ description: "OK"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Person"
+ 403:
+ description: "Unauthorized"
+ /1.0/person/person.json:
+ post:
+ tags:
+ - person
+ summary: Create a single person
+ requestBody:
+ description: "A person"
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Person'
+ responses:
+ 200:
+ description: "OK"
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/Person"
+ 403:
+ description: "Unauthorized"
+ 422:
+ description: "Invalid data: the data is a valid json, could be deserialized, but does not pass validation"
+
/1.0/person/social-work/social-issue.json:
get:
tags:
diff --git a/src/Bundle/ChillPersonBundle/config/services/controller.yaml b/src/Bundle/ChillPersonBundle/config/services/controller.yaml
index fd5e1f952..311950883 100644
--- a/src/Bundle/ChillPersonBundle/config/services/controller.yaml
+++ b/src/Bundle/ChillPersonBundle/config/services/controller.yaml
@@ -53,3 +53,9 @@ services:
$validator: '@Symfony\Component\Validator\Validator\ValidatorInterface'
$registry: '@Symfony\Component\Workflow\Registry'
tags: ['controller.service_arguments']
+
+ Chill\PersonBundle\Controller\PersonApiController:
+ arguments:
+ $authorizationHelper: '@Chill\MainBundle\Security\Authorization\AuthorizationHelper'
+ tags: ['controller.service_arguments']
+