Merge branch 'master' into features/activity-form

This commit is contained in:
Jean-Francois Monfort 2021-05-20 08:20:10 +02:00
commit 890b8db5dd
134 changed files with 5267 additions and 881 deletions

3
.gitignore vendored
View File

@ -1,8 +1,8 @@
.composer/* .composer/*
composer.phar composer.phar
composer.lock composer.lock
docs/build/ docs/build/
.php_cs.cache
###> symfony/framework-bundle ### ###> symfony/framework-bundle ###
/.env.local /.env.local
@ -19,3 +19,4 @@ docs/build/
/phpunit.xml /phpunit.xml
.phpunit.result.cache .phpunit.result.cache
###< phpunit/phpunit ### ###< phpunit/phpunit ###

View File

@ -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 # 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. # See http://docs.gitlab.com/ee/ci/services/README.html for examples.
services: services:
- name: postgres:12 - name: postgis/postgis:12-3.1-alpine
alias: db alias: db
- name: redis - name: redis
alias: redis alias: redis

View File

@ -0,0 +1,453 @@
.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
.. _api:
API
###
Chill provides a basic framework to build REST api.
Configure a route
=================
Follow those steps to build a REST api:
1. Create your model;
2. Configure the API;
You can also:
* hook into the controller to customize some steps;
* add more route and steps
.. note::
Useful links:
* `How to use annotation to configure serialization <https://symfony.com/doc/current/serializer.html>`_
* `How to create your custom normalizer <https://symfony.com/doc/current/serializer/custom_normalizer.html>`_
Auto-loading the routes
***********************
Ensure that those lines are present in your file `app/config/routing.yml`:
.. code-block:: yaml
chill_cruds:
resource: 'chill_main_crud_route_loader:load'
type: service
Create your model
*****************
Create your model on the usual way:
.. code-block:: php
namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\OriginRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=OriginRepository::class)
* @ORM\Table(name="chill_person_accompanying_period_origin")
*/
class Origin
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="json")
*/
private $label;
/**
* @ORM\Column(type="date_immutable", nullable=true)
*/
private $noActiveAfter;
// .. getters and setters
}
Configure api
*************
Configure the api using Yaml (see the full configuration: :ref:`api_full_configuration`):
.. code-block:: yaml
# config/packages/chill_main.yaml
chill_main:
apis:
accompanying_period_origin:
base_path: '/api/1.0/person/accompanying-period/origin'
class: 'Chill\PersonBundle\Entity\AccompanyingPeriod\Origin'
name: accompanying_period_origin
base_role: 'ROLE_USER'
actions:
_index:
methods:
GET: true
HEAD: true
_entity:
methods:
GET: true
HEAD: true
.. note::
If you are working on a shared bundle (aka "The chill bundles"), you should define your configuration inside the class :code:`ChillXXXXBundleExtension`, using the "prependConfig" feature:
.. code-block:: php
namespace Chill\PersonBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Class ChillPersonExtension
* Loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
* @package Chill\PersonBundle\DependencyInjection
*/
class ChillPersonExtension extends Extension implements PrependExtensionInterface
{
public function prepend(ContainerBuilder $container)
{
$this->prependCruds($container);
}
/**
* @param ContainerBuilder $container
*/
protected function prependCruds(ContainerBuilder $container)
{
$container->prependExtensionConfig('chill_main', [
'apis' => [
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\Origin::class,
'name' => 'accompanying_period_origin',
'base_path' => '/api/1.0/person/accompanying-period/origin',
'controller' => \Chill\PersonBundle\Controller\OpeningApiController::class,
'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
]
],
]
]
]
]);
}
}
The :code:`_index` and :code:`_entity` action
=============================================
The :code:`_index` and :code:`_entity` action are default actions:
* they will call a specific method in the default controller;
* they will generate defined routes:
Index:
Name: :code:`chill_api_single_accompanying_period_origin__index`
Path: :code:`/api/1.0/person/accompanying-period/origin.{_format}`
Entity:
Name: :code:`chill_api_single_accompanying_period_origin__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.
For index action, the role will be called with :code:`NULL` as :code:`$subject`. The retrieved entity will be the subject for single queries.
You can also define a role for each method. In this case, this role is used for the given method, and, if any, the base role is taken into account.
.. code-block:: yaml
# config/packages/chill_main.yaml
chill_main:
apis:
accompanying_period_origin:
base_path: '/api/1.0/person/bla/bla'
class: 'Chill\PersonBundle\Entity\Blah'
name: bla
actions:
_entity:
methods:
GET: true
HEAD: true
roles:
GET: MY_ROLE_SEE
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`.
.. code-block:: php
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class OpeningApiController extends ApiController
{
protected function customizeQuery(string $action, Request $request, $qb): void
{
$qb->where($qb->expr()->gt('e.noActiveAfter', ':now'))
->orWhere($qb->expr()->isNull('e.noActiveAfter'));
$qb->setParameter('now', new \DateTime('now'));
}
}
And set your controller in configuration:
.. code-block:: yaml
chill_main:
apis:
accompanying_period_origin:
base_path: '/api/1.0/person/accompanying-period/origin'
class: 'Chill\PersonBundle\Entity\AccompanyingPeriod\Origin'
name: accompanying_period_origin
# add a controller
controller: 'Chill\PersonBundle\Controller\OpeningApiController'
base_role: 'ROLE_USER'
actions:
_index:
methods:
GET: true
HEAD: true
_entity:
methods:
GET: true
HEAD: true
Create your own actions
=======================
You can add your own actions:
.. 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:
# add a custom participation:
participation:
methods:
POST: true
DELETE: true
GET: false
HEAD: false
PUT: false
roles:
POST: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
DELETE: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
GET: null
HEAD: null
PUT: null
single-collection: single
The key :code:`single-collection` with value :code:`single` will add a :code:`/{id}/ + "action name"` (in this example, :code:`/{id}/participation`) into the path, after the base path. If the value is :code:`collection`, no id will be set, but the action name will be append to the path.
Then, create the corresponding action into your controller:
.. 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\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;
class AccompanyingCourseApiController extends ApiController
{
protected EventDispatcherInterface $eventDispatcher;
protected ValidatorInterface $validator;
public function __construct(EventDispatcherInterface $eventDispatcher, $validator)
{
$this->eventDispatcher = $eventDispatcher;
$this->validator = $validator;
}
public function participationApi($id, Request $request, $_format)
{
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = $this->getEntity('participation', $id, $request);
$person = $this->getSerializer()
->deserialize($request->getContent(), Person::class, $_format, []);
if (NULL === $person) {
throw new BadRequestException('person id not found');
}
$this->onPostCheckACL('participation', $request, $accompanyingPeriod, $_format);
switch ($request->getMethod()) {
case Request::METHOD_POST:
$participation = $accompanyingPeriod->addPerson($person);
break;
case Request::METHOD_DELETE:
$participation = $accompanyingPeriod->removePerson($person);
break;
default:
throw new BadRequestException("This method is not supported");
}
$errors = $this->validator->validate($accompanyingPeriod);
if ($errors->count() > 0) {
// only format accepted
return $this->json($errors);
}
$this->getDoctrine()->getManager()->flush();
return $this->json($participation);
}
}
Serialization for collection
============================
A specific model has been defined for returning collection:
.. code-block:: json
{
"count": 49,
"results": [
],
"pagination": {
"more": true,
"next": "/api/1.0/search.json&q=xxxx......&page=2",
"previous": null,
"first": 0,
"items_per_page": 1
}
}
This can be achieved quickly by assembling results into a :code:`Chill\MainBundle\Serializer\Model\Collection`. The pagination information is given by using :code:`Paginator` (see :ref:`Pagination <pagination-ref>`).
.. code-block:: php
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Chill\MainBundle\Pagination\PaginatorInterface;
class MyController extends AbstractController
{
protected function serializeCollection(PaginatorInterface $paginator, $entities): Response
{
$model = new Collection($entities, $paginator);
return $this->json($model, Response::HTTP_OK, [], $context);
}
}
.. _api_full_configuration:
Full configuration example
==========================
.. code-block:: yaml
apis:
-
class: Chill\PersonBundle\Entity\AccompanyingPeriod
name: accompanying_course
base_path: /api/1.0/person/accompanying-course
controller: Chill\PersonBundle\Controller\AccompanyingCourseApiController
actions:
_entity:
roles:
GET: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
HEAD: null
POST: null
DELETE: null
PUT: null
controller_action: null
path: null
single-collection: single
methods:
GET: true
HEAD: true
POST: false
DELETE: false
PUT: false
participation:
methods:
POST: true
DELETE: true
GET: false
HEAD: false
PUT: false
roles:
POST: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
DELETE: CHILL_PERSON_ACCOMPANYING_PERIOD_SEE
GET: null
HEAD: null
PUT: null
controller_action: null
# the requirements for the route. Will be set to `[ 'id' => '\d+' ]` if left empty.
requirements: []
path: null
single-collection: single
base_role: null

View File

@ -16,6 +16,7 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
Instructions to create a new bundle <create-a-new-bundle.rst> Instructions to create a new bundle <create-a-new-bundle.rst>
CRUD (Create - Update - Delete) for one entity <crud.rst> CRUD (Create - Update - Delete) for one entity <crud.rst>
Helpers for building a REST API <api.rst>
Routing <routing.rst> Routing <routing.rst>
Menus <menus.rst> Menus <menus.rst>
Forms <forms.rst> Forms <forms.rst>

View File

@ -7,6 +7,8 @@
Free Documentation License". Free Documentation License".
.. _pagination-ref:
Pagination Pagination
########## ##########
@ -15,7 +17,7 @@ The Bundle :code:`Chill\MainBundle` provides a **Pagination** api which allow yo
A simple example A simple example
**************** ****************
In the controller, get the :class:`Chill\Main\Pagination\PaginatorFactory` from the `Container` and use this :code:`PaginatorFactory` to create a :code:`Paginator` instance. In the controller, get the :code:`Chill\Main\Pagination\PaginatorFactory` from the `Container` and use this :code:`PaginatorFactory` to create a :code:`Paginator` instance.
.. literalinclude:: pagination/example.php .. literalinclude:: pagination/example.php

View File

@ -0,0 +1,77 @@
<?php
/*
* Copyright (C) 2018 Champs Libres Cooperative <info@champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Chill\MainBundle\CRUD\CompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Chill\MainBundle\Routing\MenuComposer;
use Symfony\Component\DependencyInjection\Definition;
/**
*
*
*/
class CRUDControllerCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$crudConfig = $container->getParameter('chill_main_crud_route_loader_config');
$apiConfig = $container->getParameter('chill_main_api_route_loader_config');
foreach ($crudConfig as $crudEntry) {
$this->configureCrudController($container, $crudEntry, 'crud');
}
foreach ($apiConfig as $crudEntry) {
$this->configureCrudController($container, $crudEntry, 'api');
}
}
/**
* Add a controller for each definition, and add a methodCall to inject crud configuration to controller
*/
private function configureCrudController(ContainerBuilder $container, array $crudEntry, string $apiOrCrud): void
{
$controllerClass = $crudEntry['controller'];
$controllerServiceName = 'cs'.$apiOrCrud.'_'.$crudEntry['name'].'_controller';
if ($container->hasDefinition($controllerClass)) {
$controller = $container->getDefinition($controllerClass);
$container->removeDefinition($controllerClass);
$alreadyDefined = true;
} else {
$controller = new Definition($controllerClass);
$alreadyDefined = false;
}
$controller->addTag('controller.service_arguments');
if (FALSE === $alreadyDefined) {
$controller->setAutoconfigured(true);
$controller->setPublic(true);
}
$param = 'chill_main_'.$apiOrCrud.'_config_'.$crudEntry['name'];
$container->setParameter($param, $crudEntry);
$controller->addMethodCall('setCrudConfig', ['%'.$param.'%']);
$container->setDefinition($controllerServiceName, $controller);
}
}

View File

@ -0,0 +1,225 @@
<?php
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 Chill\MainBundle\Pagination\PaginatorFactory;
use Chill\MainBundle\Pagination\PaginatorInterface;
class AbstractCRUDController extends AbstractController
{
/**
* The crud configuration
*
* This configuration si defined by `chill_main['crud']` or `chill_main['apis']`
*
* @var array
*/
protected array $crudConfig = [];
/**
* get the instance of the entity with the given id
*
* @param string $id
* @return object
*/
protected function getEntity($action, $id, Request $request): ?object
{
return $this->getDoctrine()
->getRepository($this->getEntityClass())
->find($id);
}
/**
* Count the number of entities
*
* By default, count all entities. You can customize the query by
* using the method `customizeQuery`.
*
* @param string $action
* @param Request $request
* @return int
*/
protected function countEntities(string $action, Request $request, $_format): int
{
return $this->buildQueryEntities($action, $request)
->select('COUNT(e)')
->getQuery()
->getSingleScalarResult()
;
}
/**
* Query the entity.
*
* By default, get all entities. You can customize the query by using the
* method `customizeQuery`.
*
* The method `orderEntity` is called internally to order entities.
*
* It returns, by default, a query builder.
*
*/
protected function queryEntities(string $action, Request $request, string $_format, PaginatorInterface $paginator)
{
$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, $_format);
}
/**
* Add ordering fields in the query build by self::queryEntities
*
*/
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator, $_format)
{
return $query;
}
/**
* Build the base query for listing all entities.
*
* This method is used internally by `countEntities` `queryEntities`
*
* This base query does not contains any `WHERE` or `SELECT` clauses. You
* can add some by using the method `customizeQuery`.
*
* The alias for the entity is "e".
*
* @param string $action
* @param Request $request
* @return QueryBuilder
*/
protected function buildQueryEntities(string $action, Request $request)
{
$qb = $this->getDoctrine()->getManager()
->createQueryBuilder()
->select('e')
->from($this->getEntityClass(), 'e')
;
$this->customizeQuery($action, $request, $qb);
return $qb;
}
protected function customizeQuery(string $action, Request $request, $query): void {}
/**
* Get the result of the query
*/
protected function getQueryResult(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query)
{
return $query->getQuery()->getResult();
}
protected function onPreIndex(string $action, Request $request, string $_format): ?Response
{
return null;
}
/**
* method used by indexAction
*/
protected function onPreIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator): ?Response
{
return null;
}
/**
* method used by indexAction
*/
protected function onPostIndexBuildQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $query): ?Response
{
return null;
}
/**
* method used by indexAction
*/
protected function onPostIndexFetchQuery(string $action, Request $request, string $_format, int $totalItems, PaginatorInterface $paginator, $entities): ?Response
{
return null;
}
/**
* Get the complete FQDN of the class
*
* @return string the complete fqdn of the class
*/
protected function getEntityClass(): string
{
return $this->crudConfig['class'];
}
/**
* called on post fetch entity
*/
protected function onPostFetchEntity(string $action, Request $request, $entity, $_format): ?Response
{
return null;
}
/**
* Called on post check ACL
*/
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
{
return null;
}
/**
* check the acl. Called by every action.
*
* 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.
*
* @throws \Symfony\Component\Security\Core\Exception\AccessDeniedHttpException
*/
protected function checkACL(string $action, Request $request, string $_format, $entity = null)
{
$this->denyAccessUnlessGranted($this->getRoleFor($action, $request, $entity, $_format), $entity);
}
/**
*
* @return string the crud name
*/
protected function getCrudName(): string
{
return $this->crudConfig['name'];
}
protected function getActionConfig(string $action)
{
return $this->crudConfig['actions'][$action];
}
/**
* Set the crud configuration
*
* Used by the container to inject configuration for this crud.
*/
public function setCrudConfig(array $config): void
{
$this->crudConfig = $config;
}
/**
* @return PaginatorFactory
*/
protected function getPaginatorFactory(): PaginatorFactory
{
return $this->container->get('chill_main.paginator_factory');
}
}

View File

@ -0,0 +1,220 @@
<?php
namespace Chill\MainBundle\CRUD\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Chill\MainBundle\Serializer\Model\Collection;
use Chill\MainBundle\Pagination\PaginatorInterface;
class ApiController extends AbstractCRUDController
{
/**
* 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,
* this response is returned ;
* 5. Serialize the entity and return the result. The serialization context is given by `getSerializationContext`
*
*/
protected function entityGet(string $action, Request $request, $id, $_format = 'html'): 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;
}
if ($_format === 'json') {
$context = $this->getContextForSerialization($action, $request, $_format, $entity);
return $this->json($entity, Response::HTTP_OK, [], $context);
} else {
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This format is not implemented");
}
}
public function onBeforeSerialize(string $action, Request $request, $_format, $entity): ?Response
{
return null;
}
/**
* Base method for handling api action
*
* @return void
*/
public function entityApi(Request $request, $id, $_format): Response
{
switch ($request->getMethod()) {
case Request::METHOD_GET:
case REQUEST::METHOD_HEAD:
return $this->entityGet('_entity', $request, $id, $_format);
default:
throw new \Symfony\Component\HttpFoundation\Exception\BadRequestException("This method is not implemented");
}
}
/**
* Base action for indexing entities
*/
public function indexApi(Request $request, string $_format)
{
switch ($request->getMethod()) {
case Request::METHOD_GET:
case REQUEST::METHOD_HEAD:
return $this->indexApiAction('_index', $request, $_format);
default:
throw $this->createNotFoundException("This method is not supported");
}
}
/**
* 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
* 1. count the items, using `countEntities`
* 2. build a paginator element from the the number of entities ;
* 3. Launch `onPreIndexQuery`. If it does return a response instance, return it
* 3. build a query, using `queryEntities`
* x. fetch the results, using `getQueryResult`
* x. Launch `onPostIndexFetchQuery`. If it does return a response instance, return it
* 4. Serialize the entities in a Collection, using `SerializeCollection`
*
* @param string $action
* @param Request $request
* @return type
*/
protected function indexApiAction($action, Request $request, $_format)
{
$this->onPreIndex($action, $request, $_format);
$response = $this->checkACL($action, $request, $_format);
if ($response instanceof Response) {
return $response;
}
if (!isset($entity)) {
$entity = '';
}
$response = $this->onPostCheckACL($action, $request, $_format, $entity);
if ($response instanceof Response) {
return $response;
}
$totalItems = $this->countEntities($action, $request, $_format);
$paginator = $this->getPaginatorFactory()->create($totalItems);
$response = $this->onPreIndexBuildQuery($action, $request, $_format, $totalItems,
$paginator);
if ($response instanceof Response) {
return $response;
}
$query = $this->queryEntities($action, $request, $_format, $paginator);
$response = $this->onPostIndexBuildQuery($action, $request, $_format, $totalItems,
$paginator, $query);
if ($response instanceof Response) {
return $response;
}
$entities = $this->getQueryResult($action, $request, $_format, $totalItems, $paginator, $query);
$response = $this->onPostIndexFetchQuery($action, $request, $_format, $totalItems,
$paginator, $entities);
if ($response instanceof Response) {
return $response;
}
return $this->serializeCollection($action, $request, $_format, $paginator, $entities);
}
/**
* Serialize collections
*
*/
protected function serializeCollection(string $action, Request $request, string $_format, PaginatorInterface $paginator, $entities): Response
{
$model = new Collection($entities, $paginator);
$context = $this->getContextForSerialization($action, $request, $_format, $entities);
return $this->json($model, Response::HTTP_OK, [], $context);
}
protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array
{
return [];
}
/**
* get the role given from the config.
*/
protected function getRoleFor(string $action, Request $request, $entity, $_format): string
{
$actionConfig = $this->getActionConfig($action);
if (NULL !== $actionConfig['roles'][$request->getMethod()]) {
return $actionConfig['roles'][$request->getMethod()];
}
if ($this->crudConfig['base_role']) {
return $this->crudConfig['base_role'];
}
throw new \RuntimeException(sprintf("the config does not have any role for the ".
"method %s nor a global role for the whole action. Add those to your ".
"configuration or override the required method", $request->getMethod()));
}
protected function getSerializer(): SerializerInterface
{
return $this->get('serializer');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,9 @@ namespace Chill\MainBundle\CRUD\Routing;
use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\HttpFoundation\Request;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\CRUD\Controller\CRUDController;
/** /**
* Class CRUDRoutesLoader * Class CRUDRoutesLoader
@ -32,24 +35,34 @@ use Symfony\Component\Routing\RouteCollection;
*/ */
class CRUDRoutesLoader extends Loader class CRUDRoutesLoader extends Loader
{ {
/** protected array $crudConfig = [];
* @var array
*/ protected array $apiCrudConfig = [];
protected $config = [];
/** /**
* @var bool * @var bool
*/ */
private $isLoaded = false; private $isLoaded = false;
private const ALL_SINGLE_METHODS = [
Request::METHOD_GET,
Request::METHOD_POST,
Request::METHOD_PUT,
Request::METHOD_DELETE
];
private const ALL_INDEX_METHODS = [ Request::METHOD_GET, Request::METHOD_HEAD ];
/** /**
* CRUDRoutesLoader constructor. * CRUDRoutesLoader constructor.
* *
* @param $config * @param $crudConfig the config from cruds
* @param $apicrudConfig the config from api_crud
*/ */
public function __construct($config) public function __construct(array $crudConfig, array $apiConfig)
{ {
$this->config = $config; $this->crudConfig = $crudConfig;
$this->apiConfig = $apiConfig;
} }
/** /**
@ -63,53 +76,161 @@ class CRUDRoutesLoader extends Loader
} }
/** /**
* @return RouteCollection * Load routes for CRUD and CRUD Api
*/ */
public function load($resource, $type = null) public function load($resource, $type = null): RouteCollection
{ {
if (true === $this->isLoaded) { if (true === $this->isLoaded) {
throw new \RuntimeException('Do not add the "CRUD" loader twice'); throw new \RuntimeException('Do not add the "CRUD" loader twice');
} }
$collection = new RouteCollection(); $collection = new RouteCollection();
foreach ($this->config as $config) { foreach ($this->crudConfig as $crudConfig) {
$collection->addCollection($this->loadConfig($config)); $collection->addCollection($this->loadCrudConfig($crudConfig));
} }
foreach ($this->apiConfig as $crudConfig) {
$collection->addCollection($this->loadApi($crudConfig));
}
return $collection; return $collection;
} }
/** /**
* @param $config * Load routes for CRUD (without api)
*
* @param $crudConfig
* @return RouteCollection * @return RouteCollection
*/ */
protected function loadConfig($config): RouteCollection protected function loadCrudConfig($crudConfig): RouteCollection
{ {
$collection = new RouteCollection(); $collection = new RouteCollection();
foreach ($config['actions'] as $name => $action) { $controller ='cscrud_'.$crudConfig['name'].'_controller';
foreach ($crudConfig['actions'] as $name => $action) {
// defaults (controller name)
$defaults = [ $defaults = [
'_controller' => 'cscrud_'.$config['name'].'_controller'.':'.($action['controller_action'] ?? $name) '_controller' => $controller.':'.($action['controller_action'] ?? $name)
]; ];
if ($name === 'index') { if ($name === 'index') {
$path = "{_locale}".$config['base_path']; $path = "{_locale}".$crudConfig['base_path'];
$route = new Route($path, $defaults); $route = new Route($path, $defaults);
} elseif ($name === 'new') { } elseif ($name === 'new') {
$path = "{_locale}".$config['base_path'].'/'.$name; $path = "{_locale}".$crudConfig['base_path'].'/'.$name;
$route = new Route($path, $defaults); $route = new Route($path, $defaults);
} else { } else {
$path = "{_locale}".$config['base_path'].($action['path'] ?? '/{id}/'.$name); $path = "{_locale}".$crudConfig['base_path'].($action['path'] ?? '/{id}/'.$name);
$requirements = $action['requirements'] ?? [ $requirements = $action['requirements'] ?? [
'{id}' => '\d+' '{id}' => '\d+'
]; ];
$route = new Route($path, $defaults, $requirements); $route = new Route($path, $defaults, $requirements);
} }
$collection->add('chill_crud_'.$config['name'].'_'.$name, $route); $collection->add('chill_crud_'.$crudConfig['name'].'_'.$name, $route);
} }
return $collection; return $collection;
} }
/**
* Load routes for api single
*
* @param $crudConfig
* @return RouteCollection
*/
protected function loadApi(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 === '_entity' ? 'single' : NULL;
if ('collection' === $singleCollection) {
// continue;
}
// compute default action
switch ($name) {
case '_entity':
$controllerAction = 'entityApi';
break;
case '_index':
$controllerAction = 'indexApi';
break;
default:
$controllerAction = $name.'Api';
break;
}
$defaults = [
'_controller' => $controller.':'.($action['controller_action'] ?? $controllerAction)
];
// path are rewritten
// if name === 'default', we rewrite it to nothing :-)
$localName = \in_array($name, [ '_entity', '_index' ]) ? '' : '/'.$name;
if ('collection' === $action['single-collection'] || '_index' === $name) {
$localPath = $action['path'] ?? $localName.'.{_format}';
} else {
$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);
$collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
}
return $collection;
}
/**
* 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) {
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);
$collection->add('chill_api_single_'.$crudConfig['name'].'_'.$name, $route);
}
return $collection;
}
} }

View File

@ -14,6 +14,7 @@ use Chill\MainBundle\DependencyInjection\CompilerPass\NotificationCounterCompile
use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\MenuCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\ACLFlagsCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\ACLFlagsCompilerPass;
use Chill\MainBundle\DependencyInjection\CompilerPass\GroupingCenterCompilerPass; use Chill\MainBundle\DependencyInjection\CompilerPass\GroupingCenterCompilerPass;
use Chill\MainBundle\CRUD\CompilerPass\CRUDControllerCompilerPass;
use Chill\MainBundle\Templating\Entity\CompilerPass as RenderEntityCompilerPass; use Chill\MainBundle\Templating\Entity\CompilerPass as RenderEntityCompilerPass;
@ -33,5 +34,6 @@ class ChillMainBundle extends Bundle
$container->addCompilerPass(new ACLFlagsCompilerPass()); $container->addCompilerPass(new ACLFlagsCompilerPass());
$container->addCompilerPass(new GroupingCenterCompilerPass()); $container->addCompilerPass(new GroupingCenterCompilerPass());
$container->addCompilerPass(new RenderEntityCompilerPass()); $container->addCompilerPass(new RenderEntityCompilerPass());
$container->addCompilerPass(new CRUDControllerCompilerPass());
} }
} }

View File

@ -0,0 +1,63 @@
<?php
namespace Chill\MainBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\Routing\Annotation\Route;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\AddressReference;
/**
* Class AddressController
*
* @package Chill\MainBundle\Controller
*/
class AddressController extends AbstractController
{
/**
* Get API Data for showing endpoint
*
* @Route(
* "/{_locale}/main/api/1.0/address/{address_id}/show.{_format}",
* name="chill_main_address_api_show"
* )
* @ParamConverter("address", options={"id": "address_id"})
*/
public function showAddress(Address $address, $_format): Response
{
// TODO check ACL ?
switch ($_format) {
case 'json':
return $this->json($address);
default:
throw new BadRequestException('Unsupported format');
}
}
/**
* Get API Data for showing endpoint
*
* @Route(
* "/{_locale}/main/api/1.0/address-reference/{address_reference_id}/show.{_format}",
* name="chill_main_address_reference_api_show"
* )
* @ParamConverter("addressReference", options={"id": "address_reference_id"})
*/
public function showAddressReference(AddressReference $addressReference, $_format): Response
{
// TODO check ACL ?
switch ($_format) {
case 'json':
return $this->json($addressReference);
default:
throw new BadRequestException('Unsupported format');
}
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace Chill\MainBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
use Chill\MainBundle\Entity\AddressReference;
use Chill\MainBundle\Doctrine\Model\Point;
/**
* Load reference addresses into database
*
* @author Champs Libres
*/
class LoadAddressReferences extends AbstractFixture implements ContainerAwareInterface, OrderedFixtureInterface {
protected $faker;
public function __construct()
{
$this->faker = \Faker\Factory::create('fr_FR');
}
/**
*
* @var ContainerInterface
*/
private $container;
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
public function getOrder() {
return 51;
}
/**
* Create a random point
*
* @return Point
*/
private function getRandomPoint()
{
$lonBrussels = 4.35243;
$latBrussels = 50.84676;
$lon = $lonBrussels + 0.01 * rand(-5, 5);
$lat = $latBrussels + 0.01 * rand(-5, 5);
return Point::fromLonLat($lon, $lat);
}
/**
* Create a random reference address
*
* @return AddressReference
*/
private function getRandomAddressReference()
{
$ar= new AddressReference();
$ar->setRefId($this->faker->numerify('ref-id-######'));
$ar->setStreet($this->faker->streetName);
$ar->setStreetNumber(rand(0,199));
$ar ->setPoint($this->getRandomPoint());
$ar->setPostcode($this->getReference(
LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)]
));
$ar->setMunicipalityCode($ar->getPostcode()->getCode());
return $ar
;
}
public function load(ObjectManager $manager) {
echo "loading some reference address... \n";
for ($i=0; $i<10; $i++) {
$ar = $this->getRandomAddressReference();
$manager->persist($ar);
}
$manager->flush();
}
}

View File

@ -133,7 +133,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
$loader->load('services/search.yaml'); $loader->load('services/search.yaml');
$loader->load('services/serializer.yaml'); $loader->load('services/serializer.yaml');
$this->configureCruds($container, $config['cruds'], $loader); $this->configureCruds($container, $config['cruds'], $config['apis'], $loader);
} }
/** /**
@ -188,7 +188,12 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
$container->prependExtensionConfig('doctrine', array( $container->prependExtensionConfig('doctrine', array(
'dbal' => [ 'dbal' => [
'types' => [ 'types' => [
'dateinterval' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class 'dateinterval' => [
'class' => \Chill\MainBundle\Doctrine\Type\NativeDateIntervalType::class
],
'point' => [
'class' => \Chill\MainBundle\Doctrine\Type\PointType::class
]
] ]
] ]
)); ));
@ -210,51 +215,24 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface,
} }
/** /**
* @param ContainerBuilder $container * Load parameter for configuration and set parameters for api
* @param array $config the config under 'cruds' key
* @return null
*/ */
protected function configureCruds(ContainerBuilder $container, $config, Loader\YamlFileLoader $loader) protected function configureCruds(
ContainerBuilder $container,
array $crudConfig,
array $apiConfig,
Loader\YamlFileLoader $loader
): void
{ {
if (count($config) === 0) { if (count($crudConfig) === 0) {
return; return;
} }
$loader->load('services/crud.yaml'); $loader->load('services/crud.yaml');
$container->setParameter('chill_main_crud_route_loader_config', $config); $container->setParameter('chill_main_crud_route_loader_config', $crudConfig);
$container->setParameter('chill_main_api_route_loader_config', $apiConfig);
$definition = new Definition(); // Note: the controller are loaded inside compiler pass
$definition
->setClass(\Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader::class)
->addArgument('%chill_main_crud_route_loader_config%')
;
$container->setDefinition('chill_main_crud_route_loader', $definition);
$alreadyExistingNames = [];
foreach ($config as $crudEntry) {
$controller = $crudEntry['controller'];
$controllerServiceName = 'cscrud_'.$crudEntry['name'].'_controller';
$name = $crudEntry['name'];
// check for existing crud names
if (\in_array($name, $alreadyExistingNames)) {
throw new LogicException(sprintf("the name %s is defined twice in CRUD", $name));
}
if (!$container->has($controllerServiceName)) {
$controllerDefinition = new Definition($controller);
$controllerDefinition->addTag('controller.service_arguments');
$controllerDefinition->setAutoconfigured(true);
$controllerDefinition->setClass($crudEntry['controller']);
$container->setDefinition($controllerServiceName, $controllerDefinition);
}
$container->setParameter('chill_main_crud_config_'.$name, $crudEntry);
$container->getDefinition($controllerServiceName)
->addMethodCall('setCrudConfig', ['%chill_main_crud_config_'.$name.'%']);
}
} }
} }

View File

@ -8,6 +8,7 @@ use Chill\MainBundle\DependencyInjection\Widget\Factory\WidgetFactoryInterface;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait; use Chill\MainBundle\DependencyInjection\Widget\AddWidgetConfigurationTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Request;
/** /**
@ -140,7 +141,7 @@ class Configuration implements ConfigurationInterface
->scalarNode('controller_action') ->scalarNode('controller_action')
->defaultNull() ->defaultNull()
->info('the method name to call in the route. Will be set to the action name if left empty.') ->info('the method name to call in the route. Will be set to the action name if left empty.')
->example("'action'") ->example("action")
->end() ->end()
->scalarNode('path') ->scalarNode('path')
->defaultNull() ->defaultNull()
@ -168,6 +169,78 @@ class Configuration implements ConfigurationInterface
->end() ->end()
->end() ->end()
->arrayNode('apis')
->defaultValue([])
->arrayPrototype()
->children()
->scalarNode('class')->cannotBeEmpty()->isRequired()->end()
->scalarNode('controller')
->cannotBeEmpty()
->defaultValue(\Chill\MainBundle\CRUD\Controller\ApiController::class)
->end()
->scalarNode('name')->cannotBeEmpty()->isRequired()->end()
->scalarNode('base_path')->cannotBeEmpty()->isRequired()->end()
->scalarNode('base_role')->defaultNull()->end()
->arrayNode('actions')
->useAttributeAsKey('name')
->arrayPrototype()
->children()
->scalarNode('controller_action')
->defaultNull()
->info('the method name to call in the controller. Will be set to the concatenation '.
'of action name + \'Api\' if left empty.')
->example("showApi")
->end()
->scalarNode('path')
->defaultNull()
->info('the path that will be **appended** after the base path. Do not forget to add ' .
'arguments for the method. By default, will set to the action name, including an `{id}` '.
'parameter. A suffix of action name will be appended, except if the action name '.
'is "_entity".')
->example('/{id}/my-action')
->end()
->arrayNode('requirements')
->ignoreExtraKeys(false)
->info('the requirements for the route. Will be set to `[ \'id\' => \'\d+\' ]` if left empty.')
->end()
->enumNode('single-collection')
->values(['single', 'collection'])
->defaultValue('single')
->info('indicates if the returned object is a single element or a collection. '.
'If the action name is `_index`, this value will always be considered as '.
'`collection`')
->end()
->arrayNode('methods')
->addDefaultsIfNotSet()
->info('the allowed methods')
->children()
->booleanNode(Request::METHOD_GET)->defaultTrue()->end()
->booleanNode(Request::METHOD_HEAD)->defaultTrue()->end()
->booleanNode(Request::METHOD_POST)->defaultFalse()->end()
->booleanNode(Request::METHOD_DELETE)->defaultFalse()->end()
->booleanNode(Request::METHOD_PUT)->defaultFalse()->end()
->end()
->end()
->arrayNode('roles')
->addDefaultsIfNotSet()
->info("The role require for each http method")
->children()
->scalarNode(Request::METHOD_GET)->defaultNull()->end()
->scalarNode(Request::METHOD_HEAD)->defaultNull()->end()
->scalarNode(Request::METHOD_POST)->defaultNull()->end()
->scalarNode(Request::METHOD_DELETE)->defaultNull()->end()
->scalarNode(Request::METHOD_PUT)->defaultNull()->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end() // end of root/children ->end() // end of root/children
->end() // end of root ->end() // end of root
; ;

View File

@ -0,0 +1,103 @@
<?php
namespace Chill\MainBundle\Doctrine\Model;
use \JsonSerializable;
/**
* Description of Point
*
*/
class Point implements JsonSerializable {
private float $lat;
private float $lon;
public static string $SRID = '4326';
private function __construct(float $lon, float $lat)
{
$this->lat = $lat;
$this->lon = $lon;
}
public function toGeoJson(): string
{
$array = $this->toArrayGeoJson();
return \json_encode($array);
}
public function jsonSerialize(): array
{
return $this->toArrayGeoJson();
}
public function toArrayGeoJson(): array
{
return [
"type" => "Point",
"coordinates" => [ $this->lon, $this->lat ]
];
}
/**
*
* @return string
*/
public function toWKT(): string
{
return 'SRID='.self::$SRID.';POINT('.$this->lon.' '.$this->lat.')';
}
/**
*
* @param type $geojson
* @return Point
*/
public static function fromGeoJson(string $geojson): Point
{
$a = json_decode($geojson);
//check if the geojson string is correct
if (NULL === $a or !isset($a->type) or !isset($a->coordinates)){
throw PointException::badJsonString($geojson);
}
if ($a->type != 'Point'){
throw PointException::badGeoType();
}
$lat = $a->coordinates[1];
$lon = $a->coordinates[0];
return Point::fromLonLat($lon, $lat);
}
public static function fromLonLat(float $lon, float $lat): Point
{
if (($lon > -180 && $lon < 180) && ($lat > -90 && $lat < 90))
{
return new Point($lon, $lat);
} else {
throw PointException::badCoordinates($lon, $lat);
}
}
public static function fromArrayGeoJson(array $array): Point
{
if ($array['type'] == 'Point' &&
isset($array['coordinates']))
{
return self::fromLonLat($array['coordinates'][0], $array['coordinates'][1]);
}
}
public function getLat(): float
{
return $this->lat;
}
public function getLon(): float
{
return $this->lon;
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Chill\MainBundle\Doctrine\Model;
use \Exception;
/**
* Description of PointException
*
*/
class PointException extends Exception {
public static function badCoordinates($lon, $lat): self
{
return new self("Input coordinates are not valid in the used coordinate system (longitude = $lon , latitude = $lat)");
}
public static function badJsonString($str): self
{
return new self("The JSON string is not valid: $str");
}
public static function badGeoType(): self
{
return new self("The geoJSON object type is not valid");
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace Chill\MainBundle\Doctrine\Type;
use Chill\MainBundle\Doctrine\Model\Point;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Chill\MainBundle\Doctrine\Model\PointException;
/**
* A Type for Doctrine to implement the Geography Point type
* implemented by Postgis on postgis+postgresql databases
*
*/
class PointType extends Type {
const POINT = 'point';
/**
*
* @param array $fieldDeclaration
* @param AbstractPlatform $platform
* @return type
*/
public function getSqlDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return 'geometry(POINT,'.Point::$SRID.')';
}
/**
*
* @param type $value
* @param AbstractPlatform $platform
* @return Point
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
if ($value === NULL){
return NULL;
} else {
return Point::fromGeoJson($value);
}
}
public function getName()
{
return self::POINT;
}
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === NULL){
return NULL;
} else {
return $value->toWKT();
}
}
public function canRequireSQLConversion()
{
return true;
}
public function convertToPHPValueSQL($sqlExpr, $platform)
{
return 'ST_AsGeoJSON('.$sqlExpr.') ';
}
public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform)
{
return $sqlExpr;
}
}

View File

@ -4,6 +4,8 @@ namespace Chill\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Chill\MainBundle\Doctrine\Model\Point;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
/** /**
* Address * Address
@ -28,14 +30,14 @@ class Address
* *
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $streetAddress1 = ''; private $street = '';
/** /**
* @var string * @var string
* *
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $streetAddress2 = ''; private $streetNumber = '';
/** /**
* @var PostalCode * @var PostalCode
@ -43,7 +45,56 @@ class Address
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\PostalCode") * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\PostalCode")
*/ */
private $postcode; private $postcode;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $floor;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $corridor;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $steps;
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $buildingName;
/**
* @var string|null
*
* @ORM\Column(type="string", length=16, nullable=true)
*/
private $flat;
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $distribution;
/**
* @var string|null
*
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $extra;
/** /**
* Indicates when the address starts validation. Used to build an history * Indicates when the address starts validation. Used to build an history
* of address. By default, the current date. * of address. By default, the current date.
@ -53,27 +104,55 @@ class Address
* @ORM\Column(type="date") * @ORM\Column(type="date")
*/ */
private $validFrom; private $validFrom;
/**
* Indicates when the address ends. Used to build an history
* of address.
*
* @var \DateTime|null
*
* @ORM\Column(type="date", nullable=true)
*/
private $validTo;
/** /**
* True if the address is a "no address", aka homeless person, ... * True if the address is a "no address", aka homeless person, ...
* *
* @var bool * @var bool
*/ */
private $isNoAddress = false; private $isNoAddress = false;
/**
* A geospatial field storing the coordinates of the Address
*
* @var Point|null
*
* @ORM\Column(type="point", nullable=true)
*/
private $point;
/**
* A ThirdParty reference for person's addresses that are linked to a third party
*
* @var ThirdParty|null
*
* @ORM\ManyToOne(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty")
* @ORM\JoinColumn(nullable=true)
*/
private $linkedToThirdParty;
/** /**
* A list of metadata, added by customizable fields * A list of metadata, added by customizable fields
* *
* @var array * @var array
*/ */
private $customs = []; private $customs = [];
public function __construct() public function __construct()
{ {
$this->validFrom = new \DateTime(); $this->validFrom = new \DateTime();
} }
/** /**
* Get id * Get id
* *
@ -85,7 +164,7 @@ class Address
} }
/** /**
* Set streetAddress1 * Set streetAddress1 (legacy function)
* *
* @param string $streetAddress1 * @param string $streetAddress1
* *
@ -93,23 +172,23 @@ class Address
*/ */
public function setStreetAddress1($streetAddress1) public function setStreetAddress1($streetAddress1)
{ {
$this->streetAddress1 = $streetAddress1 === NULL ? '' : $streetAddress1; $this->street = $streetAddress1 === NULL ? '' : $streetAddress1;
return $this; return $this;
} }
/** /**
* Get streetAddress1 * Get streetAddress1 (legacy function)
* *
* @return string * @return string
*/ */
public function getStreetAddress1() public function getStreetAddress1()
{ {
return $this->streetAddress1; return $this->street;
} }
/** /**
* Set streetAddress2 * Set streetAddress2 (legacy function)
* *
* @param string $streetAddress2 * @param string $streetAddress2
* *
@ -117,19 +196,19 @@ class Address
*/ */
public function setStreetAddress2($streetAddress2) public function setStreetAddress2($streetAddress2)
{ {
$this->streetAddress2 = $streetAddress2 === NULL ? '' : $streetAddress2; $this->streetNumber = $streetAddress2 === NULL ? '' : $streetAddress2;
return $this; return $this;
} }
/** /**
* Get streetAddress2 * Get streetAddress2 (legacy function)
* *
* @return string * @return string
*/ */
public function getStreetAddress2() public function getStreetAddress2()
{ {
return $this->streetAddress2; return $this->streetNumber;
} }
/** /**
@ -155,7 +234,7 @@ class Address
{ {
return $this->postcode; return $this->postcode;
} }
/** /**
* @return \DateTime * @return \DateTime
*/ */
@ -173,19 +252,19 @@ class Address
$this->validFrom = $validFrom; $this->validFrom = $validFrom;
return $this; return $this;
} }
/** /**
* Get IsNoAddress * Get IsNoAddress
* *
* Indicate true if the address is a fake address (homeless, ...) * Indicate true if the address is a fake address (homeless, ...)
* *
* @return bool * @return bool
*/ */
public function getIsNoAddress(): bool public function getIsNoAddress(): bool
{ {
return $this->isNoAddress; return $this->isNoAddress;
} }
/** /**
* @return bool * @return bool
*/ */
@ -196,9 +275,9 @@ class Address
/** /**
* Set IsNoAddress * Set IsNoAddress
* *
* Indicate true if the address is a fake address (homeless, ...) * Indicate true if the address is a fake address (homeless, ...)
* *
* @param bool $isNoAddress * @param bool $isNoAddress
* @return $this * @return $this
*/ */
@ -207,10 +286,10 @@ class Address
$this->isNoAddress = $isNoAddress; $this->isNoAddress = $isNoAddress;
return $this; return $this;
} }
/** /**
* Get customs informations in the address * Get customs informations in the address
* *
* @return array * @return array
*/ */
public function getCustoms(): array public function getCustoms(): array
@ -220,27 +299,27 @@ class Address
/** /**
* Store custom informations in the address * Store custom informations in the address
* *
* @param array $customs * @param array $customs
* @return $this * @return $this
*/ */
public function setCustoms(array $customs): self public function setCustoms(array $customs): self
{ {
$this->customs = $customs; $this->customs = $customs;
return $this; return $this;
} }
/** /**
* Validate the address. * Validate the address.
* *
* Check that: * Check that:
* *
* * if the address is not home address: * * if the address is not home address:
* * the postal code is present * * the postal code is present
* * the valid from is not null * * the valid from is not null
* * the address street 1 is greater than 2 * * the address street 1 is greater than 2
* *
* @param ExecutionContextInterface $context * @param ExecutionContextInterface $context
* @param array $payload * @param array $payload
*/ */
@ -252,18 +331,18 @@ class Address
->atPath('validFrom') ->atPath('validFrom')
->addViolation(); ->addViolation();
} }
if ($this->isNoAddress()) { if ($this->isNoAddress()) {
return; return;
} }
if (empty($this->getStreetAddress1())) { if (empty($this->getStreetAddress1())) {
$context $context
->buildViolation("address.street1-should-be-set") ->buildViolation("address.street1-should-be-set")
->atPath('streetAddress1') ->atPath('streetAddress1')
->addViolation(); ->addViolation();
} }
if (!$this->getPostcode() instanceof PostalCode) { if (!$this->getPostcode() instanceof PostalCode) {
$context $context
->buildViolation("address.postcode-should-be-set") ->buildViolation("address.postcode-should-be-set")
@ -271,7 +350,7 @@ class Address
->addViolation(); ->addViolation();
} }
} }
/** /**
* @param Address $original * @param Address $original
* @return Address * @return Address
@ -286,5 +365,149 @@ class Address
; ;
} }
public function getStreet(): ?string
{
return $this->street;
}
public function setStreet(string $street): self
{
$this->street = $street;
return $this;
}
public function getStreetNumber(): ?string
{
return $this->streetNumber;
}
public function setStreetNumber(string $streetNumber): self
{
$this->streetNumber = $streetNumber;
return $this;
}
public function getFloor(): ?string
{
return $this->floor;
}
public function setFloor(?string $floor): self
{
$this->floor = $floor;
return $this;
}
public function getCorridor(): ?string
{
return $this->corridor;
}
public function setCorridor(?string $corridor): self
{
$this->corridor = $corridor;
return $this;
}
public function getSteps(): ?string
{
return $this->steps;
}
public function setSteps(?string $steps): self
{
$this->steps = $steps;
return $this;
}
public function getBuildingName(): ?string
{
return $this->buildingName;
}
public function setBuildingName(?string $buildingName): self
{
$this->buildingName = $buildingName;
return $this;
}
public function getFlat(): ?string
{
return $this->flat;
}
public function setFlat(?string $flat): self
{
$this->flat = $flat;
return $this;
}
public function getDistribution(): ?string
{
return $this->distribution;
}
public function setDistribution(?string $distribution): self
{
$this->distribution = $distribution;
return $this;
}
public function getExtra(): ?string
{
return $this->extra;
}
public function setExtra(?string $extra): self
{
$this->extra = $extra;
return $this;
}
public function getValidTo(): ?\DateTimeInterface
{
return $this->validTo;
}
public function setValidTo(\DateTimeInterface $validTo): self
{
$this->validTo = $validTo;
return $this;
}
public function getPoint(): ?Point
{
return $this->point;
}
public function setPoint(?Point $point): self
{
$this->point = $point;
return $this;
}
public function getLinkedToThirdParty()
{
return $this->linkedToThirdParty;
}
public function setLinkedToThirdParty($linkedToThirdParty): self
{
$this->linkedToThirdParty = $linkedToThirdParty;
return $this;
}
} }

View File

@ -0,0 +1,165 @@
<?php
namespace Chill\MainBundle\Entity;
use Chill\MainBundle\Entity\AddressReferenceRepository;
use Doctrine\ORM\Mapping as ORM;
use Chill\MainBundle\Doctrine\Model\Point;
/**
* @ORM\Entity(repositoryClass=AddressReferenceRepository::class)
* @ORM\Table(name="chill_main_address_reference")
* @ORM\HasLifecycleCallbacks()
*/
class AddressReference
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $refId;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $street;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $streetNumber;
/**
* @var PostalCode
*
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\PostalCode")
*/
private $postcode;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $municipalityCode;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $source;
/**
* A geospatial field storing the coordinates of the Address
*
* @var Point
*
* @ORM\Column(type="point")
*/
private $point;
public function getId(): ?int
{
return $this->id;
}
public function getRefId(): ?string
{
return $this->refId;
}
public function setRefId(string $refId): self
{
$this->refId = $refId;
return $this;
}
public function getStreet(): ?string
{
return $this->street;
}
public function setStreet(?string $street): self
{
$this->street = $street;
return $this;
}
public function getStreetNumber(): ?string
{
return $this->streetNumber;
}
public function setStreetNumber(?string $streetNumber): self
{
$this->streetNumber = $streetNumber;
return $this;
}
/**
* Set postcode
*
* @param PostalCode $postcode
*
* @return Address
*/
public function setPostcode(PostalCode $postcode = null)
{
$this->postcode = $postcode;
return $this;
}
/**
* Get postcode
*
* @return PostalCode
*/
public function getPostcode()
{
return $this->postcode;
}
public function getMunicipalityCode(): ?string
{
return $this->municipalityCode;
}
public function setMunicipalityCode(?string $municipalityCode): self
{
$this->municipalityCode = $municipalityCode;
return $this;
}
public function getSource(): ?string
{
return $this->source;
}
public function setSource(?string $source): self
{
$this->source = $source;
return $this;
}
public function getPoint(): ?Point
{
return $this->point;
}
public function setPoint(?Point $point): self
{
$this->point = $point;
return $this;
}
}

View File

@ -33,8 +33,8 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
* A type to create/update Address entity * A type to create/update Address entity
* *
* Options: * Options:
* *
* - `has_valid_from` (boolean): show if an entry "has valid from" must be * - `has_valid_from` (boolean): show if an entry "has valid from" must be
* shown. * shown.
* - `null_if_empty` (boolean): replace the address type by null if the street * - `null_if_empty` (boolean): replace the address type by null if the street
* or the postCode is empty. This is useful when the address is not required and * or the postCode is empty. This is useful when the address is not required and
@ -45,10 +45,10 @@ class AddressType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
->add('streetAddress1', TextType::class, array( ->add('street', TextType::class, array(
'required' => !$options['has_no_address'] // true if has no address is false 'required' => !$options['has_no_address'] // true if has no address is false
)) ))
->add('streetAddress2', TextType::class, array( ->add('streetNumber', TextType::class, array(
'required' => false 'required' => false
)) ))
->add('postCode', PostalCodeType::class, array( ->add('postCode', PostalCodeType::class, array(
@ -57,7 +57,7 @@ class AddressType extends AbstractType
'required' => !$options['has_no_address'] // true if has no address is false 'required' => !$options['has_no_address'] // true if has no address is false
)) ))
; ;
if ($options['has_valid_from']) { if ($options['has_valid_from']) {
$builder $builder
->add('validFrom', DateType::class, array( ->add('validFrom', DateType::class, array(
@ -67,7 +67,7 @@ class AddressType extends AbstractType
) )
); );
} }
if ($options['has_no_address']) { if ($options['has_no_address']) {
$builder $builder
->add('isNoAddress', ChoiceType::class, [ ->add('isNoAddress', ChoiceType::class, [
@ -79,12 +79,12 @@ class AddressType extends AbstractType
'label' => 'address.address_homeless' 'label' => 'address.address_homeless'
]); ]);
} }
if ($options['null_if_empty'] === TRUE) { if ($options['null_if_empty'] === TRUE) {
$builder->setDataMapper(new AddressDataMapper()); $builder->setDataMapper(new AddressDataMapper());
} }
} }
public function configureOptions(OptionsResolver $resolver) public function configureOptions(OptionsResolver $resolver)
{ {
$resolver $resolver

View File

@ -27,6 +27,7 @@ use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Form\Type\DataTransformer\ObjectToIdTransformer; use Chill\MainBundle\Form\Type\DataTransformer\ObjectToIdTransformer;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\Form\Type\Select2ChoiceType; use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
/** /**
* Extends choice to allow adding select2 library on widget * Extends choice to allow adding select2 library on widget
@ -41,15 +42,26 @@ class Select2CountryType extends AbstractType
*/ */
private $requestStack; private $requestStack;
/**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
/** /**
* @var ObjectManager * @var ObjectManager
*/ */
private $em; private $em;
public function __construct(RequestStack $requestStack,ObjectManager $em) public function __construct(
RequestStack $requestStack,
ObjectManager $em,
TranslatableStringHelper $translatableStringHelper
)
{ {
$this->requestStack = $requestStack; $this->requestStack = $requestStack;
$this->em = $em; $this->em = $em;
$this->translatableStringHelper = $translatableStringHelper;
} }
public function getBlockPrefix() public function getBlockPrefix()
@ -75,7 +87,7 @@ class Select2CountryType extends AbstractType
$choices = array(); $choices = array();
foreach ($countries as $c) { foreach ($countries as $c) {
$choices[$c->getId()] = $c->getName()[$locale]; $choices[$c->getId()] = $this->translatableStringHelper->localize($c->getName());
} }
asort($choices, SORT_STRING | SORT_FLAG_CASE); asort($choices, SORT_STRING | SORT_FLAG_CASE);

View File

@ -27,6 +27,7 @@ use Symfony\Component\Form\FormBuilderInterface;
use Chill\MainBundle\Form\Type\DataTransformer\MultipleObjectsToIdTransformer; use Chill\MainBundle\Form\Type\DataTransformer\MultipleObjectsToIdTransformer;
use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\Form\Type\Select2ChoiceType; use Chill\MainBundle\Form\Type\Select2ChoiceType;
use Chill\MainBundle\Templating\TranslatableStringHelper;
/** /**
* Extends choice to allow adding select2 library on widget for languages (multiple) * Extends choice to allow adding select2 library on widget for languages (multiple)
@ -43,10 +44,21 @@ class Select2LanguageType extends AbstractType
*/ */
private $em; private $em;
public function __construct(RequestStack $requestStack,ObjectManager $em) /**
*
* @var TranslatableStringHelper
*/
protected $translatableStringHelper;
public function __construct(
RequestStack $requestStack,
ObjectManager $em,
TranslatableStringHelper $translatableStringHelper
)
{ {
$this->requestStack = $requestStack; $this->requestStack = $requestStack;
$this->em = $em; $this->em = $em;
$this->translatableStringHelper = $translatableStringHelper;
} }
public function getBlockPrefix() public function getBlockPrefix()
@ -72,7 +84,7 @@ class Select2LanguageType extends AbstractType
$choices = array(); $choices = array();
foreach ($languages as $l) { foreach ($languages as $l) {
$choices[$l->getId()] = $l->getName()[$locale]; $choices[$l->getId()] = $this->translatableStringHelper->localize($l->getName());
} }
asort($choices, SORT_STRING | SORT_FLAG_CASE); asort($choices, SORT_STRING | SORT_FLAG_CASE);

View File

@ -0,0 +1,50 @@
<?php
namespace Chill\MainBundle\Repository;
use Chill\MainBundle\Entity\AddressReference;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method AddressReference|null find($id, $lockMode = null, $lockVersion = null)
* @method AddressReference|null findOneBy(array $criteria, array $orderBy = null)
* @method AddressReference[] findAll()
* @method AddressReference[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class AddressReferenceRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, AddressReference::class);
}
// /**
// * @return AddressReference[] Returns an array of AddressReference objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('a')
->andWhere('a.exampleField = :val')
->setParameter('val', $value)
->orderBy('a.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?AddressReference
{
return $this->createQueryBuilder('a')
->andWhere('a.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -28,7 +28,7 @@
// @import "bootstrap/scss/card"; // @import "bootstrap/scss/card";
// @import "bootstrap/scss/breadcrumb"; // @import "bootstrap/scss/breadcrumb";
// @import "bootstrap/scss/pagination"; // @import "bootstrap/scss/pagination";
// @import "bootstrap/scss/badge"; @import "bootstrap/scss/badge";
// @import "bootstrap/scss/jumbotron"; // @import "bootstrap/scss/jumbotron";
// @import "bootstrap/scss/alert"; // @import "bootstrap/scss/alert";
// @import "bootstrap/scss/progress"; // @import "bootstrap/scss/progress";
@ -41,7 +41,7 @@
// @import "bootstrap/scss/popover"; // @import "bootstrap/scss/popover";
// @import "bootstrap/scss/carousel"; // @import "bootstrap/scss/carousel";
// @import "bootstrap/scss/spinners"; // @import "bootstrap/scss/spinners";
// @import "bootstrap/scss/utilities"; @import "bootstrap/scss/utilities";
// @import "bootstrap/scss/print"; // @import "bootstrap/scss/print";
@import "custom"; @import "custom";

View File

@ -5,7 +5,7 @@
@include button($green, $white); @include button($green, $white);
} }
&.bt-reset, &.bt-delete { &.bt-reset, &.bt-delete, &.bt-remove {
@include button($red, $white); @include button($red, $white);
} }
@ -24,6 +24,7 @@
&.bt-save::before, &.bt-save::before,
&.bt-new::before, &.bt-new::before,
&.bt-delete::before, &.bt-delete::before,
&.bt-remove::before,
&.bt-update::before, &.bt-update::before,
&.bt-edit::before, &.bt-edit::before,
&.bt-cancel::before, &.bt-cancel::before,
@ -56,7 +57,12 @@
// add a trash // add a trash
content: ""; content: "";
} }
&.bt-remove::before {
// add a times
content: "";
}
&.bt-edit::before, &.bt-update::before { &.bt-edit::before, &.bt-update::before {
// add a pencil // add a pencil
content: ""; content: "";
@ -94,6 +100,7 @@
&.bt-save::before, &.bt-save::before,
&.bt-new::before, &.bt-new::before,
&.bt-delete::before, &.bt-delete::before,
&.bt-remove::before,
&.bt-update::before, &.bt-update::before,
&.bt-edit::before, &.bt-edit::before,
&.bt-cancel::before, &.bt-cancel::before,
@ -123,6 +130,7 @@
&.bt-save::before, &.bt-save::before,
&.bt-new::before, &.bt-new::before,
&.bt-delete::before, &.bt-delete::before,
&.bt-remove::before,
&.bt-update::before, &.bt-update::before,
&.bt-edit::before, &.bt-edit::before,
&.bt-cancel::before, &.bt-cancel::before,

View File

@ -39,6 +39,8 @@ div.subheader {
height: 130px; height: 130px;
} }
//// VUEJS ////
div.vue-component { div.vue-component {
padding: 1.5em; padding: 1.5em;
margin: 2em 0; margin: 2em 0;
@ -55,3 +57,97 @@ div.vue-component {
} }
dd { margin-left: 1em; } dd { margin-left: 1em; }
} }
//// MODAL ////
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.75);
display: table;
transition: opacity 0.3s ease;
}
.modal-header .close { // bootstrap classes, override sc-button 0 radius
border-top-right-radius: 0.3rem;
}
/*
* The following styles are auto-applied to elements with
* transition="modal" when their visibility is toggled
* by Vue.js.
*
* You can easily play with the modal transition by editing
* these styles.
*/
.modal-enter {
opacity: 0;
}
.modal-leave-active {
opacity: 0;
}
.modal-enter .modal-container,
.modal-leave-active .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
//// AddPersons modal
div.modal-body.up {
margin: auto 4em;
div.search {
position: relative;
input {
padding: 1.2em 1.5em 1.2em 2.5em;
margin: 1em 0;
}
i {
position: absolute;
top: 50%;
left: 0.5em;
padding: 0.65em 0;
opacity: 0.5;
}
}
}
div.results {
div.count {
margin: -0.5em 0 0.7em;
display: flex;
justify-content: space-between;
}
div.list-item {
line-height: 26pt;
padding: 0.3em 0.8em;
display: flex;
flex-direction: row;
&.checked {
background-color: #ececec;
border-bottom: 1px dotted #8b8b8b;
}
div.container {
& > input {
margin-right: 0.8em;
}
}
div.right_actions {
margin: 0 0 0 auto;
& > * {
margin-left: 0.5em;
}
a.sc-button {
border: 1px solid lightgrey;
font-size: 70%;
padding: 4px;
}
}
}
}
.discret {
color: grey;
margin-right: 1em;
}

View File

@ -0,0 +1,45 @@
<template>
<transition name="modal">
<div class="modal-mask">
<!-- :: styles bootstrap :: -->
<div class="modal-dialog" :class="modalDialogClass">
<div class="modal-content">
<div class="modal-header">
<slot name="header"></slot>
<button class="close sc-button grey" @click="$emit('close')">
<i class="fa fa-times" aria-hidden="true"></i></button>
</div>
<div class="modal-body up" style="overflow-y: unset;">
<slot name="body-fixed"></slot>
</div>
<div class="modal-body">
<slot name="body"></slot>
</div>
<div class="modal-footer">
<button class="sc-button cancel" @click="$emit('close')">{{ $t('action.close') }}</button>
<slot name="footer"></slot>
</div>
</div>
</div>
<!-- :: end styles bootstrap :: -->
</div>
</transition>
</template>
<script>
/*
* This Modal component is a mix between :
* - Vue3 modal implementation
* => with 'v-if:showModal' directive:parameter, html scope is added/removed not just shown/hidden
* => with slot we can pass content from parent component
* => some classes are passed from parent component
* - Bootstrap 4.6 _modal.scss module
* => using bootstrap css classes, the modal have a responsive behaviour,
* => modal design can be configured using css classes (size, scroll)
*/
export default {
name: 'Modal',
props: ['modalDialogClass'],
emits: ['close']
}
</script>

View File

@ -0,0 +1,55 @@
import { createI18n } from 'vue-i18n'
const datetimeFormats = {
fr: {
short: {
year: "numeric",
month: "numeric",
day: "numeric"
},
long: {
year: "numeric",
month: "short",
day: "numeric",
weekday: "short",
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",
next: "Suivant",
previous: "Précédent",
back: "Retour",
check_all: "cocher tout",
reset: "réinitialiser"
},
}
};
const _createI18n = (appMessages) => {
Object.assign(messages.fr, appMessages.fr);
return createI18n({
locale: 'fr',
fallbackLocale: 'fr',
datetimeFormats,
messages,
})
};
export { _createI18n }

View File

@ -6,8 +6,8 @@
<div class="chill_address_is_noaddress">{{ 'address.consider homeless'|trans }}</div> <div class="chill_address_is_noaddress">{{ 'address.consider homeless'|trans }}</div>
{% endif %} {% endif %}
<div class="chill_address_address"> <div class="chill_address_address">
{% if address.streetAddress1 is not empty %}<p class="street street1">{{ address.streetAddress1 }}</p>{% endif %} {% if address.street is not empty %}<p class="street street1">{{ address.street }}</p>{% endif %}
{% if address.streetAddress2 is not empty %}<p class="street street2">{{ address.streetAddress2 }}</p>{% endif %} {% if address.streetNumber is not empty %}<p class="street street2">{{ address.streetNumber }}</p>{% endif %}
{% if address.postCode is not empty %} {% if address.postCode is not empty %}
<p class="postalCode"><span class="code">{{ address.postCode.code }}</span> <span class="name">{{ address.postCode.name }}</span></p> <p class="postalCode"><span class="code">{{ address.postCode.code }}</span> <span class="name">{{ address.postCode.name }}</span></p>
<p class="country">{{ address.postCode.country.name|localize_translatable_string }}</p> <p class="country">{{ address.postCode.country.name|localize_translatable_string }}</p>

View File

@ -0,0 +1,28 @@
<?php
namespace Chill\MainBundle\Serializer\Model;
use Chill\MainBundle\Pagination\PaginatorInterface;
class Collection
{
private PaginatorInterface $paginator;
private $items;
public function __construct($items, PaginatorInterface $paginator)
{
$this->items = $items;
$this->paginator = $paginator;
}
public function getPaginator(): PaginatorInterface
{
return $this->paginator;
}
public function getItems()
{
return $this->items;
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Chill\MainBundle\Serializer\Normalizer;
use Chill\MainBundle\Serializer\Model\Collection;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
class CollectionNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
use NormalizerAwareTrait;
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof Collection;
}
public function normalize($collection, string $format = null, array $context = [])
{
/** @var $collection Collection */
/** @var $collection Chill\MainBundle\Pagination\PaginatorInterface */
$paginator = $collection->getPaginator();
$data['count'] = $paginator->getTotalItems();
$pagination['first'] = $paginator->getCurrentPageFirstItemNumber();
$pagination['items_per_page'] = $paginator->getItemsPerPage();
$pagination['next'] = $paginator->hasNextPage() ?
$paginator->getNextPage()->generateUrl() : null;
$pagination['previous'] = $paginator->hasPreviousPage() ?
$paginator->getPreviousPage()->generateUrl() : null;
$pagination['more'] = $paginator->hasNextPage();
$data['pagination'] = $pagination;
// normalize results
$data['results'] = $this->normalizer->normalize($collection->getItems(),
$format, $context);
return $data;
}
}

View File

@ -0,0 +1,119 @@
<?php
namespace Chill\MainBundle\Tests\Doctrine\Model;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Chill\MainBundle\Doctrine\Model\Point;
/**
* Test the point model methods
*
* @author Julien Minet <julien.minet@champs-libres.coop>
*/
class ExportControllerTest extends KernelTestCase
{
public function testToWKT()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals($point->toWKT(),'SRID=4326;POINT(4.8634 50.47382)');
}
public function testToGeojson()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals($point->toGeoJson(),'{"type":"Point","coordinates":[4.8634,50.47382]}');
}
public function testToArrayGeoJson()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals(
$point->toArrayGeoJson(),
[
'type' => 'Point',
'coordinates' => [4.8634, 50.47382]
]
);
}
public function testJsonSerialize()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);
$this->assertEquals(
$point->jsonSerialize(),
[
'type' => 'Point',
'coordinates' => [4.8634, 50.47382]
]
);
}
public function testFromGeoJson()
{
$geojson = '{"type":"Point","coordinates":[4.8634,50.47382]}';
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($point, Point::fromGeoJson($geojson));
}
public function testFromLonLat()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($point, Point::fromLonLat($lon, $lat));
}
public function testFromArrayGeoJson()
{
$array = [
'type' => 'Point',
'coordinates' => [4.8634, 50.47382]
];
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($point, Point::fromArrayGeoJson($array));
}
public function testGetLat()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($lat, $point->getLat());
}
public function testGetLon()
{
$lon = 4.8634;
$lat = 50.47382;
$point = $this->preparePoint($lon, $lat);;
$this->assertEquals($lon, $point->getLon());
}
private function preparePoint($lon, $lat)
{
return Point::fromLonLat($lon, $lat);
}
}

View File

@ -1,7 +1,8 @@
services: services:
Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader: Chill\MainBundle\CRUD\Routing\CRUDRoutesLoader:
arguments: arguments:
$config: '%chill_main_crud_route_loader_config%' $crudConfig: '%chill_main_crud_route_loader_config%'
$apiConfig: '%chill_main_api_route_loader_config%'
tags: [ routing.loader ] tags: [ routing.loader ]
Chill\MainBundle\CRUD\Resolver\Resolver: Chill\MainBundle\CRUD\Resolver\Resolver:
@ -13,4 +14,4 @@ services:
arguments: arguments:
$resolver: '@Chill\MainBundle\CRUD\Resolver\Resolver' $resolver: '@Chill\MainBundle\CRUD\Resolver\Resolver'
tags: tags:
- { name: twig.extension } - { name: twig.extension }

View File

@ -23,6 +23,7 @@ services:
arguments: arguments:
- "@request_stack" - "@request_stack"
- "@doctrine.orm.entity_manager" - "@doctrine.orm.entity_manager"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: form.type, alias: select2_chill_country } - { name: form.type, alias: select2_chill_country }
@ -31,6 +32,7 @@ services:
arguments: arguments:
- "@request_stack" - "@request_stack"
- "@doctrine.orm.entity_manager" - "@doctrine.orm.entity_manager"
- "@chill.main.helper.translatable_string"
tags: tags:
- { name: form.type, alias: select2_chill_language } - { name: form.type, alias: select2_chill_language }

View File

@ -6,6 +6,7 @@ services:
- "@request_stack" - "@request_stack"
- "@router" - "@router"
- "%chill_main.pagination.item_per_page%" - "%chill_main.pagination.item_per_page%"
Chill\MainBundle\Pagination\PaginatorFactory: '@chill_main.paginator_factory' Chill\MainBundle\Pagination\PaginatorFactory: '@chill_main.paginator_factory'
chill_main.paginator.twig_extensions: chill_main.paginator.twig_extensions:

View File

@ -11,3 +11,7 @@ services:
Chill\MainBundle\Serializer\Normalizer\UserNormalizer: Chill\MainBundle\Serializer\Normalizer\UserNormalizer:
tags: tags:
- { name: 'serializer.normalizer', priority: 64 } - { name: 'serializer.normalizer', priority: 64 }
Chill\MainBundle\Serializer\Normalizer\CollectionNormalizer:
tags:
- { name: 'serializer.normalizer', priority: 64 }

View File

@ -0,0 +1,32 @@
<?php declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Create the postgis extension
*/
final class Version20210414091001 extends AbstractMigration
{
public function up(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql('CREATE EXTENSION IF NOT EXISTS postgis;');
}
public function down(Schema $schema) : void
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
$this->addSql('DROP EXTENSION IF NOT EXISTS postgis;');
}
public function getDescription(): string
{
return "Enable the postgis extension in public schema";
}
}

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add new fields to address, including a Point geometry field.
*/
final class Version20210420115006 extends AbstractMigration
{
public function getDescription() : string
{
return 'Add a Point data type and modify the Address entity';
}
public function up(Schema $schema) : void
{
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN streetaddress1 TO street;');
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN streetaddress2 TO streetNumber;');
$this->addSql('ALTER TABLE chill_main_address ADD floor VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD corridor VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD steps VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD buildingName VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD flat VARCHAR(16) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD distribution VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD extra VARCHAR(255) DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD validTo DATE DEFAULT NULL');
$this->addSql('ALTER TABLE chill_main_address ADD point geometry(POINT,4326) DEFAULT NULL');
}
public function down(Schema $schema) : void
{
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN street TO streetaddress1;');
$this->addSql('ALTER TABLE chill_main_address RENAME COLUMN streetNumber TO streetaddress2;');
$this->addSql('ALTER TABLE chill_main_address DROP floor');
$this->addSql('ALTER TABLE chill_main_address DROP corridor');
$this->addSql('ALTER TABLE chill_main_address DROP steps');
$this->addSql('ALTER TABLE chill_main_address DROP buildingName');
$this->addSql('ALTER TABLE chill_main_address DROP flat');
$this->addSql('ALTER TABLE chill_main_address DROP distribution');
$this->addSql('ALTER TABLE chill_main_address DROP extra');
$this->addSql('ALTER TABLE chill_main_address DROP validTo');
$this->addSql('ALTER TABLE chill_main_address DROP point');
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add a AddressReference table for storing authoritative address data
*/
final class Version20210503085107 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add a AddressReference table for storing authoritative address data';
}
public function up(Schema $schema): void
{
$this->addSql('CREATE SEQUENCE chill_main_address_reference_id_seq INCREMENT BY 1 MINVALUE 1 START 1');
$this->addSql('CREATE TABLE chill_main_address_reference (id INT NOT NULL, postcode_id INT DEFAULT NULL, refId VARCHAR(255) NOT NULL, street VARCHAR(255) DEFAULT NULL, streetNumber VARCHAR(255) DEFAULT NULL, municipalityCode VARCHAR(255) DEFAULT NULL, source VARCHAR(255) DEFAULT NULL, point geometry(POINT,4326) NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE INDEX IDX_CA6C1BD7EECBFDF1 ON chill_main_address_reference (postcode_id)');
$this->addSql('ALTER TABLE chill_main_address_reference ADD CONSTRAINT FK_CA6C1BD7EECBFDF1 FOREIGN KEY (postcode_id) REFERENCES chill_main_postal_code (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}
public function down(Schema $schema): void
{
$this->addSql('DROP SEQUENCE chill_main_address_reference_id_seq CASCADE');
$this->addSql('DROP TABLE chill_main_address_reference');
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Add linkedToThirdParty field to Address
*/
final class Version20210505153727 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add linkedToThirdParty field to Address';
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_main_address ADD linkedToThirdParty_id INT DEFAULT NULL');
$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');
$this->addSql('CREATE INDEX IDX_165051F6114B8DD9 ON chill_main_address (linkedToThirdParty_id)');
$this->addSql('
CREATE TABLE chill_main_address_legacy AS
TABLE chill_main_address;
');
$this->addSql('
WITH hydrated_addresses AS (
SELECT *, rank() OVER (PARTITION BY pa_a.person_id ORDER BY validfrom)
FROM chill_main_address AS aa JOIN chill_person_persons_to_addresses AS pa_a ON aa.id = pa_a.address_id
)
UPDATE chill_main_address AS b
SET validto = (
SELECT validfrom - INTERVAL \'1 DAY\'
FROM hydrated_addresses
WHERE hydrated_addresses.id = (
SELECT a1.id
FROM hydrated_addresses AS a1 JOIN hydrated_addresses AS a2 ON a2.person_id = a1.person_id AND a2.rank = (a1.rank-1)
WHERE a2.id = b.id
)
);
');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE chill_main_address DROP CONSTRAINT FK_165051F6114B8DD9');
$this->addSql('DROP INDEX IDX_165051F6114B8DD9');
$this->addSql('ALTER TABLE chill_main_address DROP linkedToThirdParty_id');
$this->addSql('DROP TABLE IF EXISTS chill_main_address_legacy');
$this->addSql('
UPDATE chill_main_address
SET validto = null;
');
}
}

View File

@ -0,0 +1,79 @@
<?php
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;
class AccompanyingCourseApiController extends ApiController
{
protected EventDispatcherInterface $eventDispatcher;
protected ValidatorInterface $validator;
public function __construct(EventDispatcherInterface $eventDispatcher, $validator)
{
$this->eventDispatcher = $eventDispatcher;
$this->validator = $validator;
}
public function participationApi($id, Request $request, $_format)
{
/** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = $this->getEntity('participation', $id, $request);
$person = $this->getSerializer()
->deserialize($request->getContent(), Person::class, $_format, []);
if (NULL === $person) {
throw new BadRequestException('person id not found');
}
// TODO add acl
//
$this->onPostCheckACL('participation', $request, $_format, $accompanyingPeriod);
switch ($request->getMethod()) {
case Request::METHOD_POST:
$participation = $accompanyingPeriod->addPerson($person);
break;
case Request::METHOD_DELETE:
$participation = $accompanyingPeriod->removePerson($person);
$participation->setEndDate(new \DateTimeImmutable('now'));
break;
default:
throw new BadRequestException("This method is not supported");
}
$errors = $this->validator->validate($accompanyingPeriod);
if ($errors->count() > 0) {
// only format accepted
return $this->json($errors);
}
$this->getDoctrine()->getManager()->flush();
return $this->json($participation);
}
protected function onPostCheckACL(string $action, Request $request, string $_format, $entity): ?Response
{
$this->eventDispatcher->dispatch(
AccompanyingPeriodPrivacyEvent::ACCOMPANYING_PERIOD_PRIVACY_EVENT,
new AccompanyingPeriodPrivacyEvent($entity, [
'action' => $action,
'request' => $request->getMethod()
])
);
return null;
}
}

View File

@ -121,7 +121,7 @@ class AccompanyingCourseController extends Controller
* @Route( * @Route(
* "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/participation.{_format}", * "/{_locale}/person/api/1.0/accompanying-course/{accompanying_period_id}/participation.{_format}",
* name="chill_person_accompanying_course_api_add_participation", * name="chill_person_accompanying_course_api_add_participation",
* methods={"POST"}, * methods={"POST","DELETE"},
* format="json", * format="json",
* requirements={ * requirements={
* "_format": "json", * "_format": "json",
@ -129,7 +129,7 @@ class AccompanyingCourseController extends Controller
* ) * )
* @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"}) * @ParamConverter("accompanyingCourse", options={"id": "accompanying_period_id"})
*/ */
public function addParticipationAPI(Request $request, AccompanyingPeriod $accompanyingCourse, $_format): Response public function participationAPI(Request $request, AccompanyingPeriod $accompanyingCourse, $_format): Response
{ {
switch ($_format) { switch ($_format) {
case 'json': case 'json':
@ -146,7 +146,9 @@ class AccompanyingCourseController extends Controller
} }
// TODO add acl // TODO add acl
$accompanyingCourse->addPerson($person); $participation = ($request->getMethod() === 'POST') ?
$accompanyingCourse->addPerson($person) : $accompanyingCourse->removePerson($person);
$errors = $this->validator->validate($accompanyingCourse); $errors = $this->validator->validate($accompanyingCourse);
if ($errors->count() > 0) { if ($errors->count() > 0) {
@ -156,6 +158,6 @@ class AccompanyingCourseController extends Controller
$this->getDoctrine()->getManager()->flush(); $this->getDoctrine()->getManager()->flush();
return new JsonResponse(); return $this->json($participation);
} }
} }

View File

@ -0,0 +1,17 @@
<?php
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class OpeningApiController extends ApiController
{
protected function customizeQuery(string $action, Request $request, $qb): void
{
$qb->where($qb->expr()->gt('e.noActiveAfter', ':now'))
->orWhere($qb->expr()->isNull('e.noActiveAfter'));
$qb->setParameter('now', new \DateTime('now'));
}
}

View File

@ -38,6 +38,7 @@ use Symfony\Component\Translation\TranslatorInterface;
use Chill\MainBundle\Search\SearchProvider; use Chill\MainBundle\Search\SearchProvider;
use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper; use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Repository\PersonNotDuplicateRepository;
use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
@ -290,7 +291,7 @@ final class PersonController extends AbstractController
return $errors; return $errors;
} }
public function reviewAction(Request $request) public function reviewAction(Request $request, PersonNotDuplicateRepository $personNotDuplicateRepository)
{ {
if ($request->getMethod() !== 'POST') { if ($request->getMethod() !== 'POST') {
$r = new Response("You must send something to review the creation of a new Person"); $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( $form = $this->createForm(
//CreationPersonType::NAME,
CreationPersonType::class, CreationPersonType::class,
new Person(), new Person(),
array( array(
@ -326,7 +326,7 @@ final class PersonController extends AbstractController
} }
$form = $this->createForm( $form = $this->createForm(
CreationPersonType::NAME, CreationPersonType::class,
$person, $person,
array( array(
'action' => $this->generateUrl('chill_person_review'), 'action' => $this->generateUrl('chill_person_review'),
@ -342,8 +342,7 @@ final class PersonController extends AbstractController
$this->em->persist($person); $this->em->persist($person);
$alternatePersons = $this->similarPersonMatcher $alternatePersons = $this->similarPersonMatcher->matchPerson($person, $personNotDuplicateRepository);
->matchPerson($person);
if (count($alternatePersons) === 0) { if (count($alternatePersons) === 0) {
return $this->forward('ChillPersonBundle:Person:create'); return $this->forward('ChillPersonBundle:Person:create');

View File

@ -19,6 +19,7 @@ use Symfony\Component\Translation\TranslatorInterface;
use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Entity\Activity;
use Chill\DocStoreBundle\Entity\PersonDocument; use Chill\DocStoreBundle\Entity\PersonDocument;
use Chill\EventBundle\Entity\Participation; use Chill\EventBundle\Entity\Participation;
use Chill\PersonBundle\Repository\PersonNotDuplicateRepository;
use Chill\TaskBundle\Entity\SingleTask; use Chill\TaskBundle\Entity\SingleTask;
class PersonDuplicateController extends Controller class PersonDuplicateController extends Controller
@ -62,7 +63,7 @@ class PersonDuplicateController extends Controller
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
} }
public function viewAction($person_id) public function viewAction($person_id, PersonNotDuplicateRepository $personNotDuplicateRepository)
{ {
$person = $this->_getPerson($person_id); $person = $this->_getPerson($person_id);
if ($person === null) { if ($person === null) {
@ -74,10 +75,9 @@ class PersonDuplicateController extends Controller
"You are not allowed to see this person."); "You are not allowed to see this person.");
$duplicatePersons = $this->similarPersonMatcher-> $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) $notDuplicatePersons = $personNotDuplicateRepository->findNotDuplicatePerson($person);
->findNotDuplicatePerson($person);
return $this->render('ChillPersonBundle:PersonDuplicate:view.html.twig', [ return $this->render('ChillPersonBundle:PersonDuplicate:view.html.twig', [
'person' => $person, 'person' => $person,
@ -97,7 +97,7 @@ class PersonDuplicateController extends Controller
$person1->counters = $this->_getCounters($person1_id); $person1->counters = $this->_getCounters($person1_id);
$person2->counters = $this->_getCounters($person2_id); $person2->counters = $this->_getCounters($person2_id);
if ($person1 === null) { if ($person1 === null) {
throw $this->createNotFoundException("Person with id $person1_id not" throw $this->createNotFoundException("Person with id $person1_id not"
. " found on this server"); . " found on this server");
@ -264,17 +264,17 @@ class PersonDuplicateController extends Controller
return [$person1, $person2]; return [$person1, $person2];
} }
private function _getCounters($id): ?array private function _getCounters($id): ?array
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$nb_activity = $em->getRepository(Activity::class)->findBy(['person'=>$id]); $nb_activity = $em->getRepository(Activity::class)->findBy(['person'=>$id]);
$nb_document = $em->getRepository(PersonDocument::class)->findBy(['person'=>$id]); $nb_document = $em->getRepository(PersonDocument::class)->findBy(['person'=>$id]);
$nb_event = $em->getRepository(Participation::class)->findBy(['person'=>$id]); $nb_event = $em->getRepository(Participation::class)->findBy(['person'=>$id]);
$nb_task = $em->getRepository(SingleTask::class)->countByParameters(['person'=>$id]); $nb_task = $em->getRepository(SingleTask::class)->countByParameters(['person'=>$id]);
$person = $em->getRepository(Person::class)->findOneBy(['id'=>$id]); $person = $em->getRepository(Person::class)->findOneBy(['id'=>$id]);
return [ return [
'nb_activity' => count($nb_activity), 'nb_activity' => count($nb_activity),
'nb_document' => count($nb_document), 'nb_document' => count($nb_document),

View File

@ -0,0 +1,88 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
* Chill is a software for social workers
*
* Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS,
* <http://www.champs-libres.coop>, <info@champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -29,6 +29,7 @@ use Chill\PersonBundle\Entity\Person;
use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes; use Chill\MainBundle\DataFixtures\ORM\LoadPostalCodes;
use Chill\MainBundle\Entity\Address; use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Doctrine\Model\Point;
/** /**
* Load people into database * Load people into database
@ -37,17 +38,17 @@ use Chill\MainBundle\Entity\Address;
* @author Marc Ducobu <marc@champs-libres.coop> * @author Marc Ducobu <marc@champs-libres.coop>
*/ */
class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{ {
use \Symfony\Component\DependencyInjection\ContainerAwareTrait; use \Symfony\Component\DependencyInjection\ContainerAwareTrait;
protected $faker; protected $faker;
public function __construct() public function __construct()
{ {
$this->faker = \Faker\Factory::create('fr_FR'); $this->faker = \Faker\Factory::create('fr_FR');
} }
public function prepare() public function prepare()
{ {
//prepare days, month, years //prepare days, month, years
@ -56,57 +57,57 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
$this->years[] = $y; $this->years[] = $y;
$y = $y +1; $y = $y +1;
} while ($y >= 1990); } while ($y >= 1990);
$m = 1; $m = 1;
do { do {
$this->month[] = $m; $this->month[] = $m;
$m = $m +1; $m = $m +1;
} while ($m >= 12); } while ($m >= 12);
$d = 1; $d = 1;
do { do {
$this->day[] = $d; $this->day[] = $d;
$d = $d + 1; $d = $d + 1;
} while ($d <= 28); } while ($d <= 28);
} }
public function getOrder() public function getOrder()
{ {
return 10000; return 10000;
} }
public function load(ObjectManager $manager) public function load(ObjectManager $manager)
{ {
$this->loadRandPeople($manager); $this->loadRandPeople($manager);
$this->loadExpectedPeople($manager); $this->loadExpectedPeople($manager);
$manager->flush(); $manager->flush();
} }
public function loadExpectedPeople(ObjectManager $manager) public function loadExpectedPeople(ObjectManager $manager)
{ {
echo "loading expected people...\n"; echo "loading expected people...\n";
foreach ($this->peoples as $person) { foreach ($this->peoples as $person) {
$this->addAPerson($this->fillWithDefault($person), $manager); $this->addAPerson($this->fillWithDefault($person), $manager);
} }
} }
public function loadRandPeople(ObjectManager $manager) public function loadRandPeople(ObjectManager $manager)
{ {
echo "loading rand people...\n"; echo "loading rand people...\n";
$this->prepare(); $this->prepare();
$chooseLastNameOrTri = array('tri', 'tri', 'name', 'tri'); $chooseLastNameOrTri = array('tri', 'tri', 'name', 'tri');
$i = 0; $i = 0;
do { do {
$i++; $i++;
$sex = $this->genders[array_rand($this->genders)]; $sex = $this->genders[array_rand($this->genders)];
if ($chooseLastNameOrTri[array_rand($chooseLastNameOrTri)] === 'tri' ) { if ($chooseLastNameOrTri[array_rand($chooseLastNameOrTri)] === 'tri' ) {
$length = rand(2, 3); $length = rand(2, 3);
$lastName = ''; $lastName = '';
@ -117,13 +118,13 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
} else { } else {
$lastName = $this->lastNames[array_rand($this->lastNames)]; $lastName = $this->lastNames[array_rand($this->lastNames)];
} }
if ($sex === Person::MALE_GENDER) { if ($sex === Person::MALE_GENDER) {
$firstName = $this->firstNamesMale[array_rand($this->firstNamesMale)]; $firstName = $this->firstNamesMale[array_rand($this->firstNamesMale)];
} else { } else {
$firstName = $this->firstNamesFemale[array_rand($this->firstNamesFemale)]; $firstName = $this->firstNamesFemale[array_rand($this->firstNamesFemale)];
} }
// add an address on 80% of the created people // add an address on 80% of the created people
if (rand(0,100) < 80) { if (rand(0,100) < 80) {
$address = $this->getRandomAddress(); $address = $this->getRandomAddress();
@ -137,7 +138,7 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
} else { } else {
$address = null; $address = null;
} }
$person = array( $person = array(
'FirstName' => $firstName, 'FirstName' => $firstName,
'LastName' => $lastName, 'LastName' => $lastName,
@ -147,15 +148,15 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
'Address' => $address, 'Address' => $address,
'maritalStatus' => $this->maritalStatusRef[array_rand($this->maritalStatusRef)] 'maritalStatus' => $this->maritalStatusRef[array_rand($this->maritalStatusRef)]
); );
$this->addAPerson($this->fillWithDefault($person), $manager); $this->addAPerson($this->fillWithDefault($person), $manager);
} while ($i <= 100); } while ($i <= 100);
} }
/** /**
* fill a person array with default value * fill a person array with default value
* *
* @param string[] $specific * @param string[] $specific
*/ */
private function fillWithDefault(array $specific) private function fillWithDefault(array $specific)
@ -171,10 +172,10 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
'Address' => null 'Address' => null
), $specific); ), $specific);
} }
/** /**
* create a new person from array data * create a new person from array data
* *
* @param array $person * @param array $person
* @param ObjectManager $manager * @param ObjectManager $manager
* @throws \Exception * @throws \Exception
@ -200,35 +201,51 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
$this->addAccompanyingPeriods($p, $value, $manager); $this->addAccompanyingPeriods($p, $value, $manager);
break; break;
} }
//try to add the data using the setSomething function, //try to add the data using the setSomething function,
// if not possible, fallback to addSomething function // if not possible, fallback to addSomething function
if (method_exists($p, 'set'.$key)) { if (method_exists($p, 'set'.$key)) {
call_user_func(array($p, 'set'.$key), $value); call_user_func(array($p, 'set'.$key), $value);
} elseif (method_exists($p, 'add'.$key)) { } elseif (method_exists($p, 'add'.$key)) {
// if we have a "addSomething", we may have multiple items to add // if we have a "addSomething", we may have multiple items to add
// so, we set the value in an array if it is not an array, and // so, we set the value in an array if it is not an array, and
// will call the function addSomething multiple times // will call the function addSomething multiple times
if (!is_array($value)) { if (!is_array($value)) {
$value = array($value); $value = array($value);
} }
foreach($value as $v) { foreach($value as $v) {
if ($v !== NULL) { if ($v !== NULL) {
call_user_func(array($p, 'add'.$key), $v); call_user_func(array($p, 'add'.$key), $v);
} }
} }
} }
} }
$manager->persist($p); $manager->persist($p);
echo "add person'".$p->__toString()."'\n"; echo "add person'".$p->__toString()."'\n";
} }
/** /**
* Creata a random address * Create a random point
* *
* @return Point
*/
private function getRandomPoint()
{
$lonBrussels = 4.35243;
$latBrussels = 50.84676;
$lon = $lonBrussels + 0.01 * rand(-5, 5);
$lat = $latBrussels + 0.01 * rand(-5, 5);
return Point::fromLonLat($lon, $lat);
}
/**
* Create a random address
*
* @return Address * @return Address
*/ */
private function getRandomAddress() private function getRandomAddress()
@ -238,13 +255,16 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
->setStreetAddress2( ->setStreetAddress2(
rand(0,9) > 5 ? $this->faker->streetAddress : '' rand(0,9) > 5 ? $this->faker->streetAddress : ''
) )
->setPoint(
rand(0,9) > 5 ? $this->getRandomPoint() : NULL
)
->setPostcode($this->getReference( ->setPostcode($this->getReference(
LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)] LoadPostalCodes::$refs[array_rand(LoadPostalCodes::$refs)]
)) ))
->setValidFrom($this->faker->dateTimeBetween('-5 years')) ->setValidFrom($this->faker->dateTimeBetween('-5 years'))
; ;
} }
private function getCountry($countryCode) private function getCountry($countryCode)
{ {
if ($countryCode === NULL) { if ($countryCode === NULL) {
@ -257,30 +277,30 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
private $maritalStatusRef = ['ms_single', 'ms_married', 'ms_widow', 'ms_separat', private $maritalStatusRef = ['ms_single', 'ms_married', 'ms_widow', 'ms_separat',
'ms_divorce', 'ms_legalco', 'ms_unknown']; 'ms_divorce', 'ms_legalco', 'ms_unknown'];
private $firstNamesMale = array("Jean", "Mohamed", "Alfred", "Robert", "Justin", "Brian", private $firstNamesMale = array("Jean", "Mohamed", "Alfred", "Robert", "Justin", "Brian",
"Compère", "Jean-de-Dieu", "Charles", "Pierre", "Luc", "Mathieu", "Alain", "Etienne", "Eric", "Compère", "Jean-de-Dieu", "Charles", "Pierre", "Luc", "Mathieu", "Alain", "Etienne", "Eric",
"Corentin", "Gaston", "Spirou", "Fantasio", "Mahmadou", "Mohamidou", "Vursuv", "Youssef" ); "Corentin", "Gaston", "Spirou", "Fantasio", "Mahmadou", "Mohamidou", "Vursuv", "Youssef" );
private $firstNamesFemale = array("Svedana", "Sevlatina", "Irène", "Marcelle", private $firstNamesFemale = array("Svedana", "Sevlatina", "Irène", "Marcelle",
"Corentine", "Alfonsine", "Caroline", "Solange", "Gostine", "Fatoumata", "Nicole", "Corentine", "Alfonsine", "Caroline", "Solange", "Gostine", "Fatoumata", "Nicole",
"Groseille", "Chana", "Oxana", "Ivana", "Julie", "Tina", "Adèle" ); "Groseille", "Chana", "Oxana", "Ivana", "Julie", "Tina", "Adèle" );
private $lastNames = array("Diallo", "Bah", "Gaillot", "Martin"); private $lastNames = array("Diallo", "Bah", "Gaillot", "Martin");
private $lastNamesTrigrams = array("fas", "tré", "hu", 'blart', 'van', 'der', 'lin', 'den', private $lastNamesTrigrams = array("fas", "tré", "hu", 'blart', 'van', 'der', 'lin', 'den',
'ta', 'mi', 'net', 'gna', 'bol', 'sac', 'ré', 'jo', 'du', 'pont', 'cas', 'tor', 'rob', 'al', 'ta', 'mi', 'net', 'gna', 'bol', 'sac', 'ré', 'jo', 'du', 'pont', 'cas', 'tor', 'rob', 'al',
'ma', 'gone', 'car',"fu", "ka", "lot", "no", "va", "du", "bu", "su", "jau", "tte", 'sir', 'ma', 'gone', 'car',"fu", "ka", "lot", "no", "va", "du", "bu", "su", "jau", "tte", 'sir',
"lo", 'to', "cho", "car", 'mo','zu', 'qi', 'mu'); "lo", 'to', "cho", "car", 'mo','zu', 'qi', 'mu');
private $genders = array(Person::MALE_GENDER, Person::FEMALE_GENDER); private $genders = array(Person::MALE_GENDER, Person::FEMALE_GENDER);
private $years = array(); private $years = array();
private $month = array(); private $month = array();
private $day = array(); private $day = array();
private $peoples = array( private $peoples = array(
array( array(
'LastName' => "Depardieu", 'LastName' => "Depardieu",
@ -362,21 +382,21 @@ class LoadPeople extends AbstractFixture implements OrderedFixtureInterface, Con
'maritalStatus' => 'ms_legalco' 'maritalStatus' => 'ms_legalco'
), ),
); );
private function addAccompanyingPeriods(Person $person, array $periods, ObjectManager $manager) private function addAccompanyingPeriods(Person $person, array $periods, ObjectManager $manager)
{ {
foreach ($periods as $period) { foreach ($periods as $period) {
echo "adding new past Accompanying Period..\n"; echo "adding new past Accompanying Period..\n";
/** @var AccompanyingPeriod $accompanyingPeriod */ /** @var AccompanyingPeriod $accompanyingPeriod */
$accompanyingPeriod = new AccompanyingPeriod(new \DateTime($period['from'])); $accompanyingPeriod = new AccompanyingPeriod(new \DateTime($period['from']));
$accompanyingPeriod $accompanyingPeriod
->setClosingDate(new \DateTime($period['to'])) ->setClosingDate(new \DateTime($period['to']))
->setRemark($period['remark']) ->setRemark($period['remark'])
; ;
$person->addAccompanyingPeriod($accompanyingPeriod); $person->addAccompanyingPeriod($accompanyingPeriod);
} }
} }

View File

@ -25,6 +25,7 @@ use Doctrine\Persistence\ObjectManager;
use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup; use Chill\MainBundle\DataFixtures\ORM\LoadPermissionsGroup;
use Chill\MainBundle\Entity\RoleScope; use Chill\MainBundle\Entity\RoleScope;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter;
/** /**
* Add a role CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE for all groups except administrative, * Add a role CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE for all groups except administrative,
@ -44,6 +45,7 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
{ {
foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) { foreach (LoadPermissionsGroup::$refs as $permissionsGroupRef) {
$permissionsGroup = $this->getReference($permissionsGroupRef); $permissionsGroup = $this->getReference($permissionsGroupRef);
$scopeSocial = $this->getReference('scope_social');
//create permission group //create permission group
switch ($permissionsGroup->getName()) { switch ($permissionsGroup->getName()) {
@ -51,6 +53,12 @@ class LoadPersonACL extends AbstractFixture implements OrderedFixtureInterface
case 'direction': case 'direction':
printf("Adding CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE to %s permission group \n", $permissionsGroup->getName()); printf("Adding CHILL_PERSON_UPDATE & CHILL_PERSON_CREATE to %s permission group \n", $permissionsGroup->getName());
$permissionsGroup->addRoleScope(
(new RoleScope())
->setRole(AccompanyingPeriodVoter::SEE)
->setScope($scopeSocial)
);
$roleScopeUpdate = (new RoleScope()) $roleScopeUpdate = (new RoleScope())
->setRole('CHILL_PERSON_UPDATE') ->setRole('CHILL_PERSON_UPDATE')
->setScope(null); ->setScope(null);

View File

@ -0,0 +1,82 @@
<?php
/*
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -0,0 +1,70 @@
<?php
/*
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -0,0 +1,90 @@
<?php
/*
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -0,0 +1,94 @@
<?php
/*
* Chill is a suite of a modules, Chill is a software for social workers
* Copyright (C) 2021, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -28,6 +28,7 @@ use Chill\MainBundle\DependencyInjection\MissingBundleException;
use Chill\PersonBundle\Security\Authorization\PersonVoter; use Chill\PersonBundle\Security\Authorization\PersonVoter;
use Chill\MainBundle\Security\Authorization\ChillExportVoter; use Chill\MainBundle\Security\Authorization\ChillExportVoter;
use Chill\PersonBundle\Doctrine\DQL\AddressPart; use Chill\PersonBundle\Doctrine\DQL\AddressPart;
use Symfony\Component\HttpFoundation\Request;
/** /**
* Class ChillPersonExtension * Class ChillPersonExtension
@ -72,10 +73,13 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
$loader->load('services/command.yaml'); $loader->load('services/command.yaml');
$loader->load('services/actions.yaml'); $loader->load('services/actions.yaml');
$loader->load('services/form.yaml'); $loader->load('services/form.yaml');
$loader->load('services/repository.yaml');
$loader->load('services/templating.yaml'); $loader->load('services/templating.yaml');
$loader->load('services/alt_names.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/serializer.yaml');
$loader->load('services/security.yaml');
// load service advanced search only if configure // load service advanced search only if configure
if ($config['search']['search_by_phone'] != 'never') { if ($config['search']['search_by_phone'] != 'never') {
@ -307,6 +311,55 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac
'template' => '@ChillPerson/MaritalStatus/edit.html.twig', 'template' => '@ChillPerson/MaritalStatus/edit.html.twig',
] ]
] ]
],
],
'apis' => [
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod::class,
'name' => 'accompanying_course',
'base_path' => '/api/1.0/person/accompanying-course',
'controller' => \Chill\PersonBundle\Controller\AccompanyingCourseApiController::class,
'actions' => [
'_entity' => [
'roles' => [
Request::METHOD_GET => \Chill\PersonBundle\Security\Authorization\AccompanyingPeriodVoter::SEE
]
],
'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
]
]
]
],
[
'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\Origin::class,
'name' => 'accompanying_period_origin',
'base_path' => '/api/1.0/person/accompanying-period/origin',
'controller' => \Chill\PersonBundle\Controller\OpeningApiController::class,
'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
]
],
]
] ]
] ]
]); ]);

View File

@ -37,7 +37,7 @@ use Chill\MainBundle\Entity\User;
/** /**
* AccompanyingPeriod Class * AccompanyingPeriod Class
* *
* @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\AccompanyingPeriodRepository") * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period") * @ORM\Table(name="chill_person_accompanying_period")
*/ */
class AccompanyingPeriod class AccompanyingPeriod
@ -118,7 +118,7 @@ class AccompanyingPeriod
* *
* @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class, * @ORM\OneToMany(targetEntity=AccompanyingPeriodParticipation::class,
* mappedBy="accompanyingPeriod", * mappedBy="accompanyingPeriod",
* cascade={"persist", "remove", "merge", "detach"}) * cascade={"persist", "refresh", "remove", "merge", "detach"})
*/ */
private $participations; private $participations;
@ -344,50 +344,68 @@ class AccompanyingPeriod
} }
/** /**
* This private function scan Participations Collection, * Get the participation containing a person
* searching for a given Person
*/ */
private function participationsContainsPerson(Person $person): ?AccompanyingPeriodParticipation public function getParticipationsContainsPerson(Person $person): Collection
{ {
foreach ($this->participations as $participation) { return $this->getParticipations($person)->filter(
/** @var AccompanyingPeriodParticipation $participation */ function(AccompanyingPeriodParticipation $participation) use ($person) {
if ($person === $participation->getPerson()) { if ($person === $participation->getPerson()) {
return $participation; return $participation;
}} }
});
return null;
} }
/** /**
* This public function is the same but return only true or false * Get the opened participation containing a person
*
* "Open" means that the closed date is NULL
*/
public function getOpenParticipationContainsPerson(Person $person): ?AccompanyingPeriodParticipation
{
$collection = $this->getParticipationsContainsPerson($person)->filter(
function(AccompanyingPeriodParticipation $participation) use ($person) {
if (NULL === $participation->getEndDate()) {
return $participation;
}
});
return $collection->count() > 0 ? $collection->first() : NULL;
}
/**
* Return true if the accompanying period contains a person.
*
* **Note**: this participation can be opened or not.
*/ */
public function containsPerson(Person $person): bool public function containsPerson(Person $person): bool
{ {
return ($this->participationsContainsPerson($person) === null) ? false : true; return $this->getParticipationsContainsPerson($person)->count() > 0;
} }
/** /**
* Add Person * Add Person
*/ */
public function addPerson(Person $person = null): self public function addPerson(Person $person = null): AccompanyingPeriodParticipation
{ {
$participation = new AccompanyingPeriodParticipation($this, $person); $participation = new AccompanyingPeriodParticipation($this, $person);
$this->participations[] = $participation; $this->participations[] = $participation;
return $this; return $participation;
} }
/** /**
* Remove Person * Remove Person
*/ */
public function removePerson(Person $person): void public function removePerson(Person $person): ?AccompanyingPeriodParticipation
{ {
$participation = $this->participationsContainsPerson($person); $participation = $this->getOpenParticipationContainsPerson($person);
if (! null === $participation) { if ($participation instanceof AccompanyingPeriodParticipation) {
$participation->setEndDate(new \DateTimeImmutable('now')); $participation->setEndDate(new \DateTimeImmutable('now'));
$this->participations->removeElement($participation);
} }
return $participation;
} }
@ -415,7 +433,7 @@ class AccompanyingPeriod
return false; return false;
} }
$participation = $this->participationsContainsPerson($person); $participation = $this->getParticipationsContainsPerson($person);
if (!null === $participation) if (!null === $participation)
{ {
$person = $participation->getPerson(); $person = $participation->getPerson();

View File

@ -4,7 +4,6 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\ThirdPartyBundle\Entity\ThirdParty; use Chill\ThirdPartyBundle\Entity\ThirdParty;
@ -13,7 +12,7 @@ use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=AccompanyingPeriodWorkRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_work") * @ORM\Table(name="chill_person_accompanying_period_work")
*/ */
class AccompanyingPeriodWork class AccompanyingPeriodWork

View File

@ -4,13 +4,12 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\SocialWork\Goal; use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkGoalRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=AccompanyingPeriodWorkGoalRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_work_goal") * @ORM\Table(name="chill_person_accompanying_period_work_goal")
*/ */
class AccompanyingPeriodWorkGoal class AccompanyingPeriodWorkGoal

View File

@ -29,8 +29,7 @@ use Doctrine\Common\Collections\ArrayCollection;
/** /**
* ClosingMotive give an explanation why we closed the Accompanying period * ClosingMotive give an explanation why we closed the Accompanying period
* *
* @ORM\Entity( * @ORM\Entity
* repositoryClass="Chill\PersonBundle\Repository\AccompanyingPeriod\ClosingMotiveRepository")
* @ORM\Table(name="chill_person_accompanying_period_closingmotive") * @ORM\Table(name="chill_person_accompanying_period_closingmotive")
*/ */
class ClosingMotive class ClosingMotive

View File

@ -22,13 +22,12 @@
namespace Chill\PersonBundle\Entity\AccompanyingPeriod; namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\CommentRepository;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=CommentRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_comment") * @ORM\Table(name="chill_person_accompanying_period_comment")
*/ */
class Comment class Comment

View File

@ -22,11 +22,10 @@
namespace Chill\PersonBundle\Entity\AccompanyingPeriod; namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\OriginRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=OriginRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_origin") * @ORM\Table(name="chill_person_accompanying_period_origin")
*/ */
class Origin class Origin
@ -53,7 +52,7 @@ class Origin
return $this->id; return $this->id;
} }
public function getLabel(): ?string public function getLabel()
{ {
return $this->label; return $this->label;
} }

View File

@ -22,7 +22,6 @@
namespace Chill\PersonBundle\Entity\AccompanyingPeriod; namespace Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Repository\AccompanyingPeriod\ResourceRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
@ -30,7 +29,7 @@ use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=ResourceRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_resource") * @ORM\Table(name="chill_person_accompanying_period_resource")
*/ */
class Resource class Resource

View File

@ -22,7 +22,6 @@
namespace Chill\PersonBundle\Entity; namespace Chill\PersonBundle\Entity;
use Chill\PersonBundle\Repository\AccompanyingPeriodParticipationRepository;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
@ -31,7 +30,7 @@ use Doctrine\ORM\Mapping as ORM;
* AccompanyingPeriodParticipation Class * AccompanyingPeriodParticipation Class
* *
* @package Chill\PersonBundle\Entity * @package Chill\PersonBundle\Entity
* @ORM\Entity(repositoryClass=AccompanyingPeriodParticipationRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_accompanying_period_participation") * @ORM\Table(name="chill_person_accompanying_period_participation")
*/ */
class AccompanyingPeriodParticipation class AccompanyingPeriodParticipation

View File

@ -0,0 +1,69 @@
<?php
namespace Chill\PersonBundle\Entity\Household;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Chill\MainBundle\Entity\Address;
/**
* @ORM\Entity
*/
class Household
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
public function getId(): ?int
{
return $this->id;
}
/**
* Addresses
* @var Collection
*
* @ORM\ManyToMany(
* targetEntity="Chill\MainBundle\Entity\Address",
* cascade={"persist", "remove", "merge", "detach"})
* @ORM\JoinTable(name="chill_person_household_to_addresses")
* @ORM\OrderBy({"validFrom" = "DESC"})
*/
private $addresses;
/**
* @param Address $address
* @return $this
*/
public function addAddress(Address $address)
{
$this->addresses[] = $address;
return $this;
}
/**
* @param Address $address
*/
public function removeAddress(Address $address)
{
$this->addresses->removeElement($address);
}
/**
* By default, the addresses are ordered by date, descending (the most
* recent first)
*
* @return \Chill\MainBundle\Entity\Address[]
*/
public function getAddresses()
{
return $this->addresses;
}
}

View File

@ -0,0 +1,152 @@
<?php
namespace Chill\PersonBundle\Entity\Household;
use Doctrine\ORM\Mapping as ORM;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Household\Household;
/**
* @ORM\Entity
*/
class HouseholdMembers
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $position;
/**
* @ORM\Column(type="date")
*/
private $startDate;
/**
* @ORM\Column(type="date")
*/
private $endDate;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $comment;
/**
* @ORM\Column(type="boolean")
*/
private $sharedHousehold;
/**
*
* @var Person
* @ORM\ManyToOne(
* targetEntity="\Chill\PersonBundle\Entity\Person"
* )
*/
private $person;
/**
*
* @var Household
* @ORM\ManyToOne(
* targetEntity="\Chill\PersonBundle\Entity\Household\Household"
* )
*/
private $household;
public function getId(): ?int
{
return $this->id;
}
public function getPosition(): ?string
{
return $this->position;
}
public function setPosition(?string $position): self
{
$this->position = $position;
return $this;
}
public function getStartDate(): ?\DateTimeInterface
{
return $this->startDate;
}
public function setStartDate(\DateTimeInterface $startDate): self
{
$this->startDate = $startDate;
return $this;
}
public function getEndDate(): ?\DateTimeInterface
{
return $this->endDate;
}
public function setEndDate(\DateTimeInterface $endDate): self
{
$this->endDate = $endDate;
return $this;
}
public function getComment(): ?string
{
return $this->comment;
}
public function setComment(?string $comment): self
{
$this->comment = $comment;
return $this;
}
public function getSharedHousehold(): ?bool
{
return $this->sharedHousehold;
}
public function setSharedHousehold(bool $sharedHousehold): self
{
$this->sharedHousehold = $sharedHousehold;
return $this;
}
public function getPerson(): ?Person
{
return $this->person;
}
public function setPerson(?Person $person): self
{
$this->person = $person;
return $this;
}
public function getHousehold(): ?Household
{
return $this->household;
}
public function setHousehold(?Household $household): self
{
$this->household = $household;
return $this;
}
}

View File

@ -25,7 +25,7 @@ use Doctrine\ORM\Mapping as ORM;
/** /**
* MaritalStatus * MaritalStatus
* *
* @ORM\Entity() * @ORM\Entity
* @ORM\Table(name="chill_person_marital_status") * @ORM\Table(name="chill_person_marital_status")
* @ORM\HasLifecycleCallbacks() * @ORM\HasLifecycleCallbacks()
*/ */

View File

@ -38,7 +38,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
/** /**
* Person Class * Person Class
* *
* @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonRepository") * @ORM\Entity
* @ORM\Table(name="chill_person_person", * @ORM\Table(name="chill_person_person",
* indexes={@ORM\Index( * indexes={@ORM\Index(
* name="person_names", * name="person_names",
@ -390,7 +390,7 @@ class Person implements HasCenterInterface
* *
* @deprecated since 1.1 use `getOpenedAccompanyingPeriod instead * @deprecated since 1.1 use `getOpenedAccompanyingPeriod instead
*/ */
public function getCurrentAccompanyingPeriod() : AccompanyingPeriod public function getCurrentAccompanyingPeriod() : ?AccompanyingPeriod
{ {
return $this->getOpenedAccompanyingPeriod(); return $this->getOpenedAccompanyingPeriod();
} }

View File

@ -8,7 +8,7 @@ use Doctrine\ORM\Mapping as ORM;
* PersonAltName * PersonAltName
* *
* @ORM\Table(name="chill_person_alt_name") * @ORM\Table(name="chill_person_alt_name")
* @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonAltNameRepository") * @ORM\Entity
*/ */
class PersonAltName class PersonAltName
{ {

View File

@ -9,7 +9,7 @@ use Chill\MainBundle\Entity\User;
* PersonNotDuplicate * PersonNotDuplicate
* *
* @ORM\Table(name="chill_person_not_duplicate") * @ORM\Table(name="chill_person_not_duplicate")
* @ORM\Entity(repositoryClass="Chill\PersonBundle\Repository\PersonNotDuplicateRepository") * @ORM\Entity
*/ */
class PersonNotDuplicate class PersonNotDuplicate
{ {

View File

@ -8,7 +8,7 @@ use Doctrine\ORM\Mapping as ORM;
/** /**
* Person Phones * Person Phones
* *
* @ORM\Entity() * @ORM\Entity
* @ORM\Table(name="chill_person_phone") * @ORM\Table(name="chill_person_phone")
*/ */
class PersonPhone class PersonPhone

View File

@ -2,11 +2,10 @@
namespace Chill\PersonBundle\Entity\SocialWork; namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\EvaluationRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=EvaluationRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_social_work_evaluation") * @ORM\Table(name="chill_person_social_work_evaluation")
*/ */
class Evaluation class Evaluation

View File

@ -2,13 +2,12 @@
namespace Chill\PersonBundle\Entity\SocialWork; namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\GoalRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=GoalRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_social_work_goal") * @ORM\Table(name="chill_person_social_work_goal")
*/ */
class Goal class Goal

View File

@ -4,13 +4,12 @@ namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
use Chill\PersonBundle\Repository\SocialWork\ResultRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=ResultRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_social_work_result") * @ORM\Table(name="chill_person_social_work_result")
*/ */
class Result class Result

View File

@ -2,13 +2,12 @@
namespace Chill\PersonBundle\Entity\SocialWork; namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\SocialActionRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=SocialActionRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_social_action") * @ORM\Table(name="chill_person_social_action")
*/ */
class SocialAction class SocialAction

View File

@ -1,14 +1,12 @@
<?php <?php
namespace Chill\PersonBundle\Entity\SocialWork; namespace Chill\PersonBundle\Entity\SocialWork;
use Chill\PersonBundle\Repository\SocialWork\SocialIssueRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* @ORM\Entity(repositoryClass=SocialIssueRepository::class) * @ORM\Entity
* @ORM\Table(name="chill_person_social_issue") * @ORM\Table(name="chill_person_social_issue")
*/ */
class SocialIssue class SocialIssue

View File

@ -33,9 +33,10 @@ use Chill\MainBundle\Form\Type\DataTransformer\CenterTransformer;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper; use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Form\Type\PersonAltNameType; 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 NAME = 'chill_personbundle_person_creation';
const FORM_NOT_REVIEWED = 'not_reviewed'; const FORM_NOT_REVIEWED = 'not_reviewed';

View File

@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod; namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method AccompanyingPeriodWorkGoal|null find($id, $lockMode = null, $lockVersion = null) * @method AccompanyingPeriodWorkGoal|null find($id, $lockMode = null, $lockVersion = null)
@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriodWorkGoal[] findAll() * @method AccompanyingPeriodWorkGoal[] findAll()
* @method AccompanyingPeriodWorkGoal[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod; namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method AccompanyingPeriodWork|null find($id, $lockMode = null, $lockVersion = null) * @method AccompanyingPeriodWork|null find($id, $lockMode = null, $lockVersion = null)
@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriodWork[] findAll() * @method AccompanyingPeriodWork[] findAll()
* @method AccompanyingPeriodWork[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -22,8 +22,9 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod; namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\ClosingMotive;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\ORM\Query\ResultSetMappingBuilder;
/** /**
@ -32,17 +33,24 @@ use Doctrine\ORM\Query\ResultSetMappingBuilder;
* *
* @package Chill\PersonBundle\Repository * @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 * @param bool $onlyLeaf
* @return mixed * @return mixed
*/ */
public function getActiveClosingMotive(bool $onlyLeaf = true) public function getActiveClosingMotive(bool $onlyLeaf = true)
{ {
$rsm = new ResultSetMappingBuilder($this->getEntityManager()); $rsm = new ResultSetMappingBuilder($this->repository->getEntityManager());
$rsm->addRootEntityFromClassMetadata($this->getClassName(), 'cm'); $rsm->addRootEntityFromClassMetadata($this->repository->getClassName(), 'cm');
$sql = "SELECT ".(string) $rsm." $sql = "SELECT ".(string) $rsm."
FROM chill_person_accompanying_period_closingmotive AS cm FROM chill_person_accompanying_period_closingmotive AS cm
WHERE WHERE
@ -55,10 +63,11 @@ class ClosingMotiveRepository extends EntityRepository
} }
$sql .= " ORDER BY cm.ordering ASC"; $sql .= " ORDER BY cm.ordering ASC";
return $this->_em return $this
->repository
->getEntityManager()
->createNativeQuery($sql, $rsm) ->createNativeQuery($sql, $rsm)
->getResult() ->getResult();
;
} }
} }

View File

@ -23,8 +23,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod; namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment; use Chill\PersonBundle\Entity\AccompanyingPeriod\Comment;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method Comment|null find($id, $lockMode = null, $lockVersion = null) * @method Comment|null find($id, $lockMode = null, $lockVersion = null)
@ -32,11 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Comment[] findAll() * @method Comment[] findAll()
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -23,8 +23,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod; namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin; use Chill\PersonBundle\Entity\AccompanyingPeriod\Origin;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method Origin|null find($id, $lockMode = null, $lockVersion = null) * @method Origin|null find($id, $lockMode = null, $lockVersion = null)
@ -32,11 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Origin[] findAll() * @method Origin[] findAll()
* @method Origin[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @method Origin[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class OriginRepository extends ServiceEntityRepository final class OriginRepository
{ {
public function __construct(ManagerRegistry $registry) private EntityRepository $repository;
{
parent::__construct($registry, Origin::class);
}
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Origin::class);
}
} }

View File

@ -23,8 +23,8 @@
namespace Chill\PersonBundle\Repository\AccompanyingPeriod; namespace Chill\PersonBundle\Repository\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource; use Chill\PersonBundle\Entity\AccompanyingPeriod\Resource;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method Resource|null find($id, $lockMode = null, $lockVersion = null) * @method Resource|null find($id, $lockMode = null, $lockVersion = null)
@ -32,11 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Resource[] findAll() * @method Resource[] findAll()
* @method Resource[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @method Resource[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/ */
class ResourceRepository extends ServiceEntityRepository final class ResourceRepository
{ {
public function __construct(ManagerRegistry $registry) private EntityRepository $repository;
{
parent::__construct($registry, Resource::class);
}
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Resource::class);
}
} }

View File

@ -23,8 +23,8 @@
namespace Chill\PersonBundle\Repository; namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation; use Chill\PersonBundle\Entity\AccompanyingPeriodParticipation;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null) * @method AccompanyingPeriodParticipation|null find($id, $lockMode = null, $lockVersion = null)
@ -32,11 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriodParticipation[] findAll() * @method AccompanyingPeriodParticipation[] findAll()
* @method AccompanyingPeriodParticipation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -23,9 +23,8 @@
namespace Chill\PersonBundle\Repository; namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/** /**
* @method AccompanyingPeriod|null find($id, $lockMode = null, $lockVersion = null) * @method AccompanyingPeriod|null find($id, $lockMode = null, $lockVersion = null)
@ -33,10 +32,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method AccompanyingPeriod[] findAll() * @method AccompanyingPeriod[] findAll()
* @method AccompanyingPeriod[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -0,0 +1,52 @@
<?php
namespace Chill\PersonBundle\Repository\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMembers;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
/**
* @method HouseholdMembers|null find($id, $lockMode = null, $lockVersion = null)
* @method HouseholdMembers|null findOneBy(array $criteria, array $orderBy = null)
* @method HouseholdMembers[] findAll()
* @method HouseholdMembers[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
final class HouseholdMembersRepository
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(HouseholdMembers::class);
}
// /**
// * @return HouseholdMembers[] Returns an array of HouseholdMembers objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('h')
->andWhere('h.exampleField = :val')
->setParameter('val', $value)
->orderBy('h.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?HouseholdMembers
{
return $this->createQueryBuilder('h')
->andWhere('h.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,52 @@
<?php
namespace Chill\PersonBundle\Repository\Household;
use Chill\PersonBundle\Entity\Household\Household;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
/**
* @method Household|null find($id, $lockMode = null, $lockVersion = null)
* @method Household|null findOneBy(array $criteria, array $orderBy = null)
* @method Household[] findAll()
* @method Household[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
final class HouseholdRepository
{
private EntityRepository $repository;
public function __construct(EntityManagerInterface $entityManager)
{
$this->repository = $entityManager->getRepository(Household::class);
}
// /**
// * @return Household[] Returns an array of Household objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('h')
->andWhere('h.exampleField = :val')
->setParameter('val', $value)
->orderBy('h.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Household
{
return $this->createQueryBuilder('h')
->andWhere('h.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -2,12 +2,22 @@
namespace Chill\PersonBundle\Repository; namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\PersonAltName;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
/** /**
* PersonAltNameRepository * PersonAltNameRepository
* *
* This class was generated by the Doctrine ORM. Add your own custom * This class was generated by the Doctrine ORM. Add your own custom
* repository methods below. * 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);
}
} }

View File

@ -4,6 +4,7 @@ namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonNotDuplicate; use Chill\PersonBundle\Entity\PersonNotDuplicate;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
/** /**
@ -11,8 +12,15 @@ use Doctrine\ORM\EntityRepository;
* *
* @package Chill\PersonBundle\Repository * @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 * @param \Chill\PersonBundle\Entity\Person $person
* *
@ -20,7 +28,7 @@ class PersonNotDuplicateRepository extends EntityRepository
*/ */
public function findNotDuplicatePerson(Person $person) public function findNotDuplicatePerson(Person $person)
{ {
$qb = $this->createQueryBuilder('pnd'); $qb = $this->repository->createQueryBuilder('pnd');
$qb->select('pnd') $qb->select('pnd')
->where('pnd.person1 = :person OR pnd.person2 = :person') ->where('pnd.person1 = :person OR pnd.person2 = :person')
; ;

View File

@ -18,16 +18,25 @@
namespace Chill\PersonBundle\Repository; namespace Chill\PersonBundle\Repository;
use Chill\PersonBundle\Entity\Person;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository; use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
/** final class PersonRepository
* Class PersonRepository
*
* @package Chill\PersonBundle\Repository
*/
class PersonRepository extends EntityRepository
{ {
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 string $phonenumber
* @param $centers * @param $centers
@ -44,7 +53,7 @@ class PersonRepository extends EntityRepository
$maxResults, $maxResults,
array $only = ['mobile', 'phone'] array $only = ['mobile', 'phone']
) { ) {
$qb = $this->createQueryBuilder('p'); $qb = $this->repository->createQueryBuilder('p');
$qb->select('p'); $qb->select('p');
$this->addByCenters($qb, $centers); $this->addByCenters($qb, $centers);
@ -71,7 +80,7 @@ class PersonRepository extends EntityRepository
array $only = ['mobile', 'phone'] array $only = ['mobile', 'phone']
): int ): int
{ {
$qb = $this->createQueryBuilder('p'); $qb = $this->repository->createQueryBuilder('p');
$qb->select('COUNT(p)'); $qb->select('COUNT(p)');
$this->addByCenters($qb, $centers); $this->addByCenters($qb, $centers);

View File

@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork; namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Evaluation; use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method Evaluation|null find($id, $lockMode = null, $lockVersion = null) * @method Evaluation|null find($id, $lockMode = null, $lockVersion = null)
@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Evaluation[] findAll() * @method Evaluation[] findAll()
* @method Evaluation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork; namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Goal; use Chill\PersonBundle\Entity\SocialWork\Goal;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method Goal|null find($id, $lockMode = null, $lockVersion = null) * @method Goal|null find($id, $lockMode = null, $lockVersion = null)
@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Goal[] findAll() * @method Goal[] findAll()
* @method Goal[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork; namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\Result;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method Result|null find($id, $lockMode = null, $lockVersion = null) * @method Result|null find($id, $lockMode = null, $lockVersion = null)
@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method Result[] findAll() * @method Result[] findAll()
* @method Result[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -3,8 +3,8 @@
namespace Chill\PersonBundle\Repository\SocialWork; namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\ORM\EntityRepository;
/** /**
* @method SocialAction|null find($id, $lockMode = null, $lockVersion = null) * @method SocialAction|null find($id, $lockMode = null, $lockVersion = null)
@ -12,10 +12,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method SocialAction[] findAll() * @method SocialAction[] findAll()
* @method SocialAction[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -4,7 +4,8 @@ namespace Chill\PersonBundle\Repository\SocialWork;
use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\PersonBundle\Entity\SocialWork\SocialIssue;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; 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) * @method SocialIssue|null find($id, $lockMode = null, $lockVersion = null)
@ -12,10 +13,12 @@ use Doctrine\Persistence\ManagerRegistry;
* @method SocialIssue[] findAll() * @method SocialIssue[] findAll()
* @method SocialIssue[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) * @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);
} }
} }

View File

@ -1,45 +0,0 @@
<template>
<accompanying-course v-bind:accompanying_course="accompanying_course"/>
<persons-associated v-bind:persons_associated="accompanying_course.persons"/>
<requestor v-bind:accompanying_course="accompanying_course"/>
</template>
<script>
import AccompanyingCourse from './components/AccompanyingCourse.vue';
import PersonsAssociated from './components/PersonsAssociated.vue';
import Requestor from './components/Requestor.vue';
export default {
name: 'App',
components: {
AccompanyingCourse,
PersonsAssociated,
Requestor
},
data() {
return {
accompanying_course: {}
};
},
computed: {
accompanyingCourseId() {
return window.accompanyingCourseId;
}
},
methods: {
async getAccompanyingCourse() {
let data_;
return fetch(`/fr/api/parcours/${accompanyingCourseId}/show`)
.then(response => response.json())
.then(data => {
this.$data.accompanying_course = data;
});
}
},
async mounted() {
await this.getAccompanyingCourse();
}
};
</script>
<style scoped></style>

View File

@ -1,26 +0,0 @@
<template>
<div class="vue-component">
<h3>Parcours</h3>
<dl>
<dt>id</dt>
<dd>{{ accompanying_course.id }}</dd>
<dt>opening_date</dt>
<dd>{{ accompanying_course.opening_date }}</dd>
<dt>closing_date</dt>
<dd>{{ accompanying_course.closing_date }}</dd>
<dt>remark</dt>
<dd>{{ accompanying_course.remark }}</dd>
<dt>closing_motive</dt>
<dd>{{ accompanying_course.closing_motive }}</dd>
</dl>
</div>
</template>
<script>
export default {
name: 'AccompanyingCourse',
props: {
accompanying_course: Object
}
}
</script>

View File

@ -1,25 +0,0 @@
<template>
<tr>
<td>{{ person.firstname }}</td>
<td>{{ person.lastname }}</td>
<td>{{ person.startdate }}</td>
<td>{{ person.enddate }}</td>
<td>
<ul class="record_actions">
<li><button class="sc-button bt-show"></button></li>
<li><button class="sc-button bt-update"></button></li>
<li><button class="sc-button bt-delete" @click.prevent="$emit('remove', person)"></button></li>
</ul>
</td>
</tr>
</template>
<script>
export default {
name: 'PersonItem',
props: {
person: { type: Object, required: true }
},
emits: ['remove']
}
</script>

View File

@ -1,69 +0,0 @@
<template>
<div class="vue-component">
<h3>Usagers concernés</h3>
<label>{{ counter }} usagers</label>
<table class="rounded">
<thead>
<tr>
<th class="chill-orange">firstname</th>
<th class="chill-orange">lastname</th>
<th class="chill-orange">startdate</th>
<th class="chill-orange">enddate</th>
<th class="chill-orange">actions</th>
</tr>
</thead>
<tbody>
<person-item
v-for="person in persons_associated"
v-bind:person="person"
v-bind:key="person.id"
@remove="removePerson" />
</tbody>
</table>
<ul class="record_actions">
<li><button class="sc-button bt-create" @click="addPerson">Ajouter un usager</button></li>
</ul>
</div>
</template>
<script>
import PersonItem from "./PersonItem.vue"
export default {
name: 'PersonsAssociated',
components: {
PersonItem
},
props: {
persons_associated: Array
},
data() {
return {
persons: this.persons_associated
}
},
computed: {
async counter() {
// Pourquoi je peux pas compter un tableau avec length ???!!!
return this.persons_associated.length // <= boum !
}
},
methods: {
addPerson() {
this.persons_associated.push({
"firstname": "Lisa",
"lastname": "Simpson",
"startdate": "1975-09-15",
"enddate": "2021-04-20"
})
},
removePerson(item) {
this.persons_associated = this.persons_associated.filter(person => person !== item)
}
}
}
</script>

View File

@ -1,16 +0,0 @@
<template>
<div class="vue-component">
<h3>Demandeur</h3>
{{ accompanying_course.id }}
{{ accompanying_course.remark }}
</div>
</template>
<script>
export default {
name: 'Requestor',
props: {
accompanying_course: Object
}
}
</script>

View File

@ -1,8 +0,0 @@
import App from './App.vue';
import { createApp } from 'vue';
const app = createApp({
template: `<app></app>`
})
.component('app', App)
.mount('#accompanying-course');

Some files were not shown because too many files have changed in this diff Show More