diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2172dbd03..4b6da4216 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -18,7 +18,7 @@ before_script:
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
# See http://docs.gitlab.com/ee/ci/services/README.html for examples.
services:
- - name: postgres:12
+ - name: postgis/postgis:12-3.1-alpine
alias: db
- name: redis
alias: redis
diff --git a/composer.json b/composer.json
index 4181af68a..95074c18f 100644
--- a/composer.json
+++ b/composer.json
@@ -73,7 +73,8 @@
"symfony/web-profiler-bundle": "^5.0",
"symfony/var-dumper": "4.*",
"symfony/debug-bundle": "^5.1",
- "symfony/phpunit-bridge": "^5.2"
+ "symfony/phpunit-bridge": "^5.2",
+ "nelmio/alice": "^3.8"
},
"scripts": {
"auto-scripts": {
diff --git a/docs/source/development/api.rst b/docs/source/development/api.rst
index 86eb6ff65..a5fef2372 100644
--- a/docs/source/development/api.rst
+++ b/docs/source/development/api.rst
@@ -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 `_
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[^/]++)/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 `).
+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 `).
.. 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
diff --git a/docs/source/installation/index.rst b/docs/source/installation/index.rst
index 6aee5c526..1312480e3 100644
--- a/docs/source/installation/index.rst
+++ b/docs/source/installation/index.rst
@@ -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:
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 9d74dd61a..ab9e69052 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -18,11 +18,10 @@
src/Bundle/ChillMainBundle/Tests/
-
diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
index 5cc055f26..71476fb78 100644
--- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
+++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php
@@ -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,33 @@ 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;
+ }
+
+ /**
+ * 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;
}
/**
@@ -222,4 +243,9 @@ class AbstractCRUDController extends AbstractController
{
return $this->container->get('chill_main.paginator_factory');
}
+
+ protected function getValidator(): ValidatorInterface
+ {
+ return $this->get('validator');
+ }
}
diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
index 7643db0e9..9686a7b98 100644
--- a/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
+++ b/src/Bundle/ChillMainBundle/CRUD/Controller/ApiController.php
@@ -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,12 +80,186 @@ 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);
+ 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);
+
+ $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 +345,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 +466,27 @@ 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:
+ case Request::METHOD_POST:
+ 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' ]];
}
/**
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/Controller/SearchController.php b/src/Bundle/ChillMainBundle/Controller/SearchController.php
index a44af8e7b..45390ceca 100644
--- a/src/Bundle/ChillMainBundle/Controller/SearchController.php
+++ b/src/Bundle/ChillMainBundle/Controller/SearchController.php
@@ -22,6 +22,7 @@
namespace Chill\MainBundle\Controller;
+use Chill\MainBundle\Serializer\Model\Collection;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\Search\UnknowSearchDomainException;
@@ -34,6 +35,7 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Chill\MainBundle\Search\SearchProvider;
use Symfony\Contracts\Translation\TranslatorInterface;
use Chill\MainBundle\Pagination\PaginatorFactory;
+use Chill\MainBundle\Search\SearchApi;
/**
* Class SearchController
@@ -42,32 +44,24 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
*/
class SearchController extends AbstractController
{
- /**
- *
- * @var SearchProvider
- */
- protected $searchProvider;
+ protected SearchProvider $searchProvider;
- /**
- *
- * @var TranslatorInterface
- */
- protected $translator;
+ protected TranslatorInterface $translator;
- /**
- *
- * @var PaginatorFactory
- */
- protected $paginatorFactory;
+ protected PaginatorFactory $paginatorFactory;
+
+ protected SearchApi $searchApi;
function __construct(
SearchProvider $searchProvider,
TranslatorInterface $translator,
- PaginatorFactory $paginatorFactory
+ PaginatorFactory $paginatorFactory,
+ SearchApi $searchApi
) {
$this->searchProvider = $searchProvider;
$this->translator = $translator;
$this->paginatorFactory = $paginatorFactory;
+ $this->searchApi = $searchApi;
}
@@ -152,6 +146,19 @@ class SearchController extends AbstractController
array('results' => $results, 'pattern' => $pattern)
);
}
+
+ public function searchApi(Request $request, $_format): JsonResponse
+ {
+ //TODO this is an incomplete implementation
+ $query = $request->query->get('q', '');
+
+ $results = $this->searchApi->getResults($query, 0, 150);
+ $paginator = $this->paginatorFactory->create(count($results));
+
+ $collection = new Collection($results, $paginator);
+
+ return $this->json($collection);
+ }
public function advancedSearchListAction(Request $request)
{
diff --git a/src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadCenters.php b/src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadCenters.php
index 1da1af2f3..a3c01fdf6 100644
--- a/src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadCenters.php
+++ b/src/Bundle/ChillMainBundle/DataFixtures/ORM/LoadCenters.php
@@ -55,11 +55,11 @@ class LoadCenters extends AbstractFixture implements OrderedFixtureInterface
public function load(ObjectManager $manager)
{
foreach (static::$centers as $new) {
- $centerA = new Center();
- $centerA->setName($new['name']);
+ $center = new Center();
+ $center->setName($new['name']);
- $manager->persist($centerA);
- $this->addReference($new['ref'], $centerA);
+ $manager->persist($center);
+ $this->addReference($new['ref'], $center);
static::$refs[] = $new['ref'];
}
diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
index a40221263..5644138e6 100644
--- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
+++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php
@@ -35,6 +35,9 @@ 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;
/**
* Class ChillMainExtension
@@ -133,7 +136,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
$loader->load('services/search.yaml');
$loader->load('services/serializer.yaml');
- $this->configureCruds($container, $config['cruds'], $config['apis'], $loader);
+ $this->configureCruds($container, $config['cruds'], $config['apis'], $loader);
}
/**
@@ -166,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(
@@ -212,6 +227,9 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
$container->prependExtensionConfig('monolog', array(
'channels' => array('chill')
));
+
+ //add crud api
+ $this->prependCruds($container);
}
/**
@@ -235,4 +253,97 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
// Note: the controller are loaded inside compiler pass
}
+
+
+ /**
+ * @param ContainerBuilder $container
+ */
+ protected function prependCruds(ContainerBuilder $container)
+ {
+ $container->prependExtensionConfig('chill_main', [
+ 'apis' => [
+ [
+ 'class' => \Chill\MainBundle\Entity\Address::class,
+ 'name' => 'address',
+ 'base_path' => '/api/1.0/main/address',
+ 'base_role' => 'ROLE_USER',
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ],
+ ],
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_POST => true,
+ Request::METHOD_HEAD => true
+ ]
+ ],
+ ]
+ ],
+ [
+ 'class' => \Chill\MainBundle\Entity\AddressReference::class,
+ 'name' => 'address_reference',
+ 'base_path' => '/api/1.0/main/address-reference',
+ 'base_role' => 'ROLE_USER',
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ],
+ ],
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ]
+ ],
+ ]
+ ],
+ [
+ 'class' => \Chill\MainBundle\Entity\PostalCode::class,
+ 'name' => 'postal_code',
+ 'base_path' => '/api/1.0/main/postal-code',
+ 'base_role' => 'ROLE_USER',
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ],
+ ],
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ]
+ ],
+ ]
+ ],
+ [
+ 'class' => \Chill\MainBundle\Entity\Country::class,
+ 'name' => 'country',
+ 'base_path' => '/api/1.0/main/country',
+ 'base_role' => 'ROLE_USER',
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ],
+ ],
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ]
+ ],
+ ]
+ ]
+ ]
+ ]);
+ }
}
diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php b/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
index c7e4c00ef..4c90aaabb 100644
--- a/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
+++ b/src/Bundle/ChillMainBundle/DependencyInjection/Configuration.php
@@ -221,6 +221,7 @@ class Configuration implements ConfigurationInterface
->booleanNode(Request::METHOD_POST)->defaultFalse()->end()
->booleanNode(Request::METHOD_DELETE)->defaultFalse()->end()
->booleanNode(Request::METHOD_PUT)->defaultFalse()->end()
+ ->booleanNode(Request::METHOD_PATCH)->defaultFalse()->end()
->end()
->end()
->arrayNode('roles')
@@ -232,6 +233,7 @@ class Configuration implements ConfigurationInterface
->scalarNode(Request::METHOD_POST)->defaultNull()->end()
->scalarNode(Request::METHOD_DELETE)->defaultNull()->end()
->scalarNode(Request::METHOD_PUT)->defaultNull()->end()
+ ->scalarNode(Request::METHOD_PATCH)->defaultNull()->end()
->end()
->end()
->end()
diff --git a/src/Bundle/ChillMainBundle/Doctrine/Event/TrackCreateUpdateSubscriber.php b/src/Bundle/ChillMainBundle/Doctrine/Event/TrackCreateUpdateSubscriber.php
new file mode 100644
index 000000000..3926e2062
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Doctrine/Event/TrackCreateUpdateSubscriber.php
@@ -0,0 +1,65 @@
+security = $security;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getSubscribedEvents()
+ {
+ return [
+ Events::prePersist,
+ Events::preUpdate
+ ];
+ }
+
+ public function prePersist(LifecycleEventArgs $args): void
+ {
+ $object = $args->getObject();
+
+ if ($object instanceof TrackCreationInterface
+ && $this->security->getUser() instanceof User) {
+ $object->setCreatedBy($this->security->getUser());
+ $object->setCreatedAt(new \DateTimeImmutable('now'));
+ }
+
+ $this->onUpdate($object);
+ }
+
+ public function preUpdate(LifecycleEventArgs $args): void
+ {
+ $object = $args->getObject();
+
+ $this->onUpdate($object);
+ }
+
+ protected function onUpdate(object $object): void
+ {
+ if ($object instanceof TrackUpdateInterface
+ && $this->security->getUser() instanceof User) {
+ $object->setUpdatedBy($this->security->getUser());
+ $object->setUpdatedAt(new \DateTimeImmutable('now'));
+ }
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Doctrine/Model/TrackCreationInterface.php b/src/Bundle/ChillMainBundle/Doctrine/Model/TrackCreationInterface.php
new file mode 100644
index 000000000..192d4e7a9
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Doctrine/Model/TrackCreationInterface.php
@@ -0,0 +1,12 @@
+
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "scope"=Scope::class
+ * })
*/
class Scope
{
@@ -40,6 +43,7 @@ class Scope
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
+ * @Groups({"read"})
*/
private $id;
@@ -49,6 +53,7 @@ class Scope
* @var array
*
* @ORM\Column(type="json_array")
+ * @Groups({"read"})
*/
private $name = [];
diff --git a/src/Bundle/ChillMainBundle/Entity/User.php b/src/Bundle/ChillMainBundle/Entity/User.php
index 1a46b30a5..11d168f84 100644
--- a/src/Bundle/ChillMainBundle/Entity/User.php
+++ b/src/Bundle/ChillMainBundle/Entity/User.php
@@ -7,6 +7,7 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
+use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/**
* User
@@ -14,6 +15,9 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
* @ORM\Entity(repositoryClass="Chill\MainBundle\Repository\UserRepository")
* @ORM\Table(name="users")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="acl_cache_region")
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "user"=User::class
+ * })
*/
class User implements AdvancedUserInterface {
diff --git a/src/Bundle/ChillMainBundle/Resources/public/modules/bootstrap/bootstrap.scss b/src/Bundle/ChillMainBundle/Resources/public/modules/bootstrap/bootstrap.scss
index 4d3b86ed3..8a77ac48f 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/modules/bootstrap/bootstrap.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/modules/bootstrap/bootstrap.scss
@@ -28,7 +28,7 @@
// @import "bootstrap/scss/card";
// @import "bootstrap/scss/breadcrumb";
// @import "bootstrap/scss/pagination";
-// @import "bootstrap/scss/badge";
+@import "bootstrap/scss/badge";
// @import "bootstrap/scss/jumbotron";
// @import "bootstrap/scss/alert";
// @import "bootstrap/scss/progress";
@@ -41,7 +41,7 @@
// @import "bootstrap/scss/popover";
// @import "bootstrap/scss/carousel";
// @import "bootstrap/scss/spinners";
-// @import "bootstrap/scss/utilities";
+@import "bootstrap/scss/utilities";
// @import "bootstrap/scss/print";
@import "custom";
diff --git a/src/Bundle/ChillMainBundle/Resources/public/modules/scratch/custom/modules/_buttons.scss b/src/Bundle/ChillMainBundle/Resources/public/modules/scratch/custom/modules/_buttons.scss
index e94aa494e..0b7af263b 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/modules/scratch/custom/modules/_buttons.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/modules/scratch/custom/modules/_buttons.scss
@@ -5,7 +5,7 @@
@include button($green, $white);
}
- &.bt-reset, &.bt-delete {
+ &.bt-reset, &.bt-delete, &.bt-remove {
@include button($red, $white);
}
@@ -24,6 +24,7 @@
&.bt-save::before,
&.bt-new::before,
&.bt-delete::before,
+ &.bt-remove::before,
&.bt-update::before,
&.bt-edit::before,
&.bt-cancel::before,
@@ -56,7 +57,12 @@
// add a trash
content: "";
}
-
+
+ &.bt-remove::before {
+ // add a times
+ content: "";
+ }
+
&.bt-edit::before, &.bt-update::before {
// add a pencil
content: "";
@@ -94,6 +100,7 @@
&.bt-save::before,
&.bt-new::before,
&.bt-delete::before,
+ &.bt-remove::before,
&.bt-update::before,
&.bt-edit::before,
&.bt-cancel::before,
@@ -123,6 +130,7 @@
&.bt-save::before,
&.bt-new::before,
&.bt-delete::before,
+ &.bt-remove::before,
&.bt-update::before,
&.bt-edit::before,
&.bt-cancel::before,
diff --git a/src/Bundle/ChillMainBundle/Resources/public/scss/chillmain.scss b/src/Bundle/ChillMainBundle/Resources/public/scss/chillmain.scss
index 4f9ecf46d..6aa0e1c8d 100644
--- a/src/Bundle/ChillMainBundle/Resources/public/scss/chillmain.scss
+++ b/src/Bundle/ChillMainBundle/Resources/public/scss/chillmain.scss
@@ -13,9 +13,9 @@
div#header-accompanying_course-name {
background: none repeat scroll 0 0 #718596;
color: #FFF;
- padding-top: 1em;
- padding-bottom: 1em;
-
+ h1 {
+ margin: 0.4em 0;
+ }
span {
a {
color: white;
@@ -39,19 +39,25 @@ div.subheader {
height: 130px;
}
-div.vue-component {
- padding: 1.5em;
- margin: 2em 0;
- border: 2px dashed grey;
- position: relative;
- &:before {
- content: "vuejs component";
- position: absolute;
- left: 1.5em;
- top: -0.9em;
- background-color: white;
- color: grey;
- padding: 0 0.3em;
- }
- dd { margin-left: 1em; }
+//// SCRATCH BUTTONS
+.sc-button {
+ &.disabled {
+ cursor: default;
+ &.bt-remove {
+ background-color: #d9d9d9;
+ }
+ }
+}
+
+//// à ranger
+.discret {
+ color: grey;
+ margin-right: 1em;
+}
+
+table {
+ ul.record_actions {
+ margin: 0;
+ padding: 0.5em;
+ }
}
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue
new file mode 100644
index 000000000..461b0038e
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/App.vue
@@ -0,0 +1,41 @@
+
+
+
+ {{ address.address.street }}, {{ address.address.streetNumber }}
+
+
+ {{ address.city.code }} {{ address.city.name }}
+
+
+ {{ address.country.name }}
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/index.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/index.js
new file mode 100644
index 000000000..27676369e
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/index.js
@@ -0,0 +1,16 @@
+import { createApp } from 'vue'
+import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
+import { addressMessages } from './js/i18n'
+import { store } from './store'
+
+import App from './App.vue';
+
+const i18n = _createI18n(addressMessages);
+
+const app = createApp({
+ template: ` `,
+})
+.use(store)
+.use(i18n)
+.component('app', App)
+.mount('#address');
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/js/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/js/i18n.js
new file mode 100644
index 000000000..87ee9a5c0
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/js/i18n.js
@@ -0,0 +1,22 @@
+const addressMessages = {
+ fr: {
+ add_an_address: 'Ajouter une adresse',
+ select_an_address: 'Sélectionner une adresse',
+ fill_an_address: 'Compléter l\'adresse',
+ select_country: 'Choisir le pays',
+ select_city: 'Choisir une localité',
+ select_address: 'Choisir une adresse',
+ isNoAddress: 'L\'adresse n\'est pas celle d\'un domicile fixe ?',
+ floor: 'Étage',
+ corridor: 'Couloir',
+ steps: 'Escalier',
+ flat: 'Appartement',
+ buildingName: 'Nom du batiment',
+ extra: 'Complément d\'adresse',
+ distribution: 'Service particulier de distribution'
+ }
+};
+
+export {
+ addressMessages
+};
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/store/index.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/store/index.js
new file mode 100644
index 000000000..b9466b50c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/Address/store/index.js
@@ -0,0 +1,43 @@
+import 'es6-promise/auto';
+import { createStore } from 'vuex';
+
+// le fetch POST serait rangé dans la logique du composant qui appelle AddAddress
+//import { postAddress } from '... api'
+
+const debug = process.env.NODE_ENV !== 'production';
+
+const store = createStore({
+ strict: debug,
+ state: {
+ address: {},
+ errorMsg: {}
+ },
+ getters: {
+ },
+ mutations: {
+ addAddress(state, address) {
+ console.log('@M addAddress address', address);
+ state.address = address;
+ }
+ },
+ actions: {
+ addAddress({ commit }, payload) {
+ console.log('@A addAddress payload', payload);
+ commit('addAddress', payload); // à remplacer par
+
+ // fetch POST qui envoie l'adresse, et récupère la confirmation que c'est ok.
+ // La confirmation est l'adresse elle-même.
+ //
+ // postAddress(payload)
+ // .fetch(address => new Promise((resolve, reject) => {
+ // commit('addAddress', address);
+ // resolve();
+ // }))
+ // .catch((error) => {
+ // state.errorMsg.push(error.message);
+ // });
+ }
+ }
+});
+
+export { store };
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_api/AddAddress.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_api/AddAddress.js
new file mode 100644
index 000000000..4de0fcc19
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_api/AddAddress.js
@@ -0,0 +1,46 @@
+/*
+* Endpoint countries GET
+* TODO
+*/
+const fetchCountries = () => {
+ console.log('<<< fetching countries');
+ return [
+ {id: 1, name: 'France', countryCode: 'FR'},
+ {id: 2, name: 'Belgium', countryCode: 'BE'}
+ ];
+};
+
+/*
+* Endpoint cities GET
+* TODO
+*/
+const fetchCities = (country) => {
+ console.log('<<< fetching cities for', country);
+ return [
+ {id: 1, name: 'Bruxelles', code: '1000', country: 'BE'},
+ {id: 2, name: 'Aisne', code: '85045', country: 'FR'},
+ {id: 3, name: 'Saint-Gervais', code: '85230', country: 'FR'}
+ ];
+};
+
+/*
+* Endpoint chill_main_address_reference_api_show
+* method GET, get AddressReference Object
+* @returns {Promise} a promise containing all AddressReference object
+*/
+const fetchReferenceAddresses = (city) => {
+ console.log('<<< fetching references addresses for', city); // city n'est pas utilisé pour le moment
+
+ const url = `/api/1.0/main/address-reference.json`;
+ return fetch(url)
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+export {
+ fetchCountries,
+ fetchCities,
+ fetchReferenceAddresses
+};
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress.vue
new file mode 100644
index 000000000..55cf2f098
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress.vue
@@ -0,0 +1,219 @@
+
+
+ {{ $t('add_an_address') }}
+
+
+
+
+
+
+ {{ $t('add_an_address') }}
+
+
+
+
+ {{ $t('select_an_address') }}
+
+
+
+ {{ $t('isNoAddress') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('action.add')}}
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressMap.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressMap.vue
new file mode 100644
index 000000000..5b616819d
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressMap.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressMore.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressMore.vue
new file mode 100644
index 000000000..b216d5ed0
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressMore.vue
@@ -0,0 +1,112 @@
+
+
+
{{ $t('fill_an_address') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressSelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressSelection.vue
new file mode 100644
index 000000000..71738079c
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/AddressSelection.vue
@@ -0,0 +1,38 @@
+
+
+
+ {{ $t('select_address') }}
+
+ {{ item.street }}, {{ item.streetNumber }}
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/CitySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/CitySelection.vue
new file mode 100644
index 000000000..99e46db08
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/CitySelection.vue
@@ -0,0 +1,38 @@
+
+
+
+ {{ $t('select_city') }}
+
+ {{ item.code }}-{{ item.name }}
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/CountrySelection.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/CountrySelection.vue
new file mode 100644
index 000000000..4eb591135
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/AddAddress/CountrySelection.vue
@@ -0,0 +1,38 @@
+
+
+
+ {{ $t('select_country') }}
+
+ {{ item.name }}
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue
new file mode 100644
index 000000000..7498d66f3
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_components/Modal.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.js b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.js
new file mode 100644
index 000000000..319378d7e
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/_js/i18n.js
@@ -0,0 +1,63 @@
+import { createI18n } from 'vue-i18n'
+
+const datetimeFormats = {
+ fr: {
+ short: {
+ year: "numeric",
+ month: "numeric",
+ day: "numeric"
+ },
+ text: {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ },
+ long: {
+ year: "numeric",
+ month: "numeric",
+ day: "numeric",
+ hour: "numeric",
+ minute: "numeric",
+ hour12: false
+ }
+ }
+};
+const messages = {
+ fr: {
+ action: {
+ actions: "Actions",
+ show: "Voir",
+ edit: "Modifier",
+ create: "Créer",
+ remove: "Enlever",
+ delete: "Supprimer",
+ save: "Enregistrer",
+ add: "Ajouter",
+ show_modal: "Ouvrir une modale",
+ ok: "OK",
+ cancel: "Annuler",
+ close: "Fermer",
+ back: "Retour",
+ check_all: "cocher tout",
+ reset: "réinitialiser"
+ },
+ nav: {
+ next: "Suivant",
+ previous: "Précédent",
+ top: "Haut",
+ bottom: "Bas",
+ }
+ }
+};
+
+const _createI18n = (appMessages) => {
+ Object.assign(messages.fr, appMessages.fr);
+ return createI18n({
+ locale: 'fr',
+ fallbackLocale: 'fr',
+ datetimeFormats,
+ messages,
+ })
+};
+
+export { _createI18n }
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Layout/_footer.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Layout/_footer.html.twig
index 132e81a3e..0d23d499d 100644
--- a/src/Bundle/ChillMainBundle/Resources/views/Layout/_footer.html.twig
+++ b/src/Bundle/ChillMainBundle/Resources/views/Layout/_footer.html.twig
@@ -1,4 +1,4 @@
\ No newline at end of file
+ {{ 'User manual'|trans }}
+
diff --git a/src/Bundle/ChillMainBundle/Resources/views/Layout/_header-logo.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Layout/_header-logo.html.twig
index be59b454f..f619dc151 100644
--- a/src/Bundle/ChillMainBundle/Resources/views/Layout/_header-logo.html.twig
+++ b/src/Bundle/ChillMainBundle/Resources/views/Layout/_header-logo.html.twig
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/src/Bundle/ChillMainBundle/Search/Model/Result.php b/src/Bundle/ChillMainBundle/Search/Model/Result.php
new file mode 100644
index 000000000..a88c2f55f
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Search/Model/Result.php
@@ -0,0 +1,36 @@
+relevance = $relevance;
+ $this->result = $result;
+ }
+
+ public function getRelevance(): float
+ {
+ return $this->relevance;
+ }
+
+ public function getResult()
+ {
+ return $this->result;
+ }
+
+
+
+}
diff --git a/src/Bundle/ChillMainBundle/Search/SearchApi.php b/src/Bundle/ChillMainBundle/Search/SearchApi.php
new file mode 100644
index 000000000..518edd31b
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Search/SearchApi.php
@@ -0,0 +1,89 @@
+em = $em;
+ $this->search = $search;
+ }
+
+ /**
+ * @return Model/Result[]
+ */
+ public function getResults(string $query, int $offset, int $maxResult): array
+ {
+ // **warning again**: this is an incomplete implementation
+ $results = [];
+
+ foreach ($this->getPersons($query) as $p) {
+ $results[] = new Model\Result((float)\rand(0, 100) / 100, $p);
+ }
+ foreach ($this->getThirdParties($query) as $t) {
+ $results[] = new Model\Result((float)\rand(0, 100) / 100, $t);
+ }
+
+ \usort($results, function(Model\Result $a, Model\Result $b) {
+ return ($a->getRelevance() <=> $b->getRelevance()) * -1;
+ });
+
+ return $results;
+ }
+
+ public function countResults(string $query): int
+ {
+ return 0;
+ }
+
+ private function getThirdParties(string $query)
+ {
+ $thirdPartiesIds = $this->em->createQuery('SELECT t.id FROM '.ThirdParty::class.' t')
+ ->getScalarResult();
+ $nbResults = rand(0, 15);
+
+ if ($nbResults === 1) {
+ $nbResults++;
+ } elseif ($nbResults === 0) {
+ return [];
+ }
+ $ids = \array_map(function ($e) use ($thirdPartiesIds) { return $thirdPartiesIds[$e]['id'];},
+ \array_rand($thirdPartiesIds, $nbResults));
+
+ $a = $this->em->getRepository(ThirdParty::class)
+ ->findById($ids);
+ return $a;
+ }
+
+ private function getPersons(string $query)
+ {
+ $params = [
+ SearchInterface::SEARCH_PREVIEW_OPTION => false
+ ];
+ $search = $this->search->getResultByName($query, 'person_regular', 0, 50, $params, 'json');
+ $ids = \array_map(function($r) { return $r['id']; }, $search['results']);
+
+
+ if (count($ids) === 0) {
+ return [];
+ }
+
+ return $this->em->getRepository(Person::class)
+ ->findById($ids)
+ ;
+ }
+
+}
diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php
new file mode 100644
index 000000000..29d760b71
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/AddressNormalizer.php
@@ -0,0 +1,29 @@
+getId();
+ $data['text'] = $address->getStreet().', '.$address->getBuildingName();
+ $data['postcode']['name'] = $address->getPostCode()->getName();
+
+ return $data;
+ }
+
+ public function supportsNormalization($data, string $format = null)
+ {
+ return $data instanceof Address;
+ }
+
+
+}
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/Serializer/Normalizer/DateNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DateNormalizer.php
index fd902b184..8e333a2a4 100644
--- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/DateNormalizer.php
+++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DateNormalizer.php
@@ -20,15 +20,16 @@
namespace Chill\MainBundle\Serializer\Normalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
+use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
+use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
-class DateNormalizer implements NormalizerInterface
+class DateNormalizer implements NormalizerInterface, DenormalizerInterface
{
public function normalize($date, string $format = null, array $context = array())
{
/** @var \DateTimeInterface $date */
return [
- 'datetime' => $date->format(\DateTimeInterface::ISO8601),
- 'u' => $date->getTimestamp()
+ 'datetime' => $date->format(\DateTimeInterface::ISO8601)
];
}
@@ -36,4 +37,24 @@ class DateNormalizer implements NormalizerInterface
{
return $data instanceof \DateTimeInterface;
}
+
+ public function denormalize($data, string $type, string $format = null, array $context = [])
+ {
+ switch ($type) {
+ case \DateTime::class:
+ return \DateTime::createFromFormat(\DateTimeInterface::ISO8601, $data['datetime']);
+ case \DateTimeInterface::class:
+ case \DateTimeImmutable::class:
+ default:
+ return \DateTimeImmutable::createFromFormat(\DateTimeInterface::ISO8601, $data['datetime']);
+ }
+ }
+
+ public function supportsDenormalization($data, string $type, string $format = null): bool
+ {
+ return $type === \DateTimeInterface::class ||
+ $type === \DateTime::class ||
+ $type === \DateTimeImmutable::class ||
+ (\is_array($data) && array_key_exists('datetime', $data));
+ }
}
diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/DiscriminatedObjectDenormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DiscriminatedObjectDenormalizer.php
new file mode 100644
index 000000000..25b2c5017
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DiscriminatedObjectDenormalizer.php
@@ -0,0 +1,71 @@
+denormalizer->supportsDenormalization($data, $localType, $format)) {
+ try {
+ return $this->denormalizer->denormalize($data, $localType, $format, $context); } catch (RuntimeException $e) {
+ $lastException = $e;
+ }
+ }
+ }
+
+ throw new RuntimeException(sprintf("Could not find any denormalizer for those ".
+ "ALLOWED_TYPES: %s", \implode(", ", $context[self::ALLOWED_TYPES])));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function supportsDenormalization($data, string $type, string $format = null, array $context = [])
+ {
+ if (self::TYPE !== $type) {
+ return false;
+ }
+
+ if (0 === count($context[self::ALLOWED_TYPES] ?? [])) {
+ throw new \LogicException("The context should contains a list of
+ allowed types");
+ }
+
+ foreach ($context[self::ALLOWED_TYPES] as $localType) {
+ if ($this->denormalizer->supportsDenormalization($data, $localType, $format)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/DoctrineExistingEntityNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DoctrineExistingEntityNormalizer.php
new file mode 100644
index 000000000..cb2635802
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/DoctrineExistingEntityNormalizer.php
@@ -0,0 +1,71 @@
+em = $em;
+ $this->serializerMetadataFactory = $serializerMetadataFactory;
+ }
+
+ public function denormalize($data, string $type, string $format = null, array $context = [])
+ {
+ if (\array_key_exists(AbstractNormalizer::OBJECT_TO_POPULATE, $context)) {
+ return $context[AbstractNormalizer::OBJECT_TO_POPULATE];
+ }
+
+ return $this->em->getRepository($type)
+ ->find($data['id']);
+ }
+
+ public function supportsDenormalization($data, string $type, string $format = null)
+ {
+ if (FALSE === \is_array($data)) {
+ return false;
+ }
+
+ if (FALSE === \array_key_exists('id', $data)) {
+ return false;
+ }
+
+ if (FALSE === $this->em->getClassMetadata($type) instanceof ClassMetadata) {
+ return false;
+ }
+
+ // does have serializer metadata, and class discriminator ?
+ if ($this->serializerMetadataFactory->hasMetadataFor($type)) {
+
+ $classDiscriminator = $this->serializerMetadataFactory
+ ->getMetadataFor($type)->getClassDiscriminatorMapping();
+
+ if ($classDiscriminator) {
+ $typeProperty = $classDiscriminator->getTypeProperty();
+
+ // check that only 2 keys
+ // that the second key is property
+ // and that the type match the class for given type property
+ return count($data) === 2
+ && \array_key_exists($typeProperty, $data)
+ && $type === $classDiscriminator->getClassForType($data[$typeProperty]);
+ }
+ }
+
+ // we do not have any class discriminator. Check that the id is the only one key
+ return count($data) === 1;
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php
index 2a71de52b..4c1dc1523 100644
--- a/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php
+++ b/src/Bundle/ChillMainBundle/Serializer/Normalizer/UserNormalizer.php
@@ -24,7 +24,7 @@ use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
*
- *
+ * @internal we keep this normalizer, because the property 'text' may be replace by a rendering in the future
*/
class UserNormalizer implements NormalizerInterface
{
@@ -32,8 +32,10 @@ class UserNormalizer implements NormalizerInterface
{
/** @var User $user */
return [
+ 'type' => 'user',
'id' => $user->getId(),
- 'username' => $user->getUsername()
+ 'username' => $user->getUsername(),
+ 'text' => $user->getUsername()
];
}
diff --git a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php
index d258c0d30..e480ab145 100644
--- a/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php
+++ b/src/Bundle/ChillMainBundle/Test/Export/AbstractExportTest.php
@@ -172,6 +172,7 @@ abstract class AbstractExportTest extends WebTestCase
*/
public function testInitiateQuery($modifiers, $acl, $data)
{
+ var_dump($data);
$query = $this->getExport()->initiateQuery($modifiers, $acl, $data);
$this->assertTrue($query instanceof QueryBuilder || $query instanceof NativeQuery,
diff --git a/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/DoctrineExistingEntityNormalizerTest.php b/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/DoctrineExistingEntityNormalizerTest.php
new file mode 100644
index 000000000..c4f4bc13d
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/Tests/Serializer/Normalizer/DoctrineExistingEntityNormalizerTest.php
@@ -0,0 +1,51 @@
+get(EntityManagerInterface::class);
+ $serializerFactory = self::$container->get(ClassMetadataFactoryInterface::class);
+
+ $this->normalizer = new DoctrineExistingEntityNormalizer($em, $serializerFactory);
+ }
+
+ /**
+ * @dataProvider dataProviderUserId
+ */
+ public function testGetMappedClass($userId)
+ {
+ $data = [ 'type' => 'user', 'id' => $userId];
+ $supports = $this->normalizer->supportsDenormalization($data, User::class);
+
+ $this->assertTrue($supports);
+ }
+
+ public function dataProviderUserId()
+ {
+ self::bootKernel();
+
+ $userIds = self::$container->get(EntityManagerInterface::class)
+ ->getRepository(User::class)
+ ->createQueryBuilder('u')
+ ->select('u.id')
+ ->setMaxResults(1)
+ ->getQuery()
+ ->getResult()
+ ;
+
+ yield [ $userIds[0]['id'] ];
+ }
+}
diff --git a/src/Bundle/ChillMainBundle/chill.api.specs.yaml b/src/Bundle/ChillMainBundle/chill.api.specs.yaml
new file mode 100644
index 000000000..68a3eb764
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/chill.api.specs.yaml
@@ -0,0 +1,58 @@
+---
+openapi: "3.0.0"
+info:
+ version: "1.0.0"
+ title: "Chill api"
+ description: "Api documentation for chill. Currently, work in progress"
+servers:
+ - url: "/api"
+ description: "Your current dev server"
+
+components:
+ schemas:
+ Center:
+ type: object
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+
+paths:
+ /1.0/search.json:
+ get:
+ summary: perform a search across multiple entities
+ tags:
+ - search
+ - person
+ - thirdparty
+ description: >
+ **Warning**: This is currently a stub (not really implemented
+
+ The search is performed across multiple entities. The entities must be listed into
+ `type` parameters.
+
+ The results are ordered by relevance, from the most to the lowest relevant.
+
+ parameters:
+ - name: q
+ in: query
+ required: true
+ description: the pattern to search
+ schema:
+ type: string
+ - name: type[]
+ in: query
+ required: true
+ description: the type entities amongst the search is performed
+ schema:
+ type: array
+ items:
+ type: string
+ enum:
+ - person
+ - thirdparty
+ responses:
+ 200:
+ description: "OK"
+
diff --git a/src/Bundle/ChillMainBundle/chill.webpack.config.js b/src/Bundle/ChillMainBundle/chill.webpack.config.js
index 6187b31ea..78accf004 100644
--- a/src/Bundle/ChillMainBundle/chill.webpack.config.js
+++ b/src/Bundle/ChillMainBundle/chill.webpack.config.js
@@ -62,5 +62,7 @@ module.exports = function(encore, entries)
buildCKEditor(encore);
encore.addEntry('ckeditor5', __dirname + '/Resources/public/modules/ckeditor5/index.js');
+ // Address
+ encore.addEntry('address', __dirname + '/Resources/public/vuejs/Address/index.js');
};
diff --git a/src/Bundle/ChillMainBundle/config/routes.yaml b/src/Bundle/ChillMainBundle/config/routes.yaml
index 3fd7eafab..bc3187add 100644
--- a/src/Bundle/ChillMainBundle/config/routes.yaml
+++ b/src/Bundle/ChillMainBundle/config/routes.yaml
@@ -69,6 +69,13 @@ chill_main_search:
requirements:
_format: html|json
+chill_main_search_global:
+ path: '/api/1.0/search.{_format}'
+ controller: Chill\MainBundle\Controller\SearchController::searchApi
+ format: 'json'
+ requirements:
+ _format: 'json'
+
chill_main_advanced_search:
path: /{_locale}/search/advanced/{name}
controller: Chill\MainBundle\Controller\SearchController::advancedSearchAction
diff --git a/src/Bundle/ChillMainBundle/config/services.yaml b/src/Bundle/ChillMainBundle/config/services.yaml
index 93a4ee4e4..a6afa4336 100644
--- a/src/Bundle/ChillMainBundle/config/services.yaml
+++ b/src/Bundle/ChillMainBundle/config/services.yaml
@@ -3,6 +3,18 @@ parameters:
services:
+ Chill\MainBundle\Serializer\Normalizer\:
+ resource: '../Serializer/Normalizer'
+ autowire: true
+ tags:
+ - { name: 'serializer.normalizer', priority: 64 }
+
+ Chill\MainBundle\Doctrine\Event\:
+ resource: '../Doctrine/Event/'
+ autowire: true
+ tags:
+ - { name: 'doctrine.event_subscriber' }
+
chill.main.helper.translatable_string:
class: Chill\MainBundle\Templating\TranslatableStringHelper
arguments:
diff --git a/src/Bundle/ChillMainBundle/config/services/controller.yaml b/src/Bundle/ChillMainBundle/config/services/controller.yaml
index 5fb542786..6021e3d72 100644
--- a/src/Bundle/ChillMainBundle/config/services/controller.yaml
+++ b/src/Bundle/ChillMainBundle/config/services/controller.yaml
@@ -16,6 +16,7 @@ services:
$searchProvider: '@chill_main.search_provider'
$translator: '@Symfony\Contracts\Translation\TranslatorInterface'
$paginatorFactory: '@Chill\MainBundle\Pagination\PaginatorFactory'
+ $searchApi: '@Chill\MainBundle\Search\SearchApi'
tags: ['controller.service_arguments']
Chill\MainBundle\Controller\PermissionsGroupController:
diff --git a/src/Bundle/ChillMainBundle/config/services/search.yaml b/src/Bundle/ChillMainBundle/config/services/search.yaml
index e8a457415..b7a1656b3 100644
--- a/src/Bundle/ChillMainBundle/config/services/search.yaml
+++ b/src/Bundle/ChillMainBundle/config/services/search.yaml
@@ -1,3 +1,10 @@
services:
chill_main.search_provider:
- class: Chill\MainBundle\Search\SearchProvider
\ No newline at end of file
+ class: Chill\MainBundle\Search\SearchProvider
+
+ Chill\MainBundle\Search\SearchProvider: '@chill_main.search_provider'
+
+ Chill\MainBundle\Search\SearchApi:
+ arguments:
+ $em: '@Doctrine\ORM\EntityManagerInterface'
+ $search: '@Chill\MainBundle\Search\SearchProvider'
diff --git a/src/Bundle/ChillMainBundle/config/services/serializer.yaml b/src/Bundle/ChillMainBundle/config/services/serializer.yaml
index fb5f57b7e..c7cc6ca63 100644
--- a/src/Bundle/ChillMainBundle/config/services/serializer.yaml
+++ b/src/Bundle/ChillMainBundle/config/services/serializer.yaml
@@ -1,17 +1,11 @@
---
services:
- Chill\MainBundle\Serializer\Normalizer\CenterNormalizer:
- tags:
- - { name: 'serializer.normalizer', priority: 64 }
+
+ # note: the autowiring for serializers and normalizers is declared
+ # into ../services.yaml
- Chill\MainBundle\Serializer\Normalizer\DateNormalizer:
+ Chill\MainBundle\Serializer\Normalizer\DoctrineExistingEntityNormalizer:
+ autowire: true
tags:
- - { name: 'serializer.normalizer', priority: 64 }
+ - { name: 'serializer.normalizer', priority: 8 }
- Chill\MainBundle\Serializer\Normalizer\UserNormalizer:
- tags:
- - { name: 'serializer.normalizer', priority: 64 }
-
- Chill\MainBundle\Serializer\Normalizer\CollectionNormalizer:
- tags:
- - { name: 'serializer.normalizer', priority: 64 }
diff --git a/src/Bundle/ChillMainBundle/migrations/Version20210525144016.php b/src/Bundle/ChillMainBundle/migrations/Version20210525144016.php
new file mode 100644
index 000000000..3be8d9ea1
--- /dev/null
+++ b/src/Bundle/ChillMainBundle/migrations/Version20210525144016.php
@@ -0,0 +1,31 @@
+addSql('ALTER TABLE chill_main_address DROP CONSTRAINT FK_165051F6114B8DD9');
+ $this->addSql('ALTER TABLE chill_main_address ADD CONSTRAINT FK_165051F6114B8DD9 FOREIGN KEY (linkedToThirdParty_id) REFERENCES chill_3party.third_party (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE');
+ }
+
+ public function down(Schema $schema): void
+ {
+ $this->addSql('ALTER TABLE chill_main_address DROP CONSTRAINT fk_165051f6114b8dd9');
+ $this->addSql('ALTER TABLE chill_main_address ADD CONSTRAINT fk_165051f6114b8dd9 FOREIGN KEY (linkedtothirdparty_id) REFERENCES chill_3party.third_party (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php
index dc666400f..860cdfad7 100644
--- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php
+++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseApiController.php
@@ -5,13 +5,19 @@ namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\Routing\Annotation\Route;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
use Chill\PersonBundle\Entity\Person;
+use Chill\ThirdPartyBundle\Entity\ThirdParty;
+use Symfony\Component\Serializer\Exception\RuntimeException;
+use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
+use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
+use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
+use Chill\MainBundle\Entity\Scope;
+use Symfony\Component\Workflow\Registry;
class AccompanyingCourseApiController extends ApiController
{
@@ -19,10 +25,37 @@ class AccompanyingCourseApiController extends ApiController
protected ValidatorInterface $validator;
- public function __construct(EventDispatcherInterface $eventDispatcher, $validator)
- {
+ private Registry $registry;
+
+ public function __construct(
+ EventDispatcherInterface $eventDispatcher,
+ ValidatorInterface $validator,
+ Registry $registry
+ ) {
$this->eventDispatcher = $eventDispatcher;
$this->validator = $validator;
+ $this->registry = $registry;
+ }
+
+ public function confirmApi($id, Request $request, $_format): Response
+ {
+ /** @var AccompanyingPeriod $accompanyingPeriod */
+ $accompanyingPeriod = $this->getEntity('participation', $id, $request);
+
+ $this->checkACL('confirm', $request, $_format, $accompanyingPeriod);
+$workflow = $this->registry->get($accompanyingPeriod);
+
+ if (FALSE === $workflow->can($accompanyingPeriod, 'confirm')) {
+ throw new BadRequestException('It is not possible to confirm this period');
+ }
+
+ $workflow->apply($accompanyingPeriod, 'confirm');
+
+ $this->getDoctrine()->getManager()->flush();
+
+ return $this->json($accompanyingPeriod, Response::HTTP_OK, [], [
+ 'groups' => [ 'read' ]
+ ]);
}
public function participationApi($id, Request $request, $_format)
@@ -55,12 +88,76 @@ class AccompanyingCourseApiController extends ApiController
if ($errors->count() > 0) {
// only format accepted
- return $this->json($errors);
+ return $this->json($errors, 422);
}
$this->getDoctrine()->getManager()->flush();
- return $this->json($participation);
+ return $this->json($participation, 200, [], ['groups' => [ 'read' ]]);
+ }
+
+ public function resourceApi($id, Request $request, string $_format): Response
+ {
+ return $this->addRemoveSomething('resource', $id, $request, $_format, 'resource', Resource::class);
+ }
+
+ public function scopeApi($id, Request $request, string $_format): Response
+ {
+ return $this->addRemoveSomething('scope', $id, $request, $_format, 'scope', Scope::class, [ 'groups' => [ 'read' ] ]);
+ }
+
+ public function commentApi($id, Request $request, string $_format): Response
+ {
+ return $this->addRemoveSomething('comment', $id, $request, $_format, 'comment', Comment::class);
+ }
+
+ public function socialIssueApi($id, Request $request, string $_format): Response
+ {
+ return $this->addRemoveSomething('socialissue', $id, $request, $_format, 'socialIssue', SocialIssue::class, [ 'groups' => [ 'read' ] ]);
+ }
+
+ public function requestorApi($id, Request $request, string $_format): Response
+ {
+ /** @var AccompanyingPeriod $accompanyingPeriod */
+ $action = 'requestor';
+ $accompanyingPeriod = $this->getEntity($action, $id, $request);
+ // a requestor may be a person or a thirdParty
+
+ $this->checkACL($action, $request, $_format, $accompanyingPeriod);
+ $this->onPostCheckACL($action, $request, $_format, $accompanyingPeriod);
+
+ if (Request::METHOD_DELETE === $request->getMethod()) {
+ $accompanyingPeriod->setRequestor(NULL);
+ } elseif (Request::METHOD_POST === $request->getMethod()) {
+ $requestor = null;
+ $exceptions = [];
+ foreach ([Person::class, ThirdParty::class] as $class) {
+ try {
+ $requestor = $this->getSerializer()
+ ->deserialize($request->getContent(), $class, $_format, []);
+ } catch (RuntimeException $e) {
+ $exceptions[] = $e;
+ }
+ }
+ if ($requestor === null) {
+ throw new BadRequestException('Could not find any person or requestor', 0, $exceptions[0]);
+ }
+
+ $accompanyingPeriod->setRequestor($requestor);
+ } else {
+ throw new BadRequestException('method not supported');
+ }
+
+ $errors = $this->validator->validate($accompanyingPeriod);
+
+ if ($errors->count() > 0) {
+ // only format accepted
+ return $this->json($errors, 422);
+ }
+
+ $this->getDoctrine()->getManager()->flush();
+
+ return $this->json($accompanyingPeriod->getRequestor(), 200, [], ['groups' => [ 'read']]);
}
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php
index 9bc732d87..6786cb05f 100644
--- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php
+++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php
@@ -6,6 +6,7 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Chill\PersonBundle\Privacy\AccompanyingPeriodPrivacyEvent;
use Chill\PersonBundle\Entity\Person;
+use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -42,6 +43,41 @@ class AccompanyingCourseController extends Controller
$this->dispatcher = $dispatcher;
$this->validator = $validator;
}
+
+ /**
+ * @Route("/{_locale}/person/parcours/new", name="chill_person_accompanying_course_new")
+ */
+ public function newAction(Request $request): Response
+ {
+ $period = new AccompanyingPeriod();
+ $em = $this->getDoctrine()->getManager();
+
+ if ($request->query->has('person_id')) {
+ $personIds = $request->query->get('person_id');
+
+ if (FALSE === \is_array($personIds)) {
+ throw new BadRequestException("person_id parameter should be an array");
+ }
+
+ foreach ($personIds as $personId) {
+ $person = $em->getRepository(Person::class)->find($personId);
+ if (NULL !== $person) {
+ $period->addPerson($person);
+ }
+ }
+ }
+
+ $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $period);
+
+ $em->persist($period);
+ $em->flush();
+
+ return $this->redirectToRoute('chill_person_accompanying_course_show', [
+ 'accompanying_period_id' => $period->getId()
+ ]);
+
+ }
+
/**
* Homepage of Accompanying Course section
*
@@ -86,76 +122,4 @@ class AccompanyingCourseController extends Controller
]);
}
- /**
- * Get API Data for showing endpoint
- *
- * @Route(
- * "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/show.{_format}",
- * name="chill_person_accompanying_course_api_show"
- * )
- * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
- */
- public function showAPI(AccompanyingPeriod $accompanyingCourse, $_format): Response
- {
- // TODO check ACL on AccompanyingPeriod
-
- $this->dispatcher->dispatch(
- AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT,
- new AccompanyingPeriodPrivacyEvent($accompanyingCourse, [
- 'action' => 'showApi'
- ])
- );
-
- switch ($_format) {
- case 'json':
- return $this->json($accompanyingCourse);
- default:
- throw new BadRequestException('Unsupported format');
- }
-
- }
-
- /**
- * Get API Data for showing endpoint
- *
- * @Route(
- * "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/participation.{_format}",
- * name="chill_person_accompanying_course_api_add_participation",
- * methods={"POST"},
- * format="json",
- * requirements={
- * "_format": "json",
- * }
- * )
- * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
- */
- public function addParticipationAPI(Request $request, AccompanyingPeriod $accompanyingCourse, $_format): Response
- {
- switch ($_format) {
- case 'json':
- $person = $this->serializer->deserialize($request->getContent(), Person::class, $_format, [
-
- ]);
- break;
- default:
- throw new BadRequestException('Unsupported format');
- }
-
- if (NULL === $person) {
- throw new BadRequestException('person id not found');
- }
-
- // TODO add acl
- $accompanyingCourse->addPerson($person);
- $errors = $this->validator->validate($accompanyingCourse);
-
- if ($errors->count() > 0) {
- // only format accepted
- return $this->json($errors);
- }
-
- $this->getDoctrine()->getManager()->flush();
-
- return new JsonResponse();
- }
}
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/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php
index bfb32654e..02320aed3 100644
--- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php
+++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php
@@ -38,6 +38,7 @@ use Symfony\Component\Translation\TranslatorInterface;
use Chill\MainBundle\Search\SearchProvider;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
+use Chill\PersonBundle\Repository\PersonNotDuplicateRepository;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Doctrine\ORM\EntityManagerInterface;
@@ -290,7 +291,7 @@ final class PersonController extends AbstractController
return $errors;
}
- public function reviewAction(Request $request)
+ public function reviewAction(Request $request, PersonNotDuplicateRepository $personNotDuplicateRepository)
{
if ($request->getMethod() !== 'POST') {
$r = new Response("You must send something to review the creation of a new Person");
@@ -299,7 +300,6 @@ final class PersonController extends AbstractController
}
$form = $this->createForm(
- //CreationPersonType::NAME,
CreationPersonType::class,
new Person(),
array(
@@ -326,7 +326,7 @@ final class PersonController extends AbstractController
}
$form = $this->createForm(
- CreationPersonType::NAME,
+ CreationPersonType::class,
$person,
array(
'action' => $this->generateUrl('chill_person_review'),
@@ -342,8 +342,7 @@ final class PersonController extends AbstractController
$this->em->persist($person);
- $alternatePersons = $this->similarPersonMatcher
- ->matchPerson($person);
+ $alternatePersons = $this->similarPersonMatcher->matchPerson($person, $personNotDuplicateRepository);
if (count($alternatePersons) === 0) {
return $this->forward('ChillPersonBundle:Person:create');
diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php b/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php
index aa2e2768a..d30ddebae 100644
--- a/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php
+++ b/src/Bundle/ChillPersonBundle/Controller/PersonDuplicateController.php
@@ -19,6 +19,7 @@ use Symfony\Component\Translation\TranslatorInterface;
use Chill\ActivityBundle\Entity\Activity;
use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\EventBundle\Entity\Participation;
+use Chill\PersonBundle\Repository\PersonNotDuplicateRepository;
use Chill\TaskBundle\Entity\SingleTask;
class PersonDuplicateController extends Controller
@@ -62,7 +63,7 @@ class PersonDuplicateController extends Controller
$this->eventDispatcher = $eventDispatcher;
}
- public function viewAction($person_id)
+ public function viewAction($person_id, PersonNotDuplicateRepository $personNotDuplicateRepository)
{
$person = $this->_getPerson($person_id);
if ($person === null) {
@@ -74,10 +75,9 @@ class PersonDuplicateController extends Controller
"You are not allowed to see this person.");
$duplicatePersons = $this->similarPersonMatcher->
- matchPerson($person, 0.5, SimilarPersonMatcher::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL);
+ matchPerson($person, $personNotDuplicateRepository, 0.5, SimilarPersonMatcher::SIMILAR_SEARCH_ORDER_BY_ALPHABETICAL);
- $notDuplicatePersons = $this->getDoctrine()->getRepository(PersonNotDuplicate::class)
- ->findNotDuplicatePerson($person);
+ $notDuplicatePersons = $personNotDuplicateRepository->findNotDuplicatePerson($person);
return $this->render('ChillPersonBundle:PersonDuplicate:view.html.twig', [
'person' => $person,
@@ -97,7 +97,7 @@ class PersonDuplicateController extends Controller
$person1->counters = $this->_getCounters($person1_id);
$person2->counters = $this->_getCounters($person2_id);
-
+
if ($person1 === null) {
throw $this->createNotFoundException("Person with id $person1_id not"
. " found on this server");
@@ -264,17 +264,17 @@ class PersonDuplicateController extends Controller
return [$person1, $person2];
}
-
+
private function _getCounters($id): ?array
{
$em = $this->getDoctrine()->getManager();
-
+
$nb_activity = $em->getRepository(Activity::class)->findBy(['person'=>$id]);
$nb_document = $em->getRepository(PersonDocument::class)->findBy(['person'=>$id]);
$nb_event = $em->getRepository(Participation::class)->findBy(['person'=>$id]);
$nb_task = $em->getRepository(SingleTask::class)->countByParameters(['person'=>$id]);
$person = $em->getRepository(Person::class)->findOneBy(['id'=>$id]);
-
+
return [
'nb_activity' => count($nb_activity),
'nb_document' => count($nb_document),
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriod.php
new file mode 100644
index 000000000..a30943bcb
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriod.php
@@ -0,0 +1,88 @@
+,
+ *
+ * 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\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Persistence\ObjectManager;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+
+use Chill\PersonBundle\Entity\AccompanyingPeriod;
+use Chill\PersonBundle\Entity\Person;
+
+/**
+ * Description of LoadAccompanyingPeriod
+ *
+ * @author Champs-Libres Coop
+ */
+class LoadAccompanyingPeriod extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
+{
+ use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
+
+
+ public const ACCOMPANYING_PERIOD = 'parcours 1';
+
+ public function getOrder()
+ {
+ return 10004;
+ }
+
+ public static $references = array();
+
+ public function load(ObjectManager $manager)
+ {
+
+ $centerA = $this->getReference('centerA');
+ $centerAId = $centerA->getId();
+
+ $personIds = $this->container->get('doctrine.orm.entity_manager')
+ ->createQueryBuilder()
+ ->select('p.id')
+ ->from('ChillPersonBundle:Person', 'p')
+ ->where('p.center = :centerAId')
+ ->orderBy('p.id', 'ASC')
+ ->setParameter('centerAId', $centerAId)
+ ->getQuery()
+ ->getScalarResult();
+
+ $openingDate = new \DateTime('2020-04-01');
+
+ $person1 = $manager->getRepository(Person::class)->find($personIds[0]);
+ $person2 = $manager->getRepository(Person::class)->find($personIds[1]);
+
+ $socialScope = $this->getReference('scope_social');
+
+ $a = new AccompanyingPeriod($openingDate);
+ $a->addPerson($person1);
+ $a->addPerson($person2);
+ $a->addScope($socialScope);
+ $a->setStep(AccompanyingPeriod::STEP_CONFIRMED);
+
+ $manager->persist($a);
+
+ $this->addReference(self::ACCOMPANYING_PERIOD, $a);
+ echo "Adding one AccompanyingPeriod\n";
+
+ $manager->flush();
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodOrigin.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodOrigin.php
new file mode 100644
index 000000000..cbec9c439
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadAccompanyingPeriodOrigin.php
@@ -0,0 +1,62 @@
+,
+ *
+ * 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\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Persistence\ObjectManager;
+
+use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
+
+/**
+ * Description of LoadAccompanyingPeriodOrigin
+ *
+ * @author Champs-Libres Coop
+ */
+class LoadAccompanyingPeriodOrigin extends AbstractFixture implements OrderedFixtureInterface
+{
+
+ public const ACCOMPANYING_PERIOD_ORIGIN = 'accompanying_period_origin';
+
+ public function getOrder()
+ {
+ return 10005;
+ }
+
+ private $phoneCall = ['en' => 'phone call', 'fr' => 'appel téléphonique'];
+
+ public static $references = array();
+
+ public function load(ObjectManager $manager)
+ {
+ $o = new Origin();
+ $o->setLabel(json_encode($this->phoneCall));
+
+ $manager->persist($o);
+
+ $this->addReference(self::ACCOMPANYING_PERIOD_ORIGIN, $o);
+ echo "Adding one AccompanyingPeriod Origin\n";
+
+ $manager->flush();
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialActions.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialActions.php
new file mode 100644
index 000000000..ec47c6e0b
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialActions.php
@@ -0,0 +1,82 @@
+
+ *
+ * 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\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Persistence\ObjectManager;
+
+use Chill\PersonBundle\Entity\SocialWork\SocialAction;
+
+/**
+ * Create social actions
+ *
+ */
+class LoadSocialActions extends AbstractFixture implements OrderedFixtureInterface
+{
+ public function getOrder()
+ {
+ return 10020;
+ }
+
+ public static $socialActions = [
+ 'social_action_info_conseil' => [
+ 'title' => [
+ 'fr' => 'Informer, conseiller'
+ ],
+ 'issue' => 'social_issue_prev_prot'
+ ],
+ 'social_action_instruire' => [
+ 'title' => [
+ 'fr' => 'Instruire l\'imprime unique pour des impayés'
+ ],
+ 'issue' => 'social_issue_prev_prot'
+ ],
+ 'social_action_MASP' => [
+ 'title' => [
+ 'fr' => 'MASP'
+ ],
+ 'issue' => 'social_issue_diff_fin'
+ ],
+ 'social_action_protection_enfant' => [
+ 'title' => [
+ 'fr' => 'Protection Enfant confié dans le cadre judiciaire'
+ ],
+ 'issue' => 'social_issue_enfant_protection'
+ ],
+ ];
+
+ public function load(ObjectManager $manager)
+ {
+ foreach (static::$socialActions as $ref => $new) {
+ $socialAction = new SocialAction();
+ $socialAction->setTitle($new['title']);
+ $socialAction->setIssue($this->getReference($new['issue']));
+ $socialAction->setDefaultNotificationDelay(new \DateInterval('P5D'));
+
+ $manager->persist($socialAction);
+ $this->addReference($ref, $socialAction);
+ print("Adding SocialAction '".$new['title']['fr']."'\n");
+ }
+
+ $manager->flush();
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialGoals.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialGoals.php
new file mode 100644
index 000000000..a6c2b0ef0
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialGoals.php
@@ -0,0 +1,70 @@
+
+ *
+ * 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\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Persistence\ObjectManager;
+
+use Chill\PersonBundle\Entity\SocialWork\Goal;
+
+
+/**
+ * Create social goals
+ *
+ */
+class LoadSocialGoals extends AbstractFixture implements OrderedFixtureInterface
+{
+ public function getOrder()
+ {
+ return 10030;
+ }
+
+ public static $socialGoals = [
+ 'social_goal_instuire_dossier' => [
+ 'title' => [
+ 'fr' => 'Instruire le dossier de surendettement'
+ ],
+ 'action' => 'social_action_MASP'
+ ],
+ 'social_goal_proteger' => [
+ 'title' => [
+ 'fr' => 'Protéger via une assistance educative placement'
+ ],
+ 'action' => 'social_action_protection_enfant'
+ ],
+ ];
+
+ public function load(ObjectManager $manager)
+ {
+ foreach (static::$socialGoals as $ref => $new) {
+ $socialGoal = new Goal();
+ $socialGoal->setTitle($new['title']);
+ $socialGoal->addSocialAction($this->getReference($new['action']));
+
+ $manager->persist($socialGoal);
+ $this->addReference($ref, $socialGoal);
+ print("Adding SocialGoal '".$new['title']['fr']."'\n");
+ }
+
+ $manager->flush();
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialIssues.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialIssues.php
new file mode 100644
index 000000000..eb9c303ed
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialIssues.php
@@ -0,0 +1,90 @@
+
+ *
+ * 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\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Persistence\ObjectManager;
+
+use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
+
+/**
+ * Create social issues
+ *
+ */
+class LoadSocialIssues extends AbstractFixture implements OrderedFixtureInterface
+{
+ public function getOrder()
+ {
+ return 10010;
+ }
+
+ public static $socialIssues = [
+ 'social_issue_diff_fin_or_admin' => [
+ 'title' => [
+ 'fr' => 'ADULTE - DIFFICULTES FINANCIERES ET/OU ADMINISTRATIVES'
+ ]
+ ],
+ 'social_issue_prev_prot' => [
+ 'title' => [
+ 'fr' => 'ADULTE PREVENTION/PROTECTION'
+ ],
+ 'parent' => 'social_issue_diff_fin_or_admin'
+ ],
+ 'social_issue_diff_fin' => [
+ 'title' => [
+ 'fr' => 'Difficulté financière'
+ ],
+ 'parent' => 'social_issue_diff_fin_or_admin'
+ ],
+ 'social_issue_enfant_famille' => [
+ 'title' => [
+ 'fr' => 'Enfant / famille'
+ ]
+ ],
+ 'social_issue_enfant_protection' => [
+ 'title' => [
+ 'fr' => 'enfant - protection'
+ ],
+ 'parent' => 'social_issue_enfant_famille'
+ ],
+ ];
+
+ public function load(ObjectManager $manager)
+ {
+ foreach (static::$socialIssues as $ref => $new) {
+ $socialIssue = new SocialIssue();
+ $socialIssue->setTitle($new['title']);
+
+ if ( array_key_exists('parent', $new)) {
+ $parentRef = $new['parent'];
+ $parent = $this->getReference($parentRef);
+ $socialIssue->setParent($parent);
+ }
+
+ $manager->persist($socialIssue);
+ $this->addReference($ref, $socialIssue);
+ print("Adding SocialIssue '".$new['title']['fr']."'\n");
+ }
+
+ $manager->flush();
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialResults.php b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialResults.php
new file mode 100644
index 000000000..573313573
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/DataFixtures/ORM/LoadSocialResults.php
@@ -0,0 +1,94 @@
+
+ *
+ * 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\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Persistence\ObjectManager;
+
+use Chill\PersonBundle\Entity\SocialWork\Result;
+
+
+/**
+ * Create social results
+ *
+ */
+class LoadSocialResults extends AbstractFixture implements OrderedFixtureInterface
+{
+ public function getOrder()
+ {
+ return 10040;
+ }
+
+ public static $socialResults = [
+ 'social_result_FSL_acces' => [
+ 'title' => [
+ 'fr' => 'FSL - accès cautionnement'
+ ],
+ 'action' => 'social_action_instruire'
+ ],
+ 'social_result_FSL_maintien' => [
+ 'title' => [
+ 'fr' => 'FSL maintien - impayés de loyer'
+ ],
+ 'action' => 'social_action_MASP'
+ ],
+ 'social_result_soutien_parental' => [
+ 'title' => [
+ 'fr' => 'Soutien parental'
+ ],
+ // 'action' => 'social_action_protection_enfant', (via le goal)
+ 'goal' => 'social_goal_proteger'
+ ],
+ '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)
+ {
+ foreach (static::$socialResults as $ref => $new) {
+ $socialResult = new Result();
+ $socialResult->setTitle($new['title']);
+
+ if ( array_key_exists('action', $new)) {
+ $action = $this->getReference($new['action']);
+ $socialResult->addSocialAction($action);
+ }
+
+ if ( array_key_exists('goal', $new)) {
+ $goal = $this->getReference($new['goal']);
+ $socialResult->addGoal($goal);
+ }
+
+
+ $manager->persist($socialResult);
+ $this->addReference($ref, $socialResult);
+ print("Adding SocialResult '".$new['title']['fr']."'\n");
+ }
+
+ $manager->flush();
+ }
+}
diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
index 84b8bb397..27721012d 100644
--- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
+++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php
@@ -1,5 +1,4 @@
*
@@ -73,9 +72,11 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/command.yaml');
$loader->load('services/actions.yaml');
$loader->load('services/form.yaml');
- $loader->load('services/repository.yaml');
$loader->load('services/templating.yaml');
$loader->load('services/alt_names.yaml');
+ // We can get rid of this file when the service 'chill.person.repository.person' is no more used.
+ // We should use the PersonRepository service instead of a custom service name.
+ $loader->load('services/repository.yaml');
$loader->load('services/serializer.yaml');
$loader->load('services/security.yaml');
@@ -164,6 +165,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$this->prependHomepageWidget($container);
$this->prependDoctrineDQL($container);
$this->prependCruds($container);
+ $this->prependWorkflows($container);
//add person_fields parameter as global
$chillPersonConfig = $container->getExtensionConfig($this->getAlias());
@@ -193,6 +195,39 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
));
}
+ protected function prependWorkflows(ContainerBuilder $container)
+ {
+ $container->prependExtensionConfig('framework', [
+ 'workflows' => [
+ 'accompanying_period_lifecycle' => [
+ 'type' => 'state_machine',
+ 'audit_trail' => [
+ 'enabled' => true
+ ],
+ 'marking_store' => [
+ 'type' => 'method',
+ 'property' => 'step',
+ ],
+ 'supports' => [
+ 'Chill\PersonBundle\Entity\AccompanyingPeriod'
+ ],
+ 'initial_marking' => 'DRAFT',
+ 'places' => [
+ 'DRAFT',
+ 'CONFIRMED',
+ ],
+ 'transitions' => [
+ 'confirm' => [
+ 'from' => 'DRAFT',
+ 'to' => 'CONFIRMED'
+ ],
+ ],
+ ],
+ ]
+ ]);
+
+ }
+
/**
* Add a widget "add a person" on the homepage, automatically
*
@@ -320,10 +355,29 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
'actions' => [
'_entity' => [
'roles' => [
- Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
+ Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ Request::METHOD_PATCH => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ Request::METHOD_PUT => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ ],
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_PUT => true,
+ Request::METHOD_PATCH => true,
]
],
'participation' => [
+ 'methods' => [
+ Request::METHOD_POST => true,
+ Request::METHOD_DELETE => true,
+ Request::METHOD_GET => false,
+ Request::METHOD_HEAD => false,
+ ],
+ 'roles' => [
+ Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
+ ]
+ ],
+ 'resource' => [
'methods' => [
Request::METHOD_POST => true,
Request::METHOD_DELETE => true,
@@ -334,8 +388,67 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
]
- ]
-
+ ],
+ 'comment' => [
+ 'methods' => [
+ Request::METHOD_POST => true,
+ Request::METHOD_DELETE => true,
+ Request::METHOD_GET => false,
+ Request::METHOD_HEAD => false,
+ ],
+ 'roles' => [
+ Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
+ ]
+ ],
+ 'requestor' => [
+ 'methods' => [
+ Request::METHOD_POST => true,
+ Request::METHOD_DELETE => true,
+ Request::METHOD_GET => false,
+ Request::METHOD_HEAD => false,
+ ],
+ 'roles' => [
+ Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
+ ]
+ ],
+ 'scope' => [
+ 'methods' => [
+ Request::METHOD_POST => true,
+ Request::METHOD_DELETE => true,
+ Request::METHOD_GET => false,
+ Request::METHOD_HEAD => false,
+ ],
+ 'roles' => [
+ Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
+ ]
+ ],
+ 'socialissue' => [
+ 'methods' => [
+ Request::METHOD_POST => true,
+ Request::METHOD_DELETE => true,
+ Request::METHOD_GET => false,
+ Request::METHOD_HEAD => false,
+ ],
+ 'controller_action' => 'socialIssueApi',
+ 'roles' => [
+ Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ Request::METHOD_DELETE=> \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
+ ]
+ ],
+
+ 'confirm' => [
+ 'methods' => [
+ Request::METHOD_POST => true,
+ Request::METHOD_GET => false,
+ Request::METHOD_HEAD => false,
+ ],
+ 'roles' => [
+ Request::METHOD_POST => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE,
+ ]
+ ],
]
],
[
@@ -346,19 +459,61 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
'base_role' => 'ROLE_USER',
'actions' => [
'_index' => [
- 'methods' => [
+ 'methods' => [
Request::METHOD_GET => true,
Request::METHOD_HEAD => true
],
],
'_entity' => [
- 'methods' => [
+ 'methods' => [
Request::METHOD_GET => true,
Request::METHOD_HEAD => true
]
],
]
- ]
+ ],
+ [
+ 'class' => \Chill\PersonBundle\Entity\SocialWork\SocialIssue::class,
+ 'name' => 'social_work_social_issue',
+ 'base_path' => '/api/1.0/person/social-work/social-issue',
+ 'base_role' => 'ROLE_USER',
+ 'actions' => [
+ '_index' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ],
+ ],
+ '_entity' => [
+ 'methods' => [
+ Request::METHOD_GET => true,
+ Request::METHOD_HEAD => true
+ ]
+ ],
+ ]
+ ],
+ [
+ '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/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
index 68f5d76e6..9841a99f3 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php
@@ -22,25 +22,34 @@
namespace Chill\PersonBundle\Entity;
+use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
+use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
use Chill\MainBundle\Entity\Scope;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
+use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
+use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Entity\User;
+use Symfony\Component\Serializer\Annotation\Groups;
+use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/**
* AccompanyingPeriod Class
*
- * @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\AccompanyingPeriodRepository")
+ * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period")
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "accompanying_period"=AccompanyingPeriod::class
+ * })
*/
-class AccompanyingPeriod
+class AccompanyingPeriod implements TrackCreationInterface, TrackUpdateInterface
{
/**
* Mark an accompanying period as "occasional"
@@ -80,6 +89,7 @@ class AccompanyingPeriod
* @ORM\Id
* @ORM\Column(name="id", type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
+ * @Groups({"read"})
*/
private $id;
@@ -87,6 +97,7 @@ class AccompanyingPeriod
* @var \DateTime
*
* @ORM\Column(type="date")
+ * @Groups({"read", "write"})
*/
private $openingDate;
@@ -94,6 +105,7 @@ class AccompanyingPeriod
* @var \DateTime
*
* @ORM\Column(type="date", nullable=true)
+ * @Groups({"read", "write"})
*/
private $closingDate = null;
@@ -101,6 +113,7 @@ class AccompanyingPeriod
* @var string
*
* @ORM\Column(type="text")
+ * @Groups({"read", "write"})
*/
private $remark = '';
@@ -108,17 +121,28 @@ class AccompanyingPeriod
* @var Collection
*
* @ORM\OneToMany(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Comment",
- * mappedBy="accompanyingPeriod"
+ * mappedBy="accompanyingPeriod",
+ * cascade={"persist", "remove"},
+ * orphanRemoval=true
* )
*/
private $comments;
+ /**
+ * @ORM\ManyToOne(
+ * targetEntity=Comment::class
+ * )
+ * @Groups({"read"})
+ */
+ private ?Comment $initialComment = null;
+
/**
* @var Collection
*
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
* mappedBy="accompanyingPeriod",
* cascade={"persist", "refresh", "remove", "merge", "detach"})
+ * @Groups({"read"})
*/
private $participations;
@@ -128,36 +152,42 @@ class AccompanyingPeriod
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive")
* @ORM\JoinColumn(nullable=true)
+ * @Groups({"read", "write"})
*/
private $closingMotive = null;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=true)
+ * @Groups({"read", "write"})
*/
private $user;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=true)
+ * @Groups({"read"})
*/
private $createdBy;
/**
* @var string
* @ORM\Column(type="string", length=32, nullable=true)
+ * @Groups({"read"})
*/
private $step = self::STEP_DRAFT;
/**
* @ORM\ManyToOne(targetEntity=Origin::class)
* @ORM\JoinColumn(nullable=true)
+ * @Groups({"read", "write"})
*/
private $origin;
/**
* @var string
* @ORM\Column(type="string", nullable=true)
+ * @Groups({"read", "write"})
*/
private $intensity;
@@ -172,6 +202,7 @@ class AccompanyingPeriod
* joinColumns={@ORM\JoinColumn(name="accompanying_period_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="scope_id", referencedColumnName="id")}
* )
+ * @Groups({"read"})
*/
private $scopes;
@@ -189,19 +220,22 @@ class AccompanyingPeriod
/**
* @var bool
- * @ORM\Column(type="boolean")
+ * @ORM\Column(type="boolean", options={"default": false} )
+ * @Groups({"read", "write"})
*/
private $requestorAnonymous = false;
/**
* @var bool
- * @ORM\Column(type="boolean")
+ * @ORM\Column(type="boolean", options={"default": false} )
+ * @Groups({"read", "write"})
*/
private $emergency = false;
/**
* @var bool
- * @ORM\Column(type="boolean")
+ * @ORM\Column(type="boolean", options={"default": false} )
+ * @Groups({"read", "write"})
*/
private $confidential = false;
@@ -210,21 +244,54 @@ class AccompanyingPeriod
*
* @ORM\OneToMany(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod\Resource",
- * mappedBy="accompanyingPeriod"
+ * mappedBy="accompanyingPeriod",
+ * cascade={"persist", "remove"},
+ * orphanRemoval=true
* )
+ * @Groups({"read"})
*/
private $resources;
+ /**
+ * @ORM\ManyToMany(
+ * targetEntity=SocialIssue::class
+ * )
+ * @ORM\JoinTable(
+ * name="chill_person_accompanying_period_social_issues"
+ * )
+ * @Groups({"read"})
+ */
+ private Collection $socialIssues;
+
+ /**
+ * @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
+ */
+ private \DateTimeInterface $createdAt;
+
+ /**
+ * @ORM\ManyToOne(
+ * targetEntity=User::class
+ * )
+ */
+ private User $updatedBy;
+
+ /**
+ * @ORM\Column(type="datetime", nullable=true, options={"default": NULL})
+ */
+ private \DateTimeInterface $updatedAt;
+
/**
* AccompanyingPeriod constructor.
*
* @param \DateTime $dateOpening
* @uses AccompanyingPeriod::setClosingDate()
*/
- public function __construct(\DateTime $dateOpening) {
- $this->setOpeningDate($dateOpening);
+ public function __construct(\DateTime $dateOpening = null) {
+ $this->setOpeningDate($dateOpening ?? new \DateTime('now'));
$this->participations = new ArrayCollection();
$this->scopes = new ArrayCollection();
+ $this->socialIssues = new ArrayCollection();
+ $this->comments = new ArrayCollection();
}
/**
@@ -318,23 +385,55 @@ class AccompanyingPeriod
return $this->remark;
}
+ /**
+ * @Groups({"read"})
+ */
public function getComments(): Collection
{
- return $this->comments;
+ return $this->comments->filter(function (Comment $c) {
+ return $c !== $this->initialComment;
+ });
}
public function addComment(Comment $comment): self
{
$this->comments[] = $comment;
+ $comment->setAccompanyingPeriod($this);
return $this;
}
public function removeComment(Comment $comment): void
{
+ $comment->setAccompanyingPeriod(null);
$this->comments->removeElement($comment);
}
+ /**
+ * @Groups({"write"})
+ */
+ public function setInitialComment(?Comment $comment = null): self
+ {
+ if (NULL !== $this->initialComment) {
+ $this->removeComment($this->initialComment);
+ }
+ if ($comment instanceof Comment) {
+ $this->addComment($comment);
+ }
+
+ $this->initialComment = $comment;
+
+ return $this;
+ }
+
+ /**
+ * @Groups({"read"})
+ */
+ public function getInitialComment(): ?Comment
+ {
+ return $this->initialComment;
+ }
+
/**
* Get Participations Collection
*/
@@ -458,7 +557,7 @@ class AccompanyingPeriod
return false;
}
- $participation = $this->participationsContainsPerson($person);
+ $participation = $this->getParticipationsContainsPerson($person);
if (!null === $participation)
{
$person = $participation->getPerson();
@@ -540,9 +639,9 @@ class AccompanyingPeriod
return $this->requestorPerson;
}
- public function setRequestorPerson(Person $requestorPerson): self
+ private function setRequestorPerson(Person $requestorPerson = null): self
{
- $this->requestorPerson = ($this->requestorThirdParty === null) ? $requestorPerson : null;
+ $this->requestorPerson = $requestorPerson;
return $this;
}
@@ -552,21 +651,53 @@ class AccompanyingPeriod
return $this->requestorThirdParty;
}
- public function setRequestorThirdParty(ThirdParty $requestorThirdParty): self
+ private function setRequestorThirdParty(ThirdParty $requestorThirdParty = null): self
{
- $this->requestorThirdParty = ($this->requestorPerson === null) ? $requestorThirdParty : null;
+ $this->requestorThirdParty = $requestorThirdParty;
return $this;
}
/**
* @return Person|ThirdParty
+ * @Groups({"read"})
*/
public function getRequestor()
{
return $this->requestorPerson ?? $this->requestorThirdParty;
}
+
+ /**
+ * Set a requestor
+ *
+ * The requestor is either an instance of ThirdParty, or an
+ * instance of Person
+ *
+ * @param $requestor Person|ThirdParty
+ * @return self
+ * @throw UnexpectedValueException if the requestor is not a Person or ThirdParty
+ * @Groups({"write"})
+ */
+ public function setRequestor($requestor): self
+ {
+ if ($requestor instanceof Person) {
+ $this->setRequestorThirdParty(NULL);
+ $this->setRequestorPerson($requestor);
+ } elseif ($requestor instanceof ThirdParty) {
+ $this->setRequestorThirdParty($requestor);
+ $this->setRequestorPerson(NULL);
+ } elseif (NULL === $requestor) {
+ $this->setRequestorPerson(NULL);
+ $this->setRequestorThirdParty(NULL);
+ } else {
+ throw new \UnexpectedValueException("requestor is not an instance of Person or ThirdParty");
+ }
+
+ return $this;
+ }
+
+
public function isRequestorAnonymous(): bool
{
return $this->requestorAnonymous;
@@ -663,6 +794,7 @@ class AccompanyingPeriod
public function addResource(Resource $resource): self
{
+ $resource->setAccompanyingPeriod($this);
$this->resources[] = $resource;
return $this;
@@ -670,9 +802,27 @@ class AccompanyingPeriod
public function removeResource(Resource $resource): void
{
+ $resource->setAccompanyingPeriod(null);
$this->resources->removeElement($resource);
}
+ public function getSocialIssues(): Collection
+ {
+ return $this->socialIssues;
+ }
+
+ public function addSocialIssue(SocialIssue $socialIssue): self
+ {
+ $this->socialIssues[] = $socialIssue;
+
+ return $this;
+ }
+
+ public function removeSocialIssue(SocialIssue $socialIssue): void
+ {
+ $this->socialIssues->removeElement($socialIssue);
+ }
+
/**
* Get a list of all persons which are participating to this course
*/
@@ -684,4 +834,25 @@ class AccompanyingPeriod
}
);
}
+
+ public function setCreatedAt(\DateTimeInterface $datetime): self
+ {
+ $this->createdAt = $datetime;
+
+ return $this;
+ }
+
+ public function setUpdatedBy(User $user): self
+ {
+ $this->updatedBy = $user;
+
+ return $this;
+ }
+
+ public function setUpdatedAt(\DateTimeInterface $datetime): self
+ {
+ $this->updatedAt = $datetime;
+
+ return $this;
+ }
}
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php
index 6983f6c0e..25fbcc342 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php
@@ -4,7 +4,6 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
-use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
@@ -13,7 +12,7 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
- * @ORM\Entity(repositoryClass=AccompanyingPeriodWorkRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_work")
*/
class AccompanyingPeriodWork
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php
index ec371d6c2..955f9e3a1 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php
@@ -4,13 +4,12 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result;
-use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkGoalRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
- * @ORM\Entity(repositoryClass=AccompanyingPeriodWorkGoalRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_work_goal")
*/
class AccompanyingPeriodWorkGoal
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php
index 4e334b2aa..29b2a44aa 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php
@@ -29,8 +29,7 @@ use Doctrine\Common\Collections\ArrayCollection;
/**
* ClosingMotive give an explanation why we closed the Accompanying period
*
- * @ORM\Entity(
- * repositoryClass="Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository")
+ * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_closingmotive")
*/
class ClosingMotive
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php
index e8ecf3248..18b5bd38d 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Comment.php
@@ -22,21 +22,28 @@
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
-use Chill\PersonBundle\Repository\AccompanyingPeriod\CommentRepository;
use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
+use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
+use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
+use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
/**
- * @ORM\Entity(repositoryClass=CommentRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_comment")
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "accompanying_period_comment"=Comment::class
+ * })
*/
-class Comment
+class Comment implements TrackCreationInterface, TrackUpdateInterface
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
+ * @Groups({"read"})
*/
private $id;
@@ -44,34 +51,39 @@ class Comment
* @ORM\ManyToOne(
* targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod",
* inversedBy="comments")
- * @ORM\JoinColumn(nullable=false)
+ * @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $accompanyingPeriod;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=false)
+ * @Groups({"read"})
*/
private $creator;
/**
* @ORM\Column(type="datetime")
+ * @Groups({"read"})
*/
private $createdAt;
/**
* @ORM\Column(type="datetime")
+ * @Groups({"read"})
*/
private $updatedAt;
/**
* @ORM\ManyToOne(targetEntity=User::class)
* @ORM\JoinColumn(nullable=false)
+ * @Groups({"read"})
*/
private $updatedBy;
/**
* @ORM\Column(type="text")
+ * @Groups({"read", "write"})
*/
private $content;
@@ -104,6 +116,11 @@ class Comment
return $this;
}
+ public function setCreatedBy(User $user): self
+ {
+ return $this->setCreator($user);
+ }
+
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
@@ -133,7 +150,7 @@ class Comment
return $this->updatedBy;
}
- public function setUpdatedBy(?User $updatedBy): self
+ public function setUpdatedBy(User $updatedBy): self
{
$this->updatedBy = $updatedBy;
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php
index 55857de4c..3175e3156 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php
@@ -22,11 +22,11 @@
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
-use Chill\PersonBundle\Repository\AccompanyingPeriod\OriginRepository;
use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
/**
- * @ORM\Entity(repositoryClass=OriginRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_origin")
*/
class Origin
@@ -35,16 +35,19 @@ class Origin
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
+ * @Groups({"read"})
*/
private $id;
/**
* @ORM\Column(type="json")
+ * @Groups({"read"})
*/
private $label;
/**
* @ORM\Column(type="date_immutable", nullable=true)
+ * @Groups({"read"})
*/
private $noActiveAfter;
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php
index ec13fcad6..cf796722a 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Resource.php
@@ -22,16 +22,21 @@
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
-use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
use Chill\PersonBundle\Entity\Person;
+use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
+use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity(repositoryClass=ResourceRepository::class)
* @ORM\Table(name="chill_person_accompanying_period_resource")
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "accompanying_period_resource"=Resource::class
+ * })
*/
class Resource
{
@@ -39,6 +44,7 @@ class Resource
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
+ * @Groups({"read"})
*/
private $id;
@@ -91,7 +97,7 @@ class Resource
return $this->thirdParty;
}
- public function setThirdParty(?ThirdParty $thirdParty): self
+ private function setThirdParty(?ThirdParty $thirdParty): self
{
$this->thirdParty = $thirdParty;
@@ -103,7 +109,7 @@ class Resource
return $this->person;
}
- public function setPerson(?Person $person): self
+ private function setPerson(?Person $person): self
{
$this->person = $person;
@@ -121,9 +127,35 @@ class Resource
return $this;
}
+
+ /**
+ *
+ * @param $resource Person|ThirdParty
+ */
+ public function setResource($resource): self
+ {
+ if ($resource instanceof ThirdParty) {
+ $this->setThirdParty($resource);
+ $this->setPerson(NULL);
+ } elseif ($resource instanceof Person) {
+ $this->setPerson($resource);
+ $this->setThirdParty(NULL);
+ } elseif (NULL === $resource) {
+ $this->setPerson(NULL);
+ $this->setThirdParty(NULL);
+ } else {
+ throw new \UnexpectedValueException(sprintf("the resource ".
+ "should be an instance of %s or %s", Person::class,
+ ThirdParty::class));
+ }
+
+ return $this;
+ }
+
/**
- * @return Person|ThirdParty
+ * @return ThirdParty|Person
+ * @Groups({"read", "write"})
*/
public function getResource()
{
diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php
index c22e84e42..70c5a0505 100644
--- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php
+++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php
@@ -22,17 +22,21 @@
namespace Chill\PersonBundle\Entity;
-use Chill\PersonBundle\Repository\AccompanyingPeriodParticipationRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
+use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/**
* AccompanyingPeriodParticipation Class
*
* @package Chill\PersonBundle\Entity
- * @ORM\Entity(repositoryClass=AccompanyingPeriodParticipationRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_participation")
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "accompanying_period_participation"=AccompanyingPeriodParticipation::class
+ * })
*/
class AccompanyingPeriodParticipation
{
@@ -40,12 +44,14 @@ class AccompanyingPeriodParticipation
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
+ * @Groups({"read"})
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=Person::class, inversedBy="accompanyingPeriodParticipations")
* @ORM\JoinColumn(name="person_id", referencedColumnName="id", nullable=false)
+ * @Groups({"read"})
*/
private $person;
@@ -57,11 +63,13 @@ class AccompanyingPeriodParticipation
/**
* @ORM\Column(type="date", nullable=false)
+ * @Groups({"read"})
*/
private $startDate;
/**
* @ORM\Column(type="date", nullable=true)
+ * @Groups({"read"})
*/
private $endDate = null;
diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php
index a12741dee..4e8787a96 100644
--- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php
+++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php
@@ -2,13 +2,12 @@
namespace Chill\PersonBundle\Entity\Household;
-use Chill\PersonBundle\Repository\Household\HouseholdRepository;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Chill\MainBundle\Entity\Address;
/**
- * @ORM\Entity(repositoryClass=HouseholdRepository::class)
+ * @ORM\Entity
*/
class Household
{
diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMembers.php b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMembers.php
index 80864ecd0..5d16649a8 100644
--- a/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMembers.php
+++ b/src/Bundle/ChillPersonBundle/Entity/Household/HouseholdMembers.php
@@ -3,12 +3,11 @@
namespace Chill\PersonBundle\Entity\Household;
use Doctrine\ORM\Mapping as ORM;
-use Chill\PersonBundle\Repository\Household\HouseholdMembersRepository;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Household\Household;
/**
- * @ORM\Entity(repositoryClass=HouseholdMembersRepository::class)
+ * @ORM\Entity
*/
class HouseholdMembers
{
diff --git a/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php b/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php
index f1addbb2c..43ae1b09f 100644
--- a/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php
+++ b/src/Bundle/ChillPersonBundle/Entity/MaritalStatus.php
@@ -25,7 +25,7 @@ use Doctrine\ORM\Mapping as ORM;
/**
* MaritalStatus
*
- * @ORM\Entity()
+ * @ORM\Entity
* @ORM\Table(name="chill_person_marital_status")
* @ORM\HasLifecycleCallbacks()
*/
diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php
index 3624d4c17..1afa6278c 100644
--- a/src/Bundle/ChillPersonBundle/Entity/Person.php
+++ b/src/Bundle/ChillPersonBundle/Entity/Person.php
@@ -34,17 +34,21 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
+use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/**
* Person Class
*
- * @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonRepository")
+ * @ORM\Entity
* @ORM\Table(name="chill_person_person",
* indexes={@ORM\Index(
* name="person_names",
* columns={"firstName", "lastName"}
* )})
* @ORM\HasLifecycleCallbacks()
+ * @DiscriminatorMap(typeProperty="type", mapping={
+ * "person"=Person::class
+ * })
*/
class Person implements HasCenterInterface
{
diff --git a/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php b/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php
index c5295487e..95603d4ef 100644
--- a/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php
+++ b/src/Bundle/ChillPersonBundle/Entity/PersonAltName.php
@@ -8,7 +8,7 @@ use Doctrine\ORM\Mapping as ORM;
* PersonAltName
*
* @ORM\Table(name="chill_person_alt_name")
- * @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonAltNameRepository")
+ * @ORM\Entity
*/
class PersonAltName
{
diff --git a/src/Bundle/ChillPersonBundle/Entity/PersonNotDuplicate.php b/src/Bundle/ChillPersonBundle/Entity/PersonNotDuplicate.php
index ce2a1c26f..9f116c501 100644
--- a/src/Bundle/ChillPersonBundle/Entity/PersonNotDuplicate.php
+++ b/src/Bundle/ChillPersonBundle/Entity/PersonNotDuplicate.php
@@ -9,7 +9,7 @@ use Chill\MainBundle\Entity\User;
* PersonNotDuplicate
*
* @ORM\Table(name="chill_person_not_duplicate")
- * @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonNotDuplicateRepository")
+ * @ORM\Entity
*/
class PersonNotDuplicate
{
diff --git a/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php b/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php
index 4a2ffaf28..222495e17 100644
--- a/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php
+++ b/src/Bundle/ChillPersonBundle/Entity/PersonPhone.php
@@ -8,7 +8,7 @@ use Doctrine\ORM\Mapping as ORM;
/**
* Person Phones
*
- * @ORM\Entity()
+ * @ORM\Entity
* @ORM\Table(name="chill_person_phone")
*/
class PersonPhone
diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php
index 9e6dcbaa6..5a877e264 100644
--- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php
+++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Evaluation.php
@@ -2,11 +2,10 @@
namespace Chill\PersonBundle\Entity\SocialWork;
-use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
use Doctrine\ORM\Mapping as ORM;
/**
- * @ORM\Entity(repositoryClass=EvaluationRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_social_work_evaluation")
*/
class Evaluation
diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php
index c92e9a736..996fcf3eb 100644
--- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php
+++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php
@@ -2,13 +2,12 @@
namespace Chill\PersonBundle\Entity\SocialWork;
-use Chill\PersonBundle\Repository\SocialWork\GoalRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
- * @ORM\Entity(repositoryClass=GoalRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_social_work_goal")
*/
class Goal
diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php
index be38b1757..37e5c2b18 100644
--- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php
+++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php
@@ -4,13 +4,12 @@ namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
-use Chill\PersonBundle\Repository\SocialWork\ResultRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
- * @ORM\Entity(repositoryClass=ResultRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_social_work_result")
*/
class Result
diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php
index 64413d987..ff7291b45 100644
--- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php
+++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php
@@ -2,13 +2,12 @@
namespace Chill\PersonBundle\Entity\SocialWork;
-use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
- * @ORM\Entity(repositoryClass=SocialActionRepository::class)
+ * @ORM\Entity
* @ORM\Table(name="chill_person_social_action")
*/
class SocialAction
diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php
index cfec01751..6b2152cdf 100644
--- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php
+++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php
@@ -1,15 +1,18 @@
parent;
}
+ public function hasParent(): bool
+ {
+ return $this->parent !== null;
+ }
+
public function setParent(?self $parent): self
{
$this->parent = $parent;
diff --git a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php
index ec3ddbf82..e3583071f 100644
--- a/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php
+++ b/src/Bundle/ChillPersonBundle/Form/CreationPersonType.php
@@ -33,9 +33,10 @@ use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Form\Type\PersonAltNameType;
-class CreationPersonType extends AbstractType
+final class CreationPersonType extends AbstractType
{
-
+ // TODO: This is only used in test.
+ // TODO: See if this is still valid and update accordingly.
const NAME = 'chill_personbundle_person_creation';
const FORM_NOT_REVIEWED = 'not_reviewed';
diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php
index 74052e87c..e9ce20b53 100644
--- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php
+++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php
@@ -3,6 +3,7 @@
namespace Chill\PersonBundle\Menu;
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
+use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Knp\Menu\MenuItem;
use Symfony\Contracts\Translation\TranslatorInterface;
@@ -32,24 +33,31 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
public function buildMenu($menuId, MenuItem $menu, array $parameters): void
{
+ $period = $parameters['accompanyingCourse'];
+
$menu->addChild($this->translator->trans('Resume Accompanying Course'), [
'route' => 'chill_person_accompanying_course_index',
'routeParameters' => [
- 'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
+ 'accompanying_period_id' => $period->getId()
]])
->setExtras(['order' => 10]);
$menu->addChild($this->translator->trans('Edit Accompanying Course'), [
'route' => 'chill_person_accompanying_course_show',
'routeParameters' => [
- 'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
+ 'accompanying_period_id' => $period->getId()
]])
->setExtras(['order' => 20]);
+ if (AccompanyingPeriod::STEP_DRAFT === $period->getStep()) {
+ // no more menu items if the period is draft
+ return;
+ }
+
$menu->addChild($this->translator->trans('Accompanying Course Details'), [
'route' => 'chill_person_accompanying_course_history',
'routeParameters' => [
- 'accompanying_period_id' => $parameters['accompanyingCourse']->getId()
+ 'accompanying_period_id' => $period->getId()
]])
->setExtras(['order' => 30]);
}
diff --git a/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php
index ea6a1d060..0b307204d 100644
--- a/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php
+++ b/src/Bundle/ChillPersonBundle/Menu/SectionMenuBuilder.php
@@ -71,6 +71,14 @@ class SectionMenuBuilder implements LocalMenuBuilderInterface
'icons' => [ 'plus' ]
]);
}
+
+ $menu->addChild($this->translator->trans('Create an accompanying course'), [
+ 'route' => 'chill_person_accompanying_course_new'
+ ])
+ ->setExtras([
+ 'order' => 11,
+ 'icons' => [ 'plus' ]
+ ]);
}
/**
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkGoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkGoalRepository.php
index fc8233d23..d9fa5b6e3 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkGoalRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkGoalRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method AccompanyingPeriodWorkGoal|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriodWorkGoal[] findAll()
* @method AccompanyingPeriodWorkGoal[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class AccompanyingPeriodWorkGoalRepository extends ServiceEntityRepository
+final class AccompanyingPeriodWorkGoalRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, AccompanyingPeriodWorkGoal::class);
+ $this->repository = $entityManager->getRepository(AccompanyingPeriodWorkGoal::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
index f879e97d7..5d455b617 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method AccompanyingPeriodWork|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriodWork[] findAll()
* @method AccompanyingPeriodWork[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class AccompanyingPeriodWorkRepository extends ServiceEntityRepository
+final class AccompanyingPeriodWorkRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, AccompanyingPeriodWork::class);
+ $this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php
index ef1130b62..c99ca8efc 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ClosingMotiveRepository.php
@@ -22,8 +22,9 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
+use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
+use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
-use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
/**
@@ -32,17 +33,24 @@ use Doctrine\ORM\Query\ResultSetMappingBuilder;
*
* @package Chill\PersonBundle\Repository
*/
-class ClosingMotiveRepository extends EntityRepository
+final class ClosingMotiveRepository
{
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
+ {
+ $this->repository = $entityManager->getRepository(ClosingMotive::class);
+ }
+
/**
* @param bool $onlyLeaf
* @return mixed
*/
public function getActiveClosingMotive(bool $onlyLeaf = true)
{
- $rsm = new ResultSetMappingBuilder($this->getEntityManager());
- $rsm->addRootEntityFromClassMetadata($this->getClassName(), 'cm');
-
+ $rsm = new ResultSetMappingBuilder($this->repository->getEntityManager());
+ $rsm->addRootEntityFromClassMetadata($this->repository->getClassName(), 'cm');
+
$sql = "SELECT ".(string) $rsm."
FROM chill_person_accompanying_period_closingmotive AS cm
WHERE
@@ -55,10 +63,11 @@ class ClosingMotiveRepository extends EntityRepository
}
$sql .= " ORDER BY cm.ordering ASC";
-
- return $this->_em
+
+ return $this
+ ->repository
+ ->getEntityManager()
->createNativeQuery($sql, $rsm)
- ->getResult()
- ;
+ ->getResult();
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/CommentRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/CommentRepository.php
index b41e77591..aca7a9c68 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/CommentRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/CommentRepository.php
@@ -23,8 +23,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method Comment|null find($id, $lockMode = null, $lockVersion = null)
@@ -32,11 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Comment[] findAll()
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class CommentRepository extends ServiceEntityRepository
+final class CommentRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, Comment::class);
+ $this->repository = $entityManager->getRepository(Comment::class);
}
-
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/OriginRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/OriginRepository.php
index 6a5b28901..c2851b851 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/OriginRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/OriginRepository.php
@@ -23,8 +23,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method Origin|null find($id, $lockMode = null, $lockVersion = null)
@@ -32,11 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Origin[] findAll()
* @method Origin[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class OriginRepository extends ServiceEntityRepository
+final class OriginRepository
{
- public function __construct(ManagerRegistry $registry)
- {
- parent::__construct($registry, Origin::class);
- }
+ private EntityRepository $repository;
+ public function __construct(EntityManagerInterface $entityManager)
+ {
+ $this->repository = $entityManager->getRepository(Origin::class);
+ }
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ResourceRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ResourceRepository.php
index 46eaaaaa0..c32f74762 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ResourceRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/ResourceRepository.php
@@ -23,6 +23,7 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
+use Doctrine\ORM\EntityRepository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
@@ -32,11 +33,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Resource[] findAll()
* @method Resource[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class ResourceRepository extends ServiceEntityRepository
+final class ResourceRepository extends ServiceEntityRepository
{
+ private EntityRepository $repository;
+
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Resource::class);
}
-
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodParticipationRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodParticipationRepository.php
index fbe957ecf..0f157ed42 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodParticipationRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodParticipationRepository.php
@@ -23,8 +23,8 @@
namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null)
@@ -32,11 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriodParticipation[] findAll()
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class AccompanyingPeriodParticipationRepository extends ServiceEntityRepository
+final class AccompanyingPeriodParticipationRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, AccompanyingPeriodParticipation::class);
+ $this->repository = $entityManager->getRepository(AccompanyingPeriodParticipation::class);
}
-
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php
index fffb55ede..07aa0a55f 100644
--- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriodRepository.php
@@ -23,9 +23,8 @@
namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
-use Chill\PersonBundle\Entity\Person;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method AccompanyingPeriod|null find($id, $lockMode = null, $lockVersion = null)
@@ -33,10 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriod[] findAll()
* @method AccompanyingPeriod[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class AccompanyingPeriodRepository extends ServiceEntityRepository
+final class AccompanyingPeriodRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, AccompanyingPeriod::class);
+ $this->repository = $entityManager->getRepository(AccompanyingPeriod::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdMembersRepository.php b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdMembersRepository.php
index feea6d44d..b11d3d93a 100644
--- a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdMembersRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdMembersRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMembers;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method HouseholdMembers|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,11 +12,13 @@ use Doctrine\Persistence\ManagerRegistry;
* @method HouseholdMembers[] findAll()
* @method HouseholdMembers[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class HouseholdMembersRepository extends ServiceEntityRepository
+final class HouseholdMembersRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, HouseholdMembers::class);
+ $this->repository = $entityManager->getRepository(HouseholdMembers::class);
}
// /**
diff --git a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdRepository.php b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdRepository.php
index 38522806c..78a68f56d 100644
--- a/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/Household/HouseholdRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\Household;
use Chill\PersonBundle\Entity\Household\Household;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method Household|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,11 +12,13 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Household[] findAll()
* @method Household[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class HouseholdRepository extends ServiceEntityRepository
+final class HouseholdRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, Household::class);
+ $this->repository = $entityManager->getRepository(Household::class);
}
// /**
diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonAltNameRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonAltNameRepository.php
index 315cee94f..c5dea689a 100644
--- a/src/Bundle/ChillPersonBundle/Repository/PersonAltNameRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/PersonAltNameRepository.php
@@ -2,12 +2,22 @@
namespace Chill\PersonBundle\Repository;
+use Chill\PersonBundle\Entity\PersonAltName;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
+
/**
* PersonAltNameRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
-class PersonAltNameRepository extends \Doctrine\ORM\EntityRepository
+final class PersonAltNameRepository
{
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
+ {
+ $this->repository = $entityManager->getRepository(PersonAltName::class);
+ }
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonNotDuplicateRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonNotDuplicateRepository.php
index 8ab711b15..989baaeec 100644
--- a/src/Bundle/ChillPersonBundle/Repository/PersonNotDuplicateRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/PersonNotDuplicateRepository.php
@@ -4,6 +4,7 @@ namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonNotDuplicate;
+use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
/**
@@ -11,8 +12,15 @@ use Doctrine\ORM\EntityRepository;
*
* @package Chill\PersonBundle\Repository
*/
-class PersonNotDuplicateRepository extends EntityRepository
+final class PersonNotDuplicateRepository
{
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
+ {
+ $this->repository = $entityManager->getRepository(PersonNotDuplicate::class);
+ }
+
/**
* @param \Chill\PersonBundle\Entity\Person $person
*
@@ -20,7 +28,7 @@ class PersonNotDuplicateRepository extends EntityRepository
*/
public function findNotDuplicatePerson(Person $person)
{
- $qb = $this->createQueryBuilder('pnd');
+ $qb = $this->repository->createQueryBuilder('pnd');
$qb->select('pnd')
->where('pnd.person1 = :person OR pnd.person2 = :person')
;
diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php
index ccc56b639..12733a668 100644
--- a/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/PersonRepository.php
@@ -18,16 +18,25 @@
namespace Chill\PersonBundle\Repository;
+use Chill\PersonBundle\Entity\Person;
+use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
-/**
- * Class PersonRepository
- *
- * @package Chill\PersonBundle\Repository
- */
-class PersonRepository extends EntityRepository
+final class PersonRepository
{
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
+ {
+ $this->repository = $entityManager->getRepository(Person::class);
+ }
+
+ public function find($id, $lockMode = null, $lockVersion = null)
+ {
+ return $this->repository->find($id, $lockMode, $lockVersion);
+ }
+
/**
* @param string $phonenumber
* @param $centers
@@ -44,7 +53,7 @@ class PersonRepository extends EntityRepository
$maxResults,
array $only = ['mobile', 'phone']
) {
- $qb = $this->createQueryBuilder('p');
+ $qb = $this->repository->createQueryBuilder('p');
$qb->select('p');
$this->addByCenters($qb, $centers);
@@ -71,7 +80,7 @@ class PersonRepository extends EntityRepository
array $only = ['mobile', 'phone']
): int
{
- $qb = $this->createQueryBuilder('p');
+ $qb = $this->repository->createQueryBuilder('p');
$qb->select('COUNT(p)');
$this->addByCenters($qb, $centers);
diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php
index b030e1617..c9ab3c931 100644
--- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method Evaluation|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Evaluation[] findAll()
* @method Evaluation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class EvaluationRepository extends ServiceEntityRepository
+final class EvaluationRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, Evaluation::class);
+ $this->repository = $entityManager->getRepository(Evaluation::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php
index 031c31764..30576b4ed 100644
--- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Goal;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method Goal|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Goal[] findAll()
* @method Goal[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class GoalRepository extends ServiceEntityRepository
+final class GoalRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, Goal::class);
+ $this->repository = $entityManager->getRepository(Goal::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php
index 511823d61..004a2f82c 100644
--- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Result;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method Result|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Result[] findAll()
* @method Result[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class ResultRepository extends ServiceEntityRepository
+final class ResultRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, Result::class);
+ $this->repository = $entityManager->getRepository(Result::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php
index f0f2e81b4..cfa9adbd7 100644
--- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php
@@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
-use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method SocialAction|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method SocialAction[] findAll()
* @method SocialAction[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class SocialActionRepository extends ServiceEntityRepository
+final class SocialActionRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, SocialAction::class);
+ $this->repository = $entityManager->getRepository(SocialAction::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php
index e64b057f4..2f6eb3ba1 100644
--- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php
+++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php
@@ -4,7 +4,8 @@ namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
-use Doctrine\Persistence\ManagerRegistry;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\EntityRepository;
/**
* @method SocialIssue|null find($id, $lockMode = null, $lockVersion = null)
@@ -12,10 +13,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method SocialIssue[] findAll()
* @method SocialIssue[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
-class SocialIssueRepository extends ServiceEntityRepository
+final class SocialIssueRepository
{
- public function __construct(ManagerRegistry $registry)
+ private EntityRepository $repository;
+
+ public function __construct(EntityManagerInterface $entityManager)
{
- parent::__construct($registry, SocialIssue::class);
+ $this->repository = $entityManager->getRepository(SocialIssue::class);
}
}
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/App.vue
deleted file mode 100644
index 971eca1c7..000000000
--- a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/App.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/AccompanyingCourse.vue b/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/AccompanyingCourse.vue
deleted file mode 100644
index d08798c00..000000000
--- a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/AccompanyingCourse.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
Parcours
-
- id
- {{ accompanying_course.id }}
- opening_date
- {{ accompanying_course.opening_date }}
- closing_date
- {{ accompanying_course.closing_date }}
- remark
- {{ accompanying_course.remark }}
- closing_motive
- {{ accompanying_course.closing_motive }}
-
-
-
-
-
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/PersonItem.vue b/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/PersonItem.vue
deleted file mode 100644
index f75f0779b..000000000
--- a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/PersonItem.vue
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- {{ person.firstname }}
- {{ person.lastname }}
- {{ person.startdate }}
- {{ person.enddate }}
-
-
-
-
-
-
-
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/PersonsAssociated.vue
deleted file mode 100644
index f4ccf6964..000000000
--- a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/PersonsAssociated.vue
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
Usagers concernés
-
-
{{ counter }} usagers
-
-
-
-
- firstname
- lastname
- startdate
- enddate
- actions
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/Requestor.vue b/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/Requestor.vue
deleted file mode 100644
index 75a95e241..000000000
--- a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/components/Requestor.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
Demandeur
- {{ accompanying_course.id }}
- {{ accompanying_course.remark }}
-
-
-
-
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/index.js b/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/index.js
deleted file mode 100644
index 4335113f7..000000000
--- a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/index.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import App from './App.vue';
-import { createApp } from 'vue';
-
-const app = createApp({
- template: ` `
-})
-.component('app', App)
-.mount('#accompanying-course');
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/store/.keep b/src/Bundle/ChillPersonBundle/Resources/public/js/AccompanyingCourse/store/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue
new file mode 100644
index 000000000..8dd66d0f2
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/App.vue
@@ -0,0 +1,106 @@
+
+
+
+
+ {{ $t('course.title.draft') }}
+ {{ $t('course.title.active') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js
new file mode 100644
index 000000000..70b2ba56e
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/api.js
@@ -0,0 +1,177 @@
+/*
+* Endpoint v.2 chill_api_single_accompanying_course__entity
+* method GET/HEAD, get AccompanyingCourse Instance
+*
+* @id integer - id of accompanyingCourse
+*/
+const getAccompanyingCourse = (id) => {
+ const url = `/api/1.0/person/accompanying-course/${id}.json`;
+ return fetch(url)
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint v.2 chill_api_single_accompanying_course__entity
+* method PATCH, patch AccompanyingCourse Instance
+*
+* @id integer - id of accompanyingCourse
+* @body Object - dictionary with changes to post
+*/
+const patchAccompanyingCourse = (id, body) => {
+ console.log('body', body);
+ const url = `/api/1.0/person/accompanying-course/${id}.json`;
+ return fetch(url, {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json;charset=utf-8'
+ },
+ body: JSON.stringify(body)
+ })
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint to change 'DRAFT' step to 'CONFIRMED'
+*/
+const confirmAccompanyingCourse = (id) => {
+ const url = `/api/1.0/person/accompanying-course/${id}/confirm.json`
+ return fetch(url, {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json;charset=utf-8'}
+ })
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint
+*/
+const getSocialIssues = () => {
+ const url = `/api/1.0/person/social-work/social-issue.json`;
+ return fetch(url)
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint v.2 chill_api_single_accompanying_course_participation,
+* method POST/DELETE, add/close a participation to the accompanyingCourse
+*
+* @id integer - id of accompanyingCourse
+* @payload integer - id of person
+* @method string - POST or DELETE
+*/
+const postParticipation = (id, payload, method) => {
+ const body = { type: payload.type, id: payload.id };
+ const url = `/api/1.0/person/accompanying-course/${id}/participation.json`;
+ return fetch(url, {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json;charset=utf-8'
+ },
+ body: JSON.stringify(body)
+ })
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint v.2 chill_api_single_accompanying_course_requestor,
+* method POST/DELETE, add/close a requestor to the accompanyingCourse
+*
+* @id integer - id of accompanyingCourse
+* @payload object of type person|thirdparty
+* @method string - POST or DELETE
+*/
+const postRequestor = (id, payload, method) => {
+ //console.log('payload', payload);
+ const body = (payload)? { type: payload.type, id: payload.id } : {};
+ console.log('body', body);
+ const url = `/api/1.0/person/accompanying-course/${id}/requestor.json`;
+ return fetch(url, {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json;charset=utf-8'
+ },
+ body: JSON.stringify(body)
+ })
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint v.2 chill_api_single_accompanying_course_resource,
+* method POST/DELETE, add/remove a resource to the accompanyingCourse
+*
+* @id integer - id of accompanyingCourse
+* @payload object of type person|thirdparty
+* @method string - POST or DELETE
+*/
+const postResource = (id, payload, method) => {
+ //console.log('payload', payload);
+ const body = { type: "accompanying_period_resource" };
+ switch (method) {
+ case 'DELETE':
+ body['id'] = payload.id;
+ break;
+ default:
+ body['resource'] = { type: payload.type, id: payload.id };
+ }
+ console.log('body', body);
+ const url = `/api/1.0/person/accompanying-course/${id}/resource.json`;
+ return fetch(url, {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json;charset=utf-8'
+ },
+ body: JSON.stringify(body)
+ })
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint to Add/remove SocialIssue
+*/
+const postSocialIssue = (id, body, method) => {
+ //console.log('api body and method', body, method);
+ const url = `/api/1.0/person/accompanying-course/${id}/socialissue.json`;
+ return fetch(url, {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json;charset=utf-8'
+ },
+ body: JSON.stringify(body)
+ })
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+export {
+ getAccompanyingCourse,
+ patchAccompanyingCourse,
+ confirmAccompanyingCourse,
+ getSocialIssues,
+ postParticipation,
+ postRequestor,
+ postResource,
+ postSocialIssue
+};
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner.vue
new file mode 100644
index 000000000..7a91e39c3
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner.vue
@@ -0,0 +1,92 @@
+
+
+
+
+ {{ $t('course.id') }}
+ {{ accompanyingCourse.id }}
+
+
+ {{ $t('course.closing_date') }}
+ {{ $d(accompanyingCourse.closingDate.datetime, 'short') }}
+
+ {{ $t('course.closing_motive') }}
+ {{ accompanyingCourse.closingMotive.name.fr }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t('course.step.draft') }}
+
+
+
+
+
+ {{ $t('course.step.active') }}
+
+
+
+
+ {{ $t('course.open_at') }}{{ $d(accompanyingCourse.openingDate.datetime, 'text') }}
+
+
+
+ {{ $t('course.by') }}{{ accompanyingCourse.user.username }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/SocialIssue.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/SocialIssue.vue
new file mode 100644
index 000000000..7ed78355d
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/SocialIssue.vue
@@ -0,0 +1,20 @@
+
+ {{ issue.text }}
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue
new file mode 100644
index 000000000..67ef8c193
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Banner/ToggleFlags.vue
@@ -0,0 +1,89 @@
+
+
+
+
+
+ {{ $t('course.emergency') }}
+
+
+ {{ $t('course.confidential') }}
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue
new file mode 100644
index 000000000..858d6d04e
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Comment.vue
@@ -0,0 +1,99 @@
+
+
+
{{ $t('comment.title') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue
new file mode 100644
index 000000000..c2143b72a
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Confirm.vue
@@ -0,0 +1,80 @@
+
+
+
+ {{ $t('confirm.title') }}
+
+
+
+
+ {{ $t('confirm.text_draft') }}
+ {{ $t('course.step.draft') }}
+
+
+ {{ $t('confirm.text_active') }}
+ {{ $t('course.step.active') }}
+
+
+
+
+
+ {{ $t('confirm.ok') }}
+
+
+
+
+
+
+
+
+ {{ $t('confirm.sure') }}
+
+
+ {{ $t('confirm.sure_description') }}
+
+
+
+ {{ $t('confirm.ok') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue
new file mode 100644
index 000000000..7c61b066c
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated.vue
@@ -0,0 +1,91 @@
+
+
+
{{ $t('persons_associated.title')}}
+
+
+ {{ $tc('persons_associated.counter', counter) }}
+
+
+
+
+
+ {{ $t('persons_associated.firstname') }}
+ {{ $t('persons_associated.lastname') }}
+ {{ $t('persons_associated.startdate') }}
+ {{ $t('persons_associated.enddate') }}
+ {{ $t('action.actions') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated/PersonItem.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated/PersonItem.vue
new file mode 100644
index 000000000..4fa2caf12
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/PersonsAssociated/PersonItem.vue
@@ -0,0 +1,58 @@
+
+
+ {{ participation.person.firstName }}
+ {{ participation.person.lastName }}
+
+ {{ $d(participation.startDate.datetime, 'short') }}
+
+
+ {{ $d(participation.endDate.datetime, 'short') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue
new file mode 100644
index 000000000..b007bc17e
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Referrer.vue
@@ -0,0 +1,77 @@
+
+
+
{{ $t('referrer.title') }}
+
+
+
+ {{ $t('referrer.label') }}
+
+
+
+
+
+
+
+
+ {{ $t('referrer.assign_me') }}
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue
new file mode 100644
index 000000000..c6783677a
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Requestor.vue
@@ -0,0 +1,122 @@
+
+
+
+
{{ $t('requestor.title') }}
+
+
+
+
+ {{ $t('requestor.is_anonymous') }}
+
+
+
{{ $t('requestor.type') }}
+
{{ accompanyingCourse.requestor.type }}
+
{{ $t('requestor.text') }}
+
{{ accompanyingCourse.requestor.text }}
+
{{ $t('requestor.is_anonymous') }}
+
{{ accompanyingCourse.requestorAnonymous }}
+
+
+
{{ $t('requestor.person_id') }}
+ {{ accompanyingCourse.requestor.person_id }}
+ {{ $t('requestor.birthdate') }}
+ {{ $d(accompanyingCourse.requestor.birthdate.datetime, 'short') }}
+ {{ $t('requestor.center') }}
+ {{ accompanyingCourse.requestor.center.name }}
+ {{ $t('requestor.firstName') }}
+ {{ accompanyingCourse.requestor.firstName }}
+ {{ $t('requestor.lastName') }}
+ {{ accompanyingCourse.requestor.lastName }}
+ {{ $t('requestor.phonenumber') }}
+ {{ accompanyingCourse.requestor.phonenumber }}
+ {{ $t('requestor.mobilenumber') }}
+ {{ accompanyingCourse.requestor.mobilenumber }}
+ {{ $t('requestor.altNames') }}
+ {{ accompanyingCourse.requestor.altNames }}
+
+
+
+
{{ $t('requestor.person_id') }}
+ {{ accompanyingCourse.requestor.thirdparty_id }}
+ {{ $t('requestor.address') }}
+ {{ accompanyingCourse.requestor.address.text }}
+ {{ $t('requestor.location') }}
+ {{ accompanyingCourse.requestor.address.postcode.name }}
+
+
+
+
+
+ {{ $t('action.remove') }}
+
+
+
+
+
+ {{ $t('requestor.counter') }}
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue
new file mode 100644
index 000000000..596da7483
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources.vue
@@ -0,0 +1,85 @@
+
+
+
+
{{ $t('resources.title')}}
+
+
+ {{ $tc('resources.counter', counter) }}
+
+
+
+
+
+ {{ $t('resources.text') }}
+ {{ $t('resources.description') }}
+ {{ $t('action.actions') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources/ResourceItem.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources/ResourceItem.vue
new file mode 100644
index 000000000..ffec23ddf
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Resources/ResourceItem.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+ {{ $t('item.type_person') }}
+ {{ $t('item.type_thirdparty') }}
+
+ {{ resource.resource.text }}
+
+
+
+ {{ $tc('person.born') }}{{ $d(resource.resource.birthdate.datetime, 'short') }}
+
+
+ {{ resource.resource.address.text }}
+ {{ resource.resource.address.postcode.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/SocialIssue.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/SocialIssue.vue
new file mode 100644
index 000000000..26bd160c1
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/SocialIssue.vue
@@ -0,0 +1,80 @@
+
+
+
{{ $t('social_issue.title') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav.vue
new file mode 100644
index 000000000..117c4f09b
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav.vue
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav/Item.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav/Item.vue
new file mode 100644
index 000000000..413f216cc
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/StickyNav/Item.vue
@@ -0,0 +1,30 @@
+
+
+
+ {{ item.key }}
+
+
+
+ {{ item.key }}
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Test.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Test.vue
new file mode 100644
index 000000000..a72902b25
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/components/Test.vue
@@ -0,0 +1,81 @@
+
+
+
Tests
+
+
+
+
+
+ {{ $t('action.show_modal') }}
+
+
+
+
+ Ouvrir une seconde modale
+
+
+
+
+
+
+
+ Le titre de ma modale
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit.
+ Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit.
+ Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus luctus facilisis suscipit. Cras pulvinar, purus sagittis pulvinar porta, enim ex posuere lacus, in pulvinar lectus magna in odio. Nullam iaculis congue lorem ac suscipit. Proin ut rutrum augue. Ut vehicula risus nec hendrerit ullamcorper. Ut volutpat eu mi eget viverra. Morbi dictum placerat suscipit.
+ Quisque non erat tincidunt, lacinia justo ut, pulvinar nisl. Nunc id enim ut sem pretium interdum consectetur eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam posuere erat eget augue finibus luctus. Maecenas auctor, tortor non luctus ultrices, neque neque porttitor ex, nec lacinia lorem ligula et elit. Sed tempor nulla vitae lorem sollicitudin dictum. Vestibulum nec arcu eget elit pulvinar pretium. Phasellus facilisis metus sed diam luctus, feugiat scelerisque velit dignissim.
+
+
+
+ {{ $t('action.next')}}
+
+
+
+
+
+
+
+ Une autre modale
+
+
+ modal 2
+
+
+
+ {{ $t('action.save')}}
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js
new file mode 100644
index 000000000..66b6d6683
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js
@@ -0,0 +1,23 @@
+import { createApp } from 'vue'
+import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'
+import { appMessages } from './js/i18n'
+import { initPromise } from './store'
+
+import App from './App.vue';
+
+initPromise.then(store => {
+
+ //console.log('store in create_store', store);
+ //console.log('store accompanyingCourse', store.state.accompanyingCourse);
+
+ const i18n = _createI18n(appMessages);
+
+ const app = createApp({
+ template: ` `,
+ })
+ .use(store)
+ .use(i18n)
+ .component('app', App)
+ .mount('#accompanying-course');
+
+});
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js
new file mode 100644
index 000000000..242d48b8f
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/js/i18n.js
@@ -0,0 +1,95 @@
+import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'
+
+const appMessages = {
+ fr: {
+ course: {
+ id: "id",
+ title: {
+ draft: "Création d'un nouveau parcours",
+ active: "Modification du parcours"
+ },
+ opening_date: "Date d'ouverture",
+ closing_date: "Date de clôture",
+ remark: "Commentaire",
+ closing_motive: "Motif de clôture",
+ user: "TMS",
+ flags: "Indicateurs",
+ status: "État",
+ step: {
+ draft: "Brouillon",
+ active: "En file active"
+ },
+ open_at: "ouvert le ",
+ by: "par ",
+ emergency: "urgent",
+ confidential: "confidentiel",
+ regular: "régulier",
+ occasional: "ponctuel"
+ },
+ persons_associated: {
+ title: "Usagers concernés",
+ counter: "Il n'y a pas encore d'usager | 1 usager | {count} usagers",
+ firstname: "Prénom",
+ lastname: "Nom",
+ startdate: "Date d'entrée",
+ enddate: "Date de sortie",
+ add_persons: "Ajouter des usagers",
+ },
+ requestor: {
+ title: "Demandeur",
+ add_requestor: "Ajouter un demandeur",
+ is_anonymous: "Le demandeur est anonyme",
+ counter: "Il n'y a pas encore de demandeur",
+ type: "Type",
+ person_id: "id",
+ text: "Dénomination",
+ firstName: "Prénom",
+ lastName: "Nom",
+ birthdate: "Date de naissance",
+ center: "Centre",
+ phonenumber: "Téléphone",
+ mobilenumber: "Mobile",
+ altNames: "Autres noms",
+ address: "Adresse",
+ location: "Localité",
+ },
+ social_issue: {
+ title: "Problématiques sociales",
+ label: "Choisir les problématiques sociales",
+ },
+ referrer: {
+ title: "Référent du parcours",
+ label: "Vous pouvez choisir un TMS ou vous assigner directement comme référent",
+ placeholder: "Choisir un TMS",
+ assign_me: "M'assigner comme référent",
+ },
+ resources: {
+ title: "Interlocuteurs privilégiés",
+ counter: "Il n'y a pas encore d'interlocuteur | 1 interlocuteur | {count} interlocuteurs",
+ text: "Dénomination",
+ description: "Description",
+ add_resources: "Ajouter des interlocuteurs",
+ },
+ comment: {
+ title: "Observations",
+ label: "Ajout d'une note",
+ content: "Rédigez une première note...",
+ created_by: "créé par {0}, le {1}"
+ },
+ confirm: {
+ title: "Confirmation",
+ text_draft: "Le parcours est actuellement à l'état de ",
+ text_active: "En validant cette étape, vous lui donnez le statut ",
+ sure: "Êtes-vous sûr ?",
+ sure_description: "Une fois le changement confirmé, il n'est plus possible de le remettre à l'état de brouillon !",
+ ok: "Confirmer le parcours"
+ },
+
+ }
+};
+
+Object.assign(appMessages.fr, personMessages.fr);
+
+export {
+ appMessages
+};
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js
new file mode 100644
index 000000000..c9121c5fd
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/store/index.js
@@ -0,0 +1,204 @@
+import 'es6-promise/auto';
+import { createStore } from 'vuex';
+import { getAccompanyingCourse,
+ patchAccompanyingCourse,
+ confirmAccompanyingCourse,
+ postParticipation,
+ postRequestor,
+ postResource,
+ postSocialIssue } from '../api';
+
+const debug = process.env.NODE_ENV !== 'production';
+const id = window.accompanyingCourseId;
+
+let initPromise = getAccompanyingCourse(id)
+ .then(accompanying_course => new Promise((resolve, reject) => {
+
+ const store = createStore({
+ strict: debug,
+ modules: {
+ },
+ state: {
+ accompanyingCourse: accompanying_course,
+ errorMsg: []
+ },
+ getters: {
+ },
+ mutations: {
+ catchError(state, error) {
+ state.errorMsg.push(error);
+ },
+ removeParticipation(state, participation) {
+ //console.log('### mutation: remove participation', participation.id);
+ state.accompanyingCourse.participations = state.accompanyingCourse.participations.filter(element => element !== participation);
+ },
+ closeParticipation(state, { participation, payload }) {
+ //console.log('### mutation: close item', { participation, payload });
+ // find row position and replace by closed participation
+ state.accompanyingCourse.participations.splice(
+ state.accompanyingCourse.participations.findIndex(element => element === payload), 1, participation
+ );
+ },
+ addParticipation(state, participation) {
+ //console.log('### mutation: add participation', participation);
+ state.accompanyingCourse.participations.push(participation);
+ },
+ removeRequestor(state) {
+ //console.log('### mutation: removeRequestor');
+ state.accompanyingCourse.requestor = null;
+ },
+ addRequestor(state, requestor) {
+ //console.log('### mutation: addRequestor', requestor);
+ state.accompanyingCourse.requestor = requestor;
+ },
+ requestorIsAnonymous(state, value) {
+ //console.log('### mutation: requestorIsAnonymous', value);
+ state.accompanyingCourse.requestorAnonymous = value;
+ },
+ removeResource(state, resource) {
+ //console.log('### mutation: removeResource', resource);
+ state.accompanyingCourse.resources = state.accompanyingCourse.resources.filter(element => element !== resource);
+ },
+ addResource(state, resource) {
+ //console.log('### mutation: addResource', resource);
+ state.accompanyingCourse.resources.push(resource);
+ },
+ toggleIntensity(state, value) {
+ state.accompanyingCourse.intensity = value;
+ },
+ toggleEmergency(state, value) {
+ //console.log('### mutation: toggleEmergency');
+ state.accompanyingCourse.emergency = value;
+ },
+ toggleConfidential(state, value) {
+ //console.log('### mutation: toggleConfidential');
+ state.accompanyingCourse.confidential = value;
+ },
+ postFirstComment(state, comment) {
+ //console.log('### mutation: postFirstComment', comment);
+ state.accompanyingCourse.initialComment = comment;
+ },
+ updateSocialIssues(state, value) {
+ state.accompanyingCourse.socialIssues = value;
+ },
+ confirmAccompanyingCourse(state, response) {
+ //console.log('### mutation: confirmAccompanyingCourse: response', response);
+ state.accompanyingCourse.step = response.step;
+ }
+ },
+ actions: {
+ removeParticipation({ commit }, payload) {
+ commit('removeParticipation', payload);
+ // fetch DELETE request...
+ },
+ closeParticipation({ commit }, payload) {
+ //console.log('## action: fetch delete participation: payload', payload);
+ postParticipation(id, payload.person, 'DELETE')
+ .then(participation => new Promise((resolve, reject) => {
+ commit('closeParticipation', { participation, payload });
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ addParticipation({ commit }, payload) {
+ //console.log('## action: fetch post participation (select item): payload', payload);
+ postParticipation(id, payload.result, 'POST')
+ .then(participation => new Promise((resolve, reject) => {
+ commit('addParticipation', participation);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ removeRequestor({ commit, dispatch }) {
+ //console.log('## action: fetch delete requestor');
+ postRequestor(id, null, 'DELETE')
+ .then(requestor => new Promise((resolve, reject) => {
+ commit('removeRequestor');
+ dispatch('requestorIsAnonymous', false);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ addRequestor({ commit }, payload) {
+ //console.log('## action: fetch post requestor: payload', payload);
+ postRequestor(id, payload.result, 'POST')
+ .then(requestor => new Promise((resolve, reject) => {
+ commit('addRequestor', requestor);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ requestorIsAnonymous({ commit }, payload) {
+ //console.log('## action: fetch patch AccompanyingCourse: payload', payload);
+ patchAccompanyingCourse(id, { type: "accompanying_period", requestorAnonymous: payload })
+ .then(course => new Promise((resolve, reject) => {
+ commit('requestorIsAnonymous', course.requestorAnonymous)
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ removeResource({ commit }, payload) {
+ //console.log('## action: fetch postResource: payload', payload);
+ postResource(id, payload, 'DELETE')
+ .then(resource => new Promise((resolve, reject) => {
+ commit('removeResource', payload) // mieux un retour de l'objet !
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ addResource({ commit }, payload) {
+ //console.log('## action: fetch postResource: payload', payload);
+ postResource(id, payload.result, 'POST')
+ .then(resource => new Promise((resolve, reject) => {
+ commit('addResource', resource)
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ toggleIntensity({ commit }, payload) {
+ //console.log(payload);
+ patchAccompanyingCourse(id, { type: "accompanying_period", intensity: payload })
+ .then(course => new Promise((resolve, reject) => {
+ commit('toggleIntensity', course.intensity);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ toggleEmergency({ commit }, payload) {
+ patchAccompanyingCourse(id, { type: "accompanying_period", emergency: payload })
+ .then(course => new Promise((resolve, reject) => {
+ commit('toggleEmergency', course.emergency);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ toggleConfidential({ commit }, payload) {
+ patchAccompanyingCourse(id, { type: "accompanying_period", confidential: payload })
+ .then(course => new Promise((resolve, reject) => {
+ commit('toggleConfidential', course.confidential);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ postFirstComment({ commit }, payload) {
+ //console.log('## action: postFirstComment: payload', payload);
+ patchAccompanyingCourse(id, { type: "accompanying_period", initialComment: payload })
+ .then(course => new Promise((resolve, reject) => {
+ commit('postFirstComment', course.initialComment);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ updateSocialIssues({ commit }, { payload, body, method }) {
+ //console.log('## action: payload', { payload, body, method });
+ postSocialIssue(id, body, method)
+ .then(response => new Promise((resolve, reject) => {
+ //console.log('response', response);
+ commit('updateSocialIssues', payload);
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ },
+ confirmAccompanyingCourse({ commit }) {
+ //console.log('## action: confirmAccompanyingCourse');
+ confirmAccompanyingCourse(id)
+ .then(response => new Promise((resolve, reject) => {
+ commit('confirmAccompanyingCourse', response);
+ console.log('fetch resolve'); // redirection with #top anchor
+ resolve();
+ })).catch((error) => { commit('catchError', error) });
+ }
+ }
+ });
+ resolve(store);
+ }));
+
+export { initPromise };
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_api/AddPersons.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_api/AddPersons.js
new file mode 100644
index 000000000..cf2404288
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_api/AddPersons.js
@@ -0,0 +1,46 @@
+/*
+* Build query string with query and options
+*/
+const parametersToString = ({ query, options }) => {
+ let types ='';
+ options.type.forEach(function(type) {
+ types += '&type[]=' + type;
+ });
+ return 'q=' + query + types;
+};
+
+/*
+* Endpoint chill_person_search
+* method GET, get a list of persons
+*
+* @query string - the query to search for
+*/
+const searchPersons = ({ query, options }) => {
+ let queryStr = parametersToString({ query, options });
+ let url = `/fr/search.json?name=person_regular&${queryStr}`;
+ return fetch(url)
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+/*
+* Endpoint v.2 chill_main_search_global
+* method GET, get a list of persons and thirdparty
+*
+* NOTE: this is a temporary WIP endpoint, return inconsistent random results
+* @query string - the query to search for
+*/
+const searchPersons_2 = ({ query, options }) => {
+ let queryStr = parametersToString({ query, options });
+ let url = `/api/1.0/search.json?${queryStr}`;
+ return fetch(url)
+ .then(response => {
+ if (response.ok) { return response.json(); }
+ throw Error('Error with request resource response');
+ });
+};
+
+
+export { searchPersons, searchPersons_2 };
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue
new file mode 100644
index 000000000..a37f93e7e
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons.vue
@@ -0,0 +1,254 @@
+
+
+
+
+ {{ $t(buttonTitle) }}
+
+
+
+
+
+
+
+
+ {{ $t(modalTitle) }}
+
+
+
+
+
+
+
+ {{ $tc('add_persons.suggested_counter', suggestedCounter) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('action.create') }} "{{ query }}"
+
+
+
+
+
+
+ {{ $t('action.add')}}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue
new file mode 100644
index 000000000..3846f7b39
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/PersonSuggestion.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypePerson.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypePerson.vue
new file mode 100644
index 000000000..af5b13b16
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypePerson.vue
@@ -0,0 +1,35 @@
+
+
+
+
+ {{ item.result.text }}
+
+
+ {{ $d(item.result.birthdate.datetime, 'short') }}
+
+
+
+
+
+
+ {{ $t('item.type_person') }}
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeThirdParty.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeThirdParty.vue
new file mode 100644
index 000000000..37cee7852
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_components/AddPersons/TypeThirdParty.vue
@@ -0,0 +1,36 @@
+
+
+
+
+ {{ item.result.text }}
+
+
+ {{ item.result.address.text }} -
+ {{ item.result.address.postcode.name }}
+
+
+
+
+
+
+ {{ $t('item.type_thirdparty') }}
+
+
+
+
+
+
+
diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_js/i18n.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_js/i18n.js
new file mode 100644
index 000000000..e92a81e62
--- /dev/null
+++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/_js/i18n.js
@@ -0,0 +1,26 @@
+const personMessages = {
+ fr: {
+ add_persons: {
+ title: "Ajouter des usagers",
+ suggested_counter: "Pas de résultats | 1 résultat | {count} résultats",
+ selected_counter: " 1 sélectionné | {count} sélectionnés",
+ search_some_persons: "Rechercher des personnes..",
+ },
+ item: {
+ type_person: "Usager",
+ type_user: "TMS",
+ type_thirdparty: "Tiers",
+ type_household: "Ménage"
+ },
+ person: {
+ firstname: "Prénom",
+ lastname: "Nom",
+ born: "né le ",
+ },
+ error_only_one_person: "Une seule personne peut être sélectionnée !"
+ }
+};
+
+export {
+ personMessages
+};
diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig
index 6ddb46246..3c1d0dadc 100644
--- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig
+++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig
@@ -2,39 +2,24 @@