mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch 'master' into export/allow-check-multiple-geographical-zones
This commit is contained in:
commit
8bbca7e61a
@ -34,6 +34,7 @@
|
||||
"sensio/framework-extra-bundle": "^5.5",
|
||||
"spomky-labs/base64url": "^2.0",
|
||||
"symfony/browser-kit": "^4.4",
|
||||
"symfony/clock": "^6.2",
|
||||
"symfony/css-selector": "^4.4",
|
||||
"symfony/expression-language": "^4.4",
|
||||
"symfony/form": "^4.4",
|
||||
|
@ -90,9 +90,7 @@ class CountPerson implements ExportInterface
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
// we gather all center the user choose.
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
|
203
docs/source/development/entity-info.rst
Normal file
203
docs/source/development/entity-info.rst
Normal file
@ -0,0 +1,203 @@
|
||||
|
||||
.. 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".
|
||||
|
||||
.. _entity-info:
|
||||
|
||||
Stats about event on entity in php world
|
||||
########################################
|
||||
|
||||
It is necessary to be able to gather information about events for some entities:
|
||||
|
||||
- when the event has been done;
|
||||
- who did it;
|
||||
- ...
|
||||
|
||||
Those "infos" are not linked with right management, like describe in :ref:`timelines`.
|
||||
|
||||
|
||||
“infos” for some stats and info about an entity
|
||||
-----------------------------------------------
|
||||
|
||||
Building an info means:
|
||||
|
||||
- create an Entity, and map this entity to a SQL view (not a regular table);
|
||||
- use the framework to build this entity dynamically.
|
||||
|
||||
A framework api is built to be able to build multiple “infos” entities
|
||||
through “union” views:
|
||||
|
||||
- use a command ``bin/console chill:db:sync-views`` to synchronize view (create view if it does not exists, or update
|
||||
views when new SQL parts are added in the UNION query. Internally, this command call a new ``ViewEntityInfoManager``,
|
||||
which iterate over available views to build the SQL;
|
||||
- one can create a new “view entity info” by implementing a
|
||||
``ViewEntityInfoProviderInterface``
|
||||
- this implementation of the interface is free to create another
|
||||
interface for building each part of the UNION query. This interface
|
||||
is created for AccompanyingPeriodInfo:
|
||||
``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``
|
||||
|
||||
So, converting new “events” into rows for ``AccompanyingPeriodInfo`` is
|
||||
just implementing this interface!
|
||||
|
||||
Implementation for AccompanyingPeriod (``AccompanyingPeriod/AccompanyingPeriodInfo``)
|
||||
-------------------------------------------------------------------------------------
|
||||
|
||||
A class is created for computing some statistical info for an
|
||||
AccompanyingPeriod: ``AccompanyingPeriod/AccompanyingPeriodInfo``. This
|
||||
contains information about “something happens”, who did it and when.
|
||||
|
||||
Having those info in table answer some questions like:
|
||||
|
||||
- when is the last and the first action (AccompanyingPeriodWork,
|
||||
Activity, AccompanyingPeriodWorkEvaluation, …) on the period;
|
||||
- who is “acting” on the period, and when is the last “action” for each
|
||||
user.
|
||||
|
||||
The AccompanyingPeriod info is mapped to a SQL view, not a table. The
|
||||
sql view is built dynamically (see below), and gather infos from
|
||||
ActivityBundle, PersonBundle, CalendarBundle, … It is possible to create
|
||||
custom bundle and add info on this view.
|
||||
|
||||
.. code:: php
|
||||
|
||||
/**
|
||||
*
|
||||
* @ORM\Entity()
|
||||
* @ORM\Table(name="view_chill_person_accompanying_period_info") <==== THIS IS A VIEW, NOT A TABLE
|
||||
*/
|
||||
class AccompanyingPeriodInfo
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
Why do we need this ?
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For multiple jobs in PHP world:
|
||||
|
||||
- moving the accompanying period to another steps when inactive,
|
||||
automatically;
|
||||
- listing all the users which are intervening on the action on a new
|
||||
“Liste des intervenants” page;
|
||||
- filtering on exports
|
||||
|
||||
Later, we will launch automatic anonymise for accompanying period and
|
||||
all related entities through this information.
|
||||
|
||||
How is built the SQL views which is mapped to “info” entities ?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The AccompanyingPeriodInfo entity is mapped by a SQL view (not a regular
|
||||
table).
|
||||
|
||||
The sql view is built dynamically, it is a SQL view like this, for now (April 2023):
|
||||
|
||||
.. code:: sql
|
||||
|
||||
create view view_chill_person_accompanying_period_info
|
||||
(accompanyingperiod_id, relatedentity, relatedentityid, user_id, infodate, discriminator, metadata) as
|
||||
SELECT w.accompanyingperiod_id,
|
||||
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'::text AS relatedentity,
|
||||
w.id AS relatedentityid,
|
||||
cpapwr.user_id,
|
||||
w.enddate AS infodate,
|
||||
'accompanying_period_work_end'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM chill_person_accompanying_period_work w
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id
|
||||
WHERE w.enddate IS NOT NULL
|
||||
UNION
|
||||
SELECT cpapw.accompanyingperiod_id,
|
||||
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
|
||||
e.id AS relatedentityid,
|
||||
e.updatedby_id AS user_id,
|
||||
e.updatedat AS infodate,
|
||||
'accompanying_period_work_evaluation_updated_at'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM chill_person_accompanying_period_work_evaluation e
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
WHERE e.updatedat IS NOT NULL
|
||||
UNION
|
||||
SELECT cpapw.accompanyingperiod_id,
|
||||
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
|
||||
e.id AS relatedentityid,
|
||||
cpapwr.user_id,
|
||||
e.maxdate AS infodate,
|
||||
'accompanying_period_work_evaluation_start'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM chill_person_accompanying_period_work_evaluation e
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
|
||||
WHERE e.maxdate IS NOT NULL
|
||||
UNION
|
||||
SELECT cpapw.accompanyingperiod_id,
|
||||
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
|
||||
e.id AS relatedentityid,
|
||||
cpapwr.user_id,
|
||||
e.startdate AS infodate,
|
||||
'accompanying_period_work_evaluation_start'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM chill_person_accompanying_period_work_evaluation e
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
|
||||
UNION
|
||||
SELECT cpapw.accompanyingperiod_id,
|
||||
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument'::text AS relatedentity,
|
||||
doc.id AS relatedentityid,
|
||||
doc.updatedby_id AS user_id,
|
||||
doc.updatedat AS infodate,
|
||||
'accompanying_period_work_evaluation_document_updated_at'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM chill_person_accompanying_period_work_evaluation_document doc
|
||||
JOIN chill_person_accompanying_period_work_evaluation e ON doc.accompanyingperiodworkevaluation_id = e.id
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
WHERE doc.updatedat IS NOT NULL
|
||||
UNION
|
||||
SELECT cpapw.accompanyingperiod_id,
|
||||
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation'::text AS relatedentity,
|
||||
e.id AS relatedentityid,
|
||||
cpapwr.user_id,
|
||||
e.maxdate AS infodate,
|
||||
'accompanying_period_work_evaluation_max'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM chill_person_accompanying_period_work_evaluation e
|
||||
JOIN chill_person_accompanying_period_work cpapw ON cpapw.id = e.accompanyingperiodwork_id
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON cpapw.id = cpapwr.accompanyingperiodwork_id
|
||||
WHERE e.maxdate IS NOT NULL
|
||||
UNION
|
||||
SELECT w.accompanyingperiod_id,
|
||||
'Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork'::text AS relatedentity,
|
||||
w.id AS relatedentityid,
|
||||
cpapwr.user_id,
|
||||
w.startdate AS infodate,
|
||||
'accompanying_period_work_start'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM chill_person_accompanying_period_work w
|
||||
LEFT JOIN chill_person_accompanying_period_work_referrer cpapwr ON w.id = cpapwr.accompanyingperiodwork_id
|
||||
UNION
|
||||
SELECT activity.accompanyingperiod_id,
|
||||
'Chill\ActivityBundle\Entity\Activity'::text AS relatedentity,
|
||||
activity.id AS relatedentityid,
|
||||
au.user_id,
|
||||
activity.date AS infodate,
|
||||
'activity_date'::text AS discriminator,
|
||||
'{}'::jsonb AS metadata
|
||||
FROM activity
|
||||
LEFT JOIN activity_user au ON activity.id = au.activity_id
|
||||
WHERE activity.accompanyingperiod_id IS NOT NULL;
|
||||
|
||||
As you can see, the view gather multiple SELECT queries and bind them
|
||||
with UNION.
|
||||
|
||||
Each SELECT query is built dynamically, through a class implementing an
|
||||
interface: ``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``, `like
|
||||
here <https://gitlab.com/Chill-Projet/chill-bundles/-/blob/master/src/Bundle/ChillPersonBundle/Service/EntityInfo/AccompanyingPeriodInfoQueryPart/AccompanyingPeriodWorkEndQueryPartForAccompanyingPeriodInfo.php>`__
|
||||
|
||||
To add new `SELECT` query in different `UNION` parts in the sql view, create a
|
||||
service and implements this interface: ``Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface``.
|
@ -35,6 +35,7 @@ As Chill rely on the `symfony <http://symfony.com>`_ framework, reading the fram
|
||||
manual/index.rst
|
||||
Assets <assets.rst>
|
||||
Cron Jobs <cronjob.rst>
|
||||
Info about entities <entity-info.rst>
|
||||
|
||||
Layout and UI
|
||||
**************
|
||||
|
@ -6,6 +6,8 @@
|
||||
A copy of the license is included in the section entitled "GNU
|
||||
Free Documentation License".
|
||||
|
||||
.. _timelines:
|
||||
|
||||
Timelines
|
||||
*********
|
||||
|
||||
@ -18,24 +20,24 @@ Concept
|
||||
From an user point of view
|
||||
--------------------------
|
||||
|
||||
Chill has two objectives :
|
||||
Chill has two objectives :
|
||||
|
||||
* make the administrative tasks more lightweight ;
|
||||
* help social workers to have all information they need to work
|
||||
|
||||
To reach this second objective, Chill provides a special view: **timeline**. On a timeline view, information is gathered and shown on a single page, from the most recent event to the oldest one.
|
||||
|
||||
The information gathered is linked to a *context*. This *context* may be, for instance :
|
||||
The information gathered is linked to a *context*. This *context* may be, for instance :
|
||||
|
||||
* a person : events linked to this person are shown on the page ;
|
||||
* a center: events linked to a center are shown. They may concern different peoples ;
|
||||
* ...
|
||||
* ...
|
||||
|
||||
In other word, the *context* is the kind of argument that will be used in the event's query.
|
||||
|
||||
Let us recall that only the data the user has allowed to see should be shown.
|
||||
|
||||
.. seealso::
|
||||
.. seealso::
|
||||
|
||||
`The issue where the subject was first discussed <https://redmine.champs-libres.coop/issues/224>`_
|
||||
|
||||
@ -43,30 +45,30 @@ Let us recall that only the data the user has allowed to see should be shown.
|
||||
For developers
|
||||
--------------
|
||||
|
||||
The `Main` bundle provides interfaces and services to help to build timelines.
|
||||
The `Main` bundle provides interfaces and services to help to build timelines.
|
||||
|
||||
If a bundle wants to *push* information in a timeline, it should be create a service which implements `Chill\MainBundle\Timeline\TimelineProviderInterface`, and tag is with `chill.timeline` and arguments defining the supported context (you may use multiple `chill.timeline` tags in order to support multiple context with a single service/class).
|
||||
|
||||
If a bundle wants to provide a new context for a timeline, the service `chill.main.timeline_builder` will helps to gather timeline's services supporting the defined context, and run queries across the models.
|
||||
If a bundle wants to provide a new context for a timeline, the service `chill.main.timeline_builder` will helps to gather timeline's services supporting the defined context, and run queries across the models.
|
||||
|
||||
.. _understanding-queries :
|
||||
|
||||
Understanding queries
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Due to the fact that timelines should show only the X last events from Y differents tables, queries for a timeline may consume a lot of resources: at first on the database, and then on the ORM part, which will have to deserialize DB data to PHP classes, which may not be used if they are not part of the "last X events".
|
||||
Due to the fact that timelines should show only the X last events from Y differents tables, queries for a timeline may consume a lot of resources: at first on the database, and then on the ORM part, which will have to deserialize DB data to PHP classes, which may not be used if they are not part of the "last X events".
|
||||
|
||||
To avoid such load on database, the objects are queried in two steps :
|
||||
To avoid such load on database, the objects are queried in two steps :
|
||||
|
||||
1. An UNION request which gather the last X events, ordered by date. The data retrieved are the ID, the date, and a string key: a type. This type discriminates the data type.
|
||||
2. The PHP objects are queried by ID, the type helps the program to link id with the kind of objects.
|
||||
2. The PHP objects are queried by ID, the type helps the program to link id with the kind of objects.
|
||||
|
||||
Those methods should ensure that only X PHP objects will be gathered and build by the ORM.
|
||||
|
||||
What does the master timeline builder service ?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When the service `chill.main.timeline_builder` is instanciated, the service is informed of each service taggued with `chill.timeline` tags. Then,
|
||||
When the service `chill.main.timeline_builder` is instanciated, the service is informed of each service taggued with `chill.timeline` tags. Then,
|
||||
|
||||
1. The service build an UNION query by assembling column and tables names provided by the `fetchQuery` result ;
|
||||
2. The UNION query is run, the result contains an id and a type for each row (see :ref:`above <understanding-queries>`)
|
||||
@ -84,7 +86,7 @@ To push events on a timeline :
|
||||
Implementing the TimelineProviderInterface
|
||||
------------------------------------------
|
||||
|
||||
The has the following signature :
|
||||
The has the following signature :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -92,19 +94,19 @@ The has the following signature :
|
||||
|
||||
interface TimelineProviderInterface
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $context
|
||||
* @param mixed[] $args the argument to the context.
|
||||
* @return TimelineSingleQuery
|
||||
* @throw \LogicException if the context is not supported
|
||||
*/
|
||||
public function fetchQuery($context, array $args);
|
||||
|
||||
|
||||
/**
|
||||
* Indicate if the result type may be handled by the service
|
||||
*
|
||||
*
|
||||
* @param string $type the key present in the SELECT query
|
||||
* @return boolean
|
||||
*/
|
||||
@ -113,42 +115,42 @@ The has the following signature :
|
||||
/**
|
||||
* fetch entities from db into an associative array. The keys **MUST BE**
|
||||
* the id
|
||||
*
|
||||
* All ids returned by all SELECT queries
|
||||
*
|
||||
* All ids returned by all SELECT queries
|
||||
* (@see TimeLineProviderInterface::fetchQuery) and with the type
|
||||
* supported by the provider (@see TimelineProviderInterface::supportsType)
|
||||
* will be passed as argument.
|
||||
*
|
||||
*
|
||||
* @param array $ids an array of id
|
||||
* @return mixed[] an associative array of entities, with id as key
|
||||
*/
|
||||
public function getEntities(array $ids);
|
||||
|
||||
|
||||
/**
|
||||
* return an associative array with argument to render the entity
|
||||
* in an html template, which will be included in the timeline page
|
||||
*
|
||||
*
|
||||
* The result must have the following key :
|
||||
*
|
||||
*
|
||||
* - `template` : the template FQDN
|
||||
* - `template_data`: the data required by the template
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* array(
|
||||
* array(
|
||||
* 'template' => 'ChillMyBundle:timeline:template.html.twig',
|
||||
* 'template_data' => array(
|
||||
* 'accompanyingPeriod' => $entity,
|
||||
* 'person' => $args['person']
|
||||
* 'accompanyingPeriod' => $entity,
|
||||
* 'person' => $args['person']
|
||||
* )
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* `$context` and `$args` are defined by the bundle which will call the timeline
|
||||
* rendering.
|
||||
*
|
||||
* rendering.
|
||||
*
|
||||
* @param type $entity
|
||||
* @param type $context
|
||||
* @param array $args
|
||||
@ -156,7 +158,7 @@ The has the following signature :
|
||||
* @throws \LogicException if the context is not supported
|
||||
*/
|
||||
public function getEntityTemplate($entity, $context, array $args);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -176,7 +178,7 @@ The parameters should be replaced into the query by :code:`?`. They will be repl
|
||||
|
||||
`$context` and `$args` are defined by the bundle which will call the timeline rendering. You may use them to build a different query depending on this context.
|
||||
|
||||
For instance, if the context is `'person'`, the args will be this array :
|
||||
For instance, if the context is `'person'`, the args will be this array :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -197,7 +199,7 @@ You should find in the bundle documentation which contexts are arguments the bun
|
||||
|
||||
.. note::
|
||||
|
||||
We encourage to use `ClassMetaData` to define column names arguments. If you change your column names, changes will be reflected automatically during the execution of your code.
|
||||
We encourage to use `ClassMetaData` to define column names arguments. If you change your column names, changes will be reflected automatically during the execution of your code.
|
||||
|
||||
Example of an implementation :
|
||||
|
||||
@ -215,13 +217,13 @@ Example of an implementation :
|
||||
*/
|
||||
class TimelineReportProvider implements TimelineProviderInterface
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
|
||||
public function __construct(EntityManager $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
@ -230,9 +232,9 @@ Example of an implementation :
|
||||
public function fetchQuery($context, array $args)
|
||||
{
|
||||
$this->checkContext($context);
|
||||
|
||||
|
||||
$metadata = $this->em->getClassMetadata('ChillReportBundle:Report');
|
||||
|
||||
|
||||
return TimelineSingleQuery::fromArray([
|
||||
'id' => $metadata->getColumnName('id'),
|
||||
'type' => 'report',
|
||||
@ -254,11 +256,11 @@ Example of an implementation :
|
||||
The `supportsType` function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This function indicate to the master `chill.main.timeline_builder` service (which orchestrate the build of UNION queries) that the service supports the type indicated in the result's array of the `fetchQuery` function.
|
||||
This function indicate to the master `chill.main.timeline_builder` service (which orchestrate the build of UNION queries) that the service supports the type indicated in the result's array of the `fetchQuery` function.
|
||||
|
||||
The implementation of our previous example will be :
|
||||
The implementation of our previous example will be :
|
||||
|
||||
.. code-block:: php
|
||||
.. code-block:: php
|
||||
|
||||
|
||||
namespace Chill\ReportBundle\Timeline;
|
||||
@ -272,7 +274,7 @@ The implementation of our previous example will be :
|
||||
//...
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function supportsType($type)
|
||||
@ -304,12 +306,12 @@ The results **must be** an array where the id given by the UNION query (remember
|
||||
{
|
||||
$reports = $this->em->getRepository('ChillReportBundle:Report')
|
||||
->findBy(array('id' => $ids));
|
||||
|
||||
|
||||
$result = array();
|
||||
foreach($reports as $report) {
|
||||
$result[$report->getId()] = $report;
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -318,9 +320,9 @@ The results **must be** an array where the id given by the UNION query (remember
|
||||
The `getEntityTemplate` function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is where the master service will collect information to render the entity.
|
||||
This is where the master service will collect information to render the entity.
|
||||
|
||||
The result must be an associative array with :
|
||||
The result must be an associative array with :
|
||||
|
||||
- **template** is the FQDN of the template ;
|
||||
- **template_data** is an associative array where keys are the variables'names for this template, and values are the values.
|
||||
@ -332,8 +334,8 @@ Example :
|
||||
array(
|
||||
'template' => 'ChillMyBundle:timeline:template.html.twig',
|
||||
'template_data' => array(
|
||||
'period' => $entity,
|
||||
'person' => $args['person']
|
||||
'period' => $entity,
|
||||
'person' => $args['person']
|
||||
)
|
||||
);
|
||||
|
||||
@ -349,7 +351,7 @@ Create a timeline with his own context
|
||||
|
||||
You have to create a Controller which will execute the service `chill.main.timeline_builder`. Using the `Chill\MainBundle\Timeline\TimelineBuilder::getTimelineHTML` function, you will get an HTML representation of the timeline, which you may include with twig `raw` filter.
|
||||
|
||||
Example :
|
||||
Example :
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
|
@ -151,6 +151,7 @@ This script will :
|
||||
|
||||
# mount into to container
|
||||
./docker-php.sh
|
||||
bin/console chill:db:sync-views
|
||||
# and load fixtures
|
||||
bin/console doctrine:migrations:migrate
|
||||
|
||||
@ -161,7 +162,7 @@ Chill will be available at ``http://localhost:8001.`` Currently, there isn't any
|
||||
|
||||
# mount into to container
|
||||
./docker-php.sh
|
||||
# and load fixtures
|
||||
# and load fixtures (do not this for production)
|
||||
bin/console doctrine:fixtures:load --purge-with-truncate
|
||||
|
||||
There are several users available:
|
||||
@ -204,8 +205,10 @@ How to create the database schema (= run migrations) ?
|
||||
# if a container is running
|
||||
./docker-php.sh
|
||||
bin/console doctrine:migrations:migrate
|
||||
bin/console chill:db:sync-views
|
||||
# if not
|
||||
docker-compose run --user $(id -u) php bin/console doctrine:migrations:migrate
|
||||
docker-compose run --user $(id -u) php bin/console chill:db:sync-views
|
||||
|
||||
|
||||
How to read the email sent by the program ?
|
||||
@ -236,6 +239,23 @@ How to open a terminal in the project
|
||||
# if not
|
||||
docker-compose run --user $(id -u) php /bin/bash
|
||||
|
||||
How to run cron-jobs ?
|
||||
======================
|
||||
|
||||
Some command must be executed in :ref:`cron jobs <cronjob>`. To execute them:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# if a container is running
|
||||
./docker-php.sh
|
||||
bin/console chill:cron-job:execute
|
||||
# some of them are executed only during the night. So, we have to force the execution during the day:
|
||||
bin/console chill:cron-job:execute 'name-of-the-cron'
|
||||
# if not
|
||||
docker-compose run --user $(id -u) php bin/console chill:cron-job:execute
|
||||
# some of them are executed only during the night. So, we have to force the execution during the day:
|
||||
docker-compose run --user $(id -u) php bin/console chill:cron-job:execute 'name-of-the-cron'
|
||||
|
||||
How to run composer ?
|
||||
=====================
|
||||
|
||||
|
@ -38,6 +38,14 @@ This should be adapted to your needs:
|
||||
|
||||
* Think about how you will backup your database. Some adminsys find easier to store database outside of docker, which might be easier to administrate or replicate.
|
||||
|
||||
Run migrations on each update
|
||||
=============================
|
||||
|
||||
Every time you start a new version, you should apply update the sql schema:
|
||||
|
||||
- running ``bin/console doctrine:migration:migrate`` to run sql migration;
|
||||
- synchonizing sql views to the last state: ``bin/console chill:db:sync-views``
|
||||
|
||||
Cron jobs
|
||||
=========
|
||||
|
||||
|
47
rector.php
47
rector.php
@ -12,14 +12,49 @@ return static function (RectorConfig $rectorConfig): void {
|
||||
__DIR__ . '/src',
|
||||
]);
|
||||
|
||||
$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
|
||||
$rectorConfig->cacheDirectory(__DIR__.'/.cache/rector');
|
||||
//$rectorConfig->cacheClass(\Rector\Caching\ValueObject\Storage\FileCacheStorage::class);
|
||||
//$rectorConfig->cacheDirectory(__DIR__ . '/.cache/rector');
|
||||
|
||||
// register a single rule
|
||||
$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
|
||||
$rectorConfig->disableParallel();
|
||||
|
||||
// define sets of rules
|
||||
// $rectorConfig->sets([
|
||||
// LevelSetList::UP_TO_PHP_74
|
||||
// ]);
|
||||
//define sets of rules
|
||||
$rectorConfig->sets([
|
||||
LevelSetList::UP_TO_PHP_74
|
||||
]);
|
||||
|
||||
// skip some path...
|
||||
$rectorConfig->skip([
|
||||
// make rector stuck for some files
|
||||
\Rector\Php56\Rector\FunctionLike\AddDefaultValueForUndefinedVariableRector::class,
|
||||
|
||||
// we need to discuss this: are we going to have FALSE in tests instead of an error ?
|
||||
\Rector\Php71\Rector\FuncCall\CountOnNullRector::class,
|
||||
|
||||
// must merge MR500 and review a typing of "ArrayCollection" in entities
|
||||
\Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector::class,
|
||||
|
||||
// remove all PHP80 rules, in order to activate them one by one
|
||||
\Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector::class,
|
||||
\Rector\Php80\Rector\Class_\AnnotationToAttributeRector::class,
|
||||
\Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector::class,
|
||||
\Rector\Php80\Rector\FuncCall\ClassOnObjectRector::class,
|
||||
\Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector::class,
|
||||
\Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector::class,
|
||||
\Rector\Php80\Rector\Class_\DoctrineAnnotationClassToAttributeRector::class,
|
||||
\Rector\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector::class,
|
||||
\Rector\Php80\Rector\Ternary\GetDebugTypeRector::class,
|
||||
\Rector\Php80\Rector\FunctionLike\MixedTypeRector::class,
|
||||
\Rector\Php80\Rector\Property\NestedAnnotationToAttributeRector::class,
|
||||
\Rector\Php80\Rector\FuncCall\Php8ResourceReturnToObjectRector::class,
|
||||
\Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector::class,
|
||||
\Rector\Php80\Rector\ClassMethod\SetStateToStaticRector::class,
|
||||
\Rector\Php80\Rector\NotIdentical\StrContainsRector::class,
|
||||
\Rector\Php80\Rector\Identical\StrEndsWithRector::class,
|
||||
\Rector\Php80\Rector\Identical\StrStartsWithRector::class,
|
||||
\Rector\Php80\Rector\Class_\StringableForToStringRector::class,
|
||||
\Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector::class,
|
||||
\Rector\Php80\Rector\FunctionLike\UnionTypesRector::class
|
||||
]);
|
||||
};
|
||||
|
@ -50,7 +50,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
||||
->findAll();
|
||||
|
||||
foreach ($persons as $person) {
|
||||
$activityNbr = mt_rand(0, 3);
|
||||
$activityNbr = random_int(0, 3);
|
||||
|
||||
for ($i = 0; $i < $activityNbr; ++$i) {
|
||||
$activity = $this->newRandomActivity($person);
|
||||
@ -75,7 +75,7 @@ class LoadActivity extends AbstractFixture implements OrderedFixtureInterface
|
||||
|
||||
// ->setAttendee($this->faker->boolean())
|
||||
|
||||
for ($i = 0; mt_rand(0, 4) > $i; ++$i) {
|
||||
for ($i = 0; random_int(0, 4) > $i; ++$i) {
|
||||
$reason = $this->getRandomActivityReason();
|
||||
|
||||
if (null !== $reason) {
|
||||
|
@ -26,7 +26,7 @@ class Configuration implements ConfigurationInterface
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_activity');
|
||||
$rootNode = $treeBuilder->getRootNode('chill_activity');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$rootNode
|
||||
->children()
|
||||
@ -59,9 +59,7 @@ class Configuration implements ConfigurationInterface
|
||||
->info('The number of seconds of this duration. Must be an integer.')
|
||||
->cannotBeEmpty()
|
||||
->validate()
|
||||
->ifTrue(static function ($data) {
|
||||
return !is_int($data);
|
||||
})->thenInvalid('The value %s is not a valid integer')
|
||||
->ifTrue(static fn ($data) => !is_int($data))->thenInvalid('The value %s is not a valid integer')
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('label')
|
||||
|
@ -195,7 +195,7 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac
|
||||
* @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User")
|
||||
* @Groups({"docgen:read"})
|
||||
*/
|
||||
private ?User $user;
|
||||
private ?User $user = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToMany(targetEntity="Chill\MainBundle\Entity\User")
|
||||
|
@ -34,7 +34,7 @@ class ActivityPresence
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
* @Serializer\Groups({"docgen:read"})
|
||||
*/
|
||||
private ?int $id;
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="json")
|
||||
|
@ -122,7 +122,7 @@ class ActivityType
|
||||
* @ORM\GeneratedValue(strategy="AUTO")
|
||||
* @Groups({"docgen:read"})
|
||||
*/
|
||||
private ?int $id;
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", nullable=false, options={"default": ""})
|
||||
|
@ -86,9 +86,7 @@ class AvgActivityDuration implements ExportInterface, GroupedExportInterface
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->repository->createQueryBuilder('activity');
|
||||
|
||||
|
@ -87,9 +87,7 @@ class AvgActivityVisitDuration implements ExportInterface, GroupedExportInterfac
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->repository->createQueryBuilder('activity');
|
||||
|
||||
|
@ -86,9 +86,7 @@ class CountActivity implements ExportInterface, GroupedExportInterface
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->repository
|
||||
->createQueryBuilder('activity')
|
||||
|
@ -109,9 +109,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->entityManager->createQueryBuilder();
|
||||
|
||||
|
@ -87,9 +87,7 @@ class SumActivityDuration implements ExportInterface, GroupedExportInterface
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->repository
|
||||
->createQueryBuilder('activity')
|
||||
|
@ -87,9 +87,7 @@ class SumActivityVisitDuration implements ExportInterface, GroupedExportInterfac
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->repository
|
||||
->createQueryBuilder('activity')
|
||||
|
@ -137,13 +137,11 @@ class ListActivity implements ListInterface, GroupedExportInterface
|
||||
|
||||
$activity = $activityRepository->find($value);
|
||||
|
||||
return implode(', ', array_map(function (ActivityReason $r) {
|
||||
return '"' .
|
||||
$this->translatableStringHelper->localize($r->getCategory()->getName())
|
||||
. ' > ' .
|
||||
$this->translatableStringHelper->localize($r->getName())
|
||||
. '"';
|
||||
}, $activity->getReasons()->toArray()));
|
||||
return implode(', ', array_map(fn (ActivityReason $r) => '"' .
|
||||
$this->translatableStringHelper->localize($r->getCategory()->getName())
|
||||
. ' > ' .
|
||||
$this->translatableStringHelper->localize($r->getName())
|
||||
. '"', $activity->getReasons()->toArray()));
|
||||
};
|
||||
|
||||
case 'circle_name':
|
||||
@ -152,7 +150,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
|
||||
return 'circle';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(json_decode($value, true));
|
||||
return $this->translatableStringHelper->localize(json_decode($value, true, 512, JSON_THROW_ON_ERROR));
|
||||
};
|
||||
|
||||
case 'type_name':
|
||||
@ -161,7 +159,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
|
||||
return 'activity type';
|
||||
}
|
||||
|
||||
return $this->translatableStringHelper->localize(json_decode($value, true));
|
||||
return $this->translatableStringHelper->localize(json_decode($value, true, 512, JSON_THROW_ON_ERROR));
|
||||
};
|
||||
|
||||
default:
|
||||
@ -197,9 +195,7 @@ class ListActivity implements ListInterface, GroupedExportInterface
|
||||
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = [])
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
// throw an error if any fields are present
|
||||
if (!array_key_exists('fields', $data)) {
|
||||
|
@ -179,7 +179,7 @@ class ListActivityHelper
|
||||
}
|
||||
}
|
||||
|
||||
$decoded = json_decode($value);
|
||||
$decoded = json_decode($value, null, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
return implode(
|
||||
'|',
|
||||
|
@ -61,12 +61,9 @@ class ActivityTypeFilter implements FilterInterface
|
||||
$builder->add('accepted_activitytypes', EntityType::class, [
|
||||
'class' => ActivityType::class,
|
||||
'choices' => $this->activityTypeRepository->findAllActive(),
|
||||
'choice_label' => function (ActivityType $aty) {
|
||||
return
|
||||
($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
|
||||
.
|
||||
$this->translatableStringHelper->localize($aty->getName());
|
||||
},
|
||||
'choice_label' => fn (ActivityType $aty) => ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
|
||||
.
|
||||
$this->translatableStringHelper->localize($aty->getName()),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
|
@ -64,11 +64,9 @@ class UserScopeFilter implements FilterInterface
|
||||
{
|
||||
$builder->add('accepted_userscope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => function (Scope $s) {
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
);
|
||||
},
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
|
@ -61,12 +61,9 @@ class ActivityTypeFilter implements ExportElementValidatedInterface, FilterInter
|
||||
$builder->add('types', EntityType::class, [
|
||||
'choices' => $this->activityTypeRepository->findAllActive(),
|
||||
'class' => ActivityType::class,
|
||||
'choice_label' => function (ActivityType $aty) {
|
||||
return
|
||||
($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
|
||||
.
|
||||
$this->translatableStringHelper->localize($aty->getName());
|
||||
},
|
||||
'choice_label' => fn (ActivityType $aty) => ($aty->hasCategory() ? $this->translatableStringHelper->localize($aty->getCategory()->getName()) . ' > ' : '')
|
||||
.
|
||||
$this->translatableStringHelper->localize($aty->getName()),
|
||||
'group_by' => function (ActivityType $type) {
|
||||
if (!$type->hasCategory()) {
|
||||
return null;
|
||||
|
@ -32,7 +32,7 @@ class ActivityReasonCategoryType extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => 'Chill\ActivityBundle\Entity\ActivityReasonCategory',
|
||||
'data_class' => \Chill\ActivityBundle\Entity\ActivityReasonCategory::class,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -211,13 +211,9 @@ class ActivityType extends AbstractType
|
||||
'required' => $activityType->isRequired('attendee'),
|
||||
'expanded' => true,
|
||||
'class' => ActivityPresence::class,
|
||||
'choice_label' => function (ActivityPresence $activityPresence) {
|
||||
return $this->translatableStringHelper->localize($activityPresence->getName());
|
||||
},
|
||||
'query_builder' => static function (EntityRepository $er) {
|
||||
return $er->createQueryBuilder('a')
|
||||
->where('a.active = true');
|
||||
},
|
||||
'choice_label' => fn (ActivityPresence $activityPresence) => $this->translatableStringHelper->localize($activityPresence->getName()),
|
||||
'query_builder' => static fn (EntityRepository $er) => $er->createQueryBuilder('a')
|
||||
->where('a.active = true'),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -357,9 +353,7 @@ class ActivityType extends AbstractType
|
||||
|
||||
return (string) $location->getId();
|
||||
},
|
||||
function (?string $id): ?Location {
|
||||
return $this->om->getRepository(Location::class)->findOneBy(['id' => (int) $id]);
|
||||
}
|
||||
fn (?string $id): ?Location => $this->om->getRepository(Location::class)->findOneBy(['id' => (int) $id])
|
||||
));
|
||||
}
|
||||
|
||||
@ -401,9 +395,7 @@ class ActivityType extends AbstractType
|
||||
// the datetimetransformer will then handle timezone as GMT
|
||||
$timezoneUTC = new DateTimeZone('GMT');
|
||||
/** @var DateTime $data */
|
||||
$data = $formEvent->getData() === null ?
|
||||
DateTime::createFromFormat('U', '300') :
|
||||
$formEvent->getData();
|
||||
$data = $formEvent->getData() ?? DateTime::createFromFormat('U', '300');
|
||||
$seconds = $data->getTimezone()->getOffset($data);
|
||||
$data->setTimeZone($timezoneUTC);
|
||||
$data->add(new DateInterval('PT' . $seconds . 'S'));
|
||||
|
@ -45,9 +45,7 @@ class ActivityTypeType extends AbstractType
|
||||
])
|
||||
->add('category', EntityType::class, [
|
||||
'class' => ActivityTypeCategory::class,
|
||||
'choice_label' => function (ActivityTypeCategory $activityTypeCategory) {
|
||||
return $this->translatableStringHelper->localize($activityTypeCategory->getName());
|
||||
},
|
||||
'choice_label' => fn (ActivityTypeCategory $activityTypeCategory) => $this->translatableStringHelper->localize($activityTypeCategory->getName()),
|
||||
])
|
||||
->add('ordering', NumberType::class, [
|
||||
'required' => true,
|
||||
|
@ -45,9 +45,7 @@ class PickActivityReasonType extends AbstractType
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'class' => ActivityReason::class,
|
||||
'choice_label' => function (ActivityReason $choice) {
|
||||
return $this->reasonRender->renderString($choice, []);
|
||||
},
|
||||
'choice_label' => fn (ActivityReason $choice) => $this->reasonRender->renderString($choice, []),
|
||||
'group_by' => function (ActivityReason $choice): ?string {
|
||||
if (null !== $category = $choice->getCategory()) {
|
||||
return $this->translatableStringHelper->localize($category->getName());
|
||||
|
@ -38,10 +38,8 @@ class TranslatableActivityReasonCategoryType extends AbstractType
|
||||
$resolver->setDefaults(
|
||||
[
|
||||
'class' => ActivityReasonCategory::class,
|
||||
'choice_label' => function (ActivityReasonCategory $category) {
|
||||
return $this->translatableStringHelper->localize($category->getName())
|
||||
. (!$category->getActive() ? ' (' . $this->translator->trans('inactive') . ')' : '');
|
||||
},
|
||||
'choice_label' => fn (ActivityReasonCategory $category) => $this->translatableStringHelper->localize($category->getName())
|
||||
. (!$category->getActive() ? ' (' . $this->translator->trans('inactive') . ')' : ''),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -39,9 +39,7 @@ class TranslatableActivityType extends AbstractType
|
||||
'class' => ActivityType::class,
|
||||
'active_only' => true,
|
||||
'choices' => $this->activityTypeRepository->findAllActive(),
|
||||
'choice_label' => function (ActivityType $type) {
|
||||
return $this->translatableStringHelper->localize($type->getName());
|
||||
},
|
||||
'choice_label' => fn (ActivityType $type) => $this->translatableStringHelper->localize($type->getName()),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -254,9 +254,7 @@ final class ActivityACLAwareRepository implements ActivityACLAwareRepositoryInte
|
||||
$reachableScopes = $this->authorizationHelper->getReachableScopes($this->tokenStorage->getToken()->getUser(), ActivityVoter::SEE, $center);
|
||||
// we get the ids for those scopes
|
||||
$reachablesScopesId = array_map(
|
||||
static function (Scope $scope) {
|
||||
return $scope->getId();
|
||||
},
|
||||
static fn (Scope $scope) => $scope->getId(),
|
||||
$reachableScopes
|
||||
);
|
||||
|
||||
|
@ -134,9 +134,7 @@ class ActivityContext implements
|
||||
$builder->add($key, EntityType::class, [
|
||||
'class' => Person::class,
|
||||
'choices' => $persons,
|
||||
'choice_label' => function (Person $p) {
|
||||
return $this->personRender->renderString($p, []);
|
||||
},
|
||||
'choice_label' => fn (Person $p) => $this->personRender->renderString($p, []),
|
||||
'multiple' => false,
|
||||
'required' => false,
|
||||
'expanded' => true,
|
||||
|
@ -145,9 +145,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
|
||||
return array_filter(
|
||||
$activities,
|
||||
function ($activity) use ($user) {
|
||||
$activityUsernames = array_map(static function ($user) {
|
||||
return $user['username'];
|
||||
}, $activity['users'] ?? []);
|
||||
$activityUsernames = array_map(static fn ($user) => $user['username'], $activity['users'] ?? []);
|
||||
return in_array($user->getUsername(), $activityUsernames, true);
|
||||
}
|
||||
);
|
||||
@ -158,9 +156,7 @@ class ListActivitiesByAccompanyingPeriodContext implements
|
||||
return array_filter(
|
||||
$works,
|
||||
function ($work) use ($user) {
|
||||
$workUsernames = array_map(static function ($user) {
|
||||
return $user['username'];
|
||||
}, $work['referrers'] ?? []);
|
||||
$workUsernames = array_map(static fn ($user) => $user['username'], $work['referrers'] ?? []);
|
||||
|
||||
return in_array($user->getUsername(), $workUsernames, true);
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\ActivityBundle\Service\EntityInfo\AccompanyingPeriodInfoQueryPart;
|
||||
|
||||
use Chill\ActivityBundle\Entity\Activity;
|
||||
use Chill\PersonBundle\Service\EntityInfo\AccompanyingPeriodInfoUnionQueryPartInterface;
|
||||
|
||||
class ActivityUsersDateQueryPartForAccompanyingPeriodInfo implements AccompanyingPeriodInfoUnionQueryPartInterface
|
||||
{
|
||||
public function getAccompanyingPeriodIdColumn(): string
|
||||
{
|
||||
return 'activity.accompanyingperiod_id';
|
||||
}
|
||||
|
||||
public function getRelatedEntityColumn(): string
|
||||
{
|
||||
return Activity::class;
|
||||
}
|
||||
|
||||
public function getRelatedEntityIdColumn(): string
|
||||
{
|
||||
return 'activity.id';
|
||||
}
|
||||
|
||||
public function getUserIdColumn(): string
|
||||
{
|
||||
return 'au.user_id';
|
||||
}
|
||||
|
||||
public function getDateTimeColumn(): string
|
||||
{
|
||||
return 'activity.date';
|
||||
}
|
||||
|
||||
public function getDiscriminator(): string
|
||||
{
|
||||
return 'activity_date';
|
||||
}
|
||||
|
||||
public function getMetadataColumn(): string
|
||||
{
|
||||
return '\'{}\'::jsonb';
|
||||
}
|
||||
|
||||
public function getFromStatement(): string
|
||||
{
|
||||
return 'activity
|
||||
LEFT JOIN activity_user au on activity.id = au.activity_id';
|
||||
}
|
||||
|
||||
public function getWhereClause(): string
|
||||
{
|
||||
return 'activity.accompanyingperiod_id IS NOT NULL';
|
||||
}
|
||||
}
|
@ -369,12 +369,8 @@ final class ActivityControllerTest extends WebTestCase
|
||||
$center
|
||||
);
|
||||
$reachableScopesId = array_intersect(
|
||||
array_map(static function ($s) {
|
||||
return $s->getId();
|
||||
}, $reachableScopesDelete),
|
||||
array_map(static function ($s) {
|
||||
return $s->getId();
|
||||
}, $reachableScopesUpdate)
|
||||
array_map(static fn ($s) => $s->getId(), $reachableScopesDelete),
|
||||
array_map(static fn ($s) => $s->getId(), $reachableScopesUpdate)
|
||||
);
|
||||
|
||||
if (count($reachableScopesId) === 0) {
|
||||
|
@ -188,9 +188,7 @@ final class ActivityTypeTest extends KernelTestCase
|
||||
|
||||
// map all the values in an array
|
||||
$values = array_map(
|
||||
static function ($choice) {
|
||||
return $choice->value;
|
||||
},
|
||||
static fn ($choice) => $choice->value,
|
||||
$view['activity']['durationTime']->vars['choices']
|
||||
);
|
||||
|
||||
|
@ -79,15 +79,13 @@ final class TranslatableActivityReasonTest extends TypeTestCase
|
||||
$request = $prophet->prophesize();
|
||||
$translator = $prophet->prophesize();
|
||||
|
||||
$request->willExtend('Symfony\Component\HttpFoundation\Request');
|
||||
$request->willExtend(\Symfony\Component\HttpFoundation\Request::class);
|
||||
$request->getLocale()->willReturn($fallbackLocale);
|
||||
|
||||
$requestStack->willExtend('Symfony\Component\HttpFoundation\RequestStack');
|
||||
$requestStack->getCurrentRequest()->will(static function () use ($request) {
|
||||
return $request;
|
||||
});
|
||||
$requestStack->willExtend(\Symfony\Component\HttpFoundation\RequestStack::class);
|
||||
$requestStack->getCurrentRequest()->will(static fn () => $request);
|
||||
|
||||
$translator->willExtend('Symfony\Component\Translation\Translator');
|
||||
$translator->willExtend(\Symfony\Component\Translation\Translator::class);
|
||||
$translator->getFallbackLocales()->willReturn($locale);
|
||||
|
||||
return new TranslatableStringHelper(
|
||||
|
@ -160,7 +160,7 @@ final class ActivityVoterTest extends KernelTestCase
|
||||
{
|
||||
$token = $this->prophet->prophesize();
|
||||
$token
|
||||
->willImplement('\Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
|
||||
->willImplement('\\' . \Symfony\Component\Security\Core\Authentication\Token\TokenInterface::class);
|
||||
|
||||
if (null === $user) {
|
||||
$token->getUser()->willReturn(null);
|
||||
|
@ -34,6 +34,7 @@ services:
|
||||
resource: '../Validator/Constraints/'
|
||||
|
||||
Chill\ActivityBundle\Service\DocGenerator\:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
resource: '../Service/DocGenerator/'
|
||||
|
||||
Chill\ActivityBundle\Service\EntityInfo\:
|
||||
resource: '../Service/EntityInfo/'
|
||||
|
@ -22,7 +22,7 @@ class Configuration implements ConfigurationInterface
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_aside_activity');
|
||||
|
||||
$treeBuilder->getRootNode('chill_aside_activity')
|
||||
$treeBuilder->getRootNode()
|
||||
->children()
|
||||
->arrayNode('form')
|
||||
->canBeEnabled()
|
||||
@ -132,9 +132,7 @@ class Configuration implements ConfigurationInterface
|
||||
->info('The number of seconds of this duration. Must be an integer.')
|
||||
->cannotBeEmpty()
|
||||
->validate()
|
||||
->ifTrue(static function ($data) {
|
||||
return !is_int($data);
|
||||
})->thenInvalid('The value %s is not a valid integer')
|
||||
->ifTrue(static fn ($data) => !is_int($data))->thenInvalid('The value %s is not a valid integer')
|
||||
->end()
|
||||
->end()
|
||||
->scalarNode('label')
|
||||
|
@ -57,7 +57,7 @@ class AsideActivity implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private ?int $id;
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=100, nullable=true)
|
||||
|
@ -59,9 +59,7 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface
|
||||
$labels = array_combine($values, $values);
|
||||
$labels['_header'] = $this->getTitle();
|
||||
|
||||
return static function ($value) use ($labels) {
|
||||
return $labels[$value];
|
||||
};
|
||||
return static fn ($value) => $labels[$value];
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
|
@ -95,9 +95,7 @@ final class AsideActivityFormType extends AbstractType
|
||||
// the datetimetransformer will then handle timezone as GMT
|
||||
$timezoneUTC = new DateTimeZone('GMT');
|
||||
/** @var DateTimeImmutable $data */
|
||||
$data = $formEvent->getData() === null ?
|
||||
DateTime::createFromFormat('U', '300') :
|
||||
$formEvent->getData();
|
||||
$data = $formEvent->getData() ?? DateTime::createFromFormat('U', '300');
|
||||
$seconds = $data->getTimezone()->getOffset($data);
|
||||
$data->setTimeZone($timezoneUTC);
|
||||
$data->add(new DateInterval('PT' . $seconds . 'S'));
|
||||
|
@ -116,7 +116,7 @@ abstract class AbstractElementController extends AbstractController
|
||||
$indexPage = 'chill_budget_elements_household_index';
|
||||
}
|
||||
|
||||
$entity = null !== $element->getPerson() ? $element->getPerson() : $element->getHousehold();
|
||||
$entity = $element->getPerson() ?? $element->getHousehold();
|
||||
|
||||
$form = $this->createForm($this->getType(), $element);
|
||||
$form->add('submit', SubmitType::class);
|
||||
|
@ -19,7 +19,7 @@ class CalculatorCompilerPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$manager = $container->getDefinition('Chill\BudgetBundle\Calculator\CalculatorManager');
|
||||
$manager = $container->getDefinition(\Chill\BudgetBundle\Calculator\CalculatorManager::class);
|
||||
|
||||
foreach ($container->findTaggedServiceIds('chill_budget.calculator') as $id => $tags) {
|
||||
foreach ($tags as $tag) {
|
||||
|
@ -19,7 +19,7 @@ class Configuration implements ConfigurationInterface
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_budget');
|
||||
$rootNode = $treeBuilder->getRootNode('chill_budget');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$rootNode
|
||||
->children()
|
||||
|
@ -41,7 +41,7 @@ abstract class AbstractElement
|
||||
/**
|
||||
* @ORM\Column(name="comment", type="text", nullable=true)
|
||||
*/
|
||||
private ?string $comment;
|
||||
private ?string $comment = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="endDate", type="datetime_immutable", nullable=true)
|
||||
@ -50,7 +50,7 @@ abstract class AbstractElement
|
||||
* message="The budget element's end date must be after the start date"
|
||||
* )
|
||||
*/
|
||||
private ?DateTimeImmutable $endDate;
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(
|
||||
|
@ -52,9 +52,7 @@ class ChargeType extends AbstractType
|
||||
'label' => 'Charge type',
|
||||
'required' => true,
|
||||
'placeholder' => $this->translator->trans('admin.form.Choose the type of charge'),
|
||||
'choice_label' => function (ChargeKind $resource) {
|
||||
return $this->translatableStringHelper->localize($resource->getName());
|
||||
},
|
||||
'choice_label' => fn (ChargeKind $resource) => $this->translatableStringHelper->localize($resource->getName()),
|
||||
'attr' => ['class' => 'select2'],
|
||||
])
|
||||
->add('amount', MoneyType::class)
|
||||
|
@ -51,9 +51,7 @@ class ResourceType extends AbstractType
|
||||
'label' => 'Resource type',
|
||||
'required' => true,
|
||||
'placeholder' => $this->translator->trans('admin.form.Choose the type of resource'),
|
||||
'choice_label' => function (ResourceKind $resource) {
|
||||
return $this->translatableStringHelper->localize($resource->getName());
|
||||
},
|
||||
'choice_label' => fn (ResourceKind $resource) => $this->translatableStringHelper->localize($resource->getName()),
|
||||
'attr' => ['class' => 'select2'],
|
||||
])
|
||||
->add('amount', MoneyType::class)
|
||||
|
@ -16,8 +16,14 @@
|
||||
<td class="el-type">
|
||||
{% if f.isResource %}
|
||||
{{ f.resource.name|localize_translatable_string }}
|
||||
{% if f.resource.getKind is same as 'other' %}
|
||||
: {{ f.getComment }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ f.charge.name|localize_translatable_string }}
|
||||
{% if f.charge.getKind is same as 'other' %}
|
||||
: {{ f.getComment }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ f.amount|format_currency('EUR') }}</td>
|
||||
|
@ -66,9 +66,7 @@ final class SummaryBudget implements SummaryBudgetInterface
|
||||
];
|
||||
}
|
||||
|
||||
$personIds = $household->getCurrentPersons()->map(static function (Person $p) {
|
||||
return $p->getId();
|
||||
});
|
||||
$personIds = $household->getCurrentPersons()->map(static fn (Person $p) => $p->getId());
|
||||
$ids = implode(', ', array_fill(0, count($personIds), '?'));
|
||||
|
||||
$parameters = [...$personIds, $household->getId()];
|
||||
@ -127,18 +125,14 @@ final class SummaryBudget implements SummaryBudgetInterface
|
||||
{
|
||||
$keys = array_map(static fn (ChargeKind $kind) => $kind->getKind(), $this->chargeKindRepository->findAll());
|
||||
|
||||
return array_combine($keys, array_map(function ($kind) {
|
||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->chargeKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
|
||||
}, $keys));
|
||||
return array_combine($keys, array_map(fn ($kind) => ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->chargeKindRepository->findOneByKind($kind)->getName()), 'comment' => ''], $keys));
|
||||
}
|
||||
|
||||
private function getEmptyResourceArray(): array
|
||||
{
|
||||
$keys = array_map(static fn (ResourceKind $kind) => $kind->getKind(), $this->resourceKindRepository->findAll());
|
||||
|
||||
return array_combine($keys, array_map(function ($kind) {
|
||||
return ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->resourceKindRepository->findOneByKind($kind)->getName()), 'comment' => ''];
|
||||
}, $keys));
|
||||
return array_combine($keys, array_map(fn ($kind) => ['sum' => 0.0, 'label' => $this->translatableStringHelper->localize($this->resourceKindRepository->findOneByKind($kind)->getName()), 'comment' => ''], $keys));
|
||||
}
|
||||
|
||||
private function rowToArray(array $rows, string $kind): array
|
||||
|
@ -50,9 +50,7 @@ final class SummaryBudgetTest extends TestCase
|
||||
],
|
||||
]);
|
||||
$queryCharges->setParameters(Argument::type('array'))
|
||||
->will(static function ($args, $query) {
|
||||
return $query;
|
||||
});
|
||||
->will(static fn ($args, $query) => $query);
|
||||
|
||||
$queryResources = $this->prophesize(AbstractQuery::class);
|
||||
$queryResources->getResult()->willReturn([
|
||||
@ -63,9 +61,7 @@ final class SummaryBudgetTest extends TestCase
|
||||
],
|
||||
]);
|
||||
$queryResources->setParameters(Argument::type('array'))
|
||||
->will(static function ($args, $query) {
|
||||
return $query;
|
||||
});
|
||||
->will(static fn ($args, $query) => $query);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->createNativeQuery(Argument::type('string'), Argument::type(Query\ResultSetMapping::class))
|
||||
@ -100,9 +96,7 @@ final class SummaryBudgetTest extends TestCase
|
||||
$resourceRepository->findOneByKind('misc')->willReturn($misc);
|
||||
|
||||
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(static function ($arg) {
|
||||
return $arg[0]['fr'];
|
||||
});
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(static fn ($arg) => $arg[0]['fr']);
|
||||
|
||||
$person = new Person();
|
||||
$personReflection = new ReflectionClass($person);
|
||||
|
@ -60,7 +60,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->logger->info(__CLASS__ . ' execute command');
|
||||
$this->logger->info(self::class . ' execute command');
|
||||
|
||||
$limit = 50;
|
||||
$offset = 0;
|
||||
@ -71,7 +71,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
||||
$created = 0;
|
||||
$renewed = 0;
|
||||
|
||||
$this->logger->info(__CLASS__ . ' the number of user to get - renew', [
|
||||
$this->logger->info(self::class . ' the number of user to get - renew', [
|
||||
'total' => $total,
|
||||
'expiration' => $expiration->format(DateTimeImmutable::ATOM),
|
||||
]);
|
||||
@ -92,7 +92,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
||||
// we first try to renew an existing subscription, if any.
|
||||
// if not, or if it fails, we try to create a new one
|
||||
if ($this->mapCalendarToUser->hasActiveSubscription($user)) {
|
||||
$this->logger->debug(__CLASS__ . ' renew a subscription for', [
|
||||
$this->logger->debug(self::class . ' renew a subscription for', [
|
||||
'userId' => $user->getId(),
|
||||
'username' => $user->getUsernameCanonical(),
|
||||
]);
|
||||
@ -104,7 +104,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
||||
if (0 !== $expirationTs) {
|
||||
++$renewed;
|
||||
} else {
|
||||
$this->logger->warning(__CLASS__ . ' could not renew subscription for a user', [
|
||||
$this->logger->warning(self::class . ' could not renew subscription for a user', [
|
||||
'userId' => $user->getId(),
|
||||
'username' => $user->getUsernameCanonical(),
|
||||
]);
|
||||
@ -112,7 +112,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
||||
}
|
||||
|
||||
if (!$this->mapCalendarToUser->hasActiveSubscription($user)) {
|
||||
$this->logger->debug(__CLASS__ . ' create a subscription for', [
|
||||
$this->logger->debug(self::class . ' create a subscription for', [
|
||||
'userId' => $user->getId(),
|
||||
'username' => $user->getUsernameCanonical(),
|
||||
]);
|
||||
@ -124,7 +124,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
||||
if (0 !== $expirationTs) {
|
||||
++$created;
|
||||
} else {
|
||||
$this->logger->warning(__CLASS__ . ' could not create subscription for a user', [
|
||||
$this->logger->warning(self::class . ' could not create subscription for a user', [
|
||||
'userId' => $user->getId(),
|
||||
'username' => $user->getUsernameCanonical(),
|
||||
]);
|
||||
@ -139,7 +139,7 @@ class MapAndSubscribeUserCalendarCommand extends Command
|
||||
$this->em->clear();
|
||||
}
|
||||
|
||||
$this->logger->warning(__CLASS__ . ' process executed', [
|
||||
$this->logger->warning(self::class . ' process executed', [
|
||||
'created' => $created,
|
||||
'renewed' => $renewed,
|
||||
]);
|
||||
|
@ -24,7 +24,7 @@ class Configuration implements ConfigurationInterface
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_calendar');
|
||||
$rootNode = $treeBuilder->getRootNode('chill_calendar');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$rootNode
|
||||
->children()
|
||||
|
@ -514,9 +514,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
||||
*/
|
||||
public function getUsers(): ReadableCollection
|
||||
{
|
||||
return $this->getInvites()->map(static function (Invite $i) {
|
||||
return $i->getUser();
|
||||
});
|
||||
return $this->getInvites()->map(static fn (Invite $i) => $i->getUser());
|
||||
}
|
||||
|
||||
public function hasCalendarRange(): bool
|
||||
@ -601,9 +599,7 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente
|
||||
}
|
||||
|
||||
$invite = $this->invites
|
||||
->filter(static function (Invite $invite) use ($user) {
|
||||
return $invite->getUser() === $user;
|
||||
})
|
||||
->filter(static fn (Invite $invite) => $invite->getUser() === $user)
|
||||
->first();
|
||||
$this->removeInvite($invite);
|
||||
|
||||
|
@ -63,7 +63,7 @@ class CalendarRange implements TrackCreationInterface, TrackUpdateInterface
|
||||
* @Groups({"read", "write", "calendar:read"})
|
||||
* @Assert\NotNull
|
||||
*/
|
||||
private ?Location $location;
|
||||
private ?Location $location = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime_immutable", nullable=false)
|
||||
|
@ -61,9 +61,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface
|
||||
$labels = array_combine($values, $values);
|
||||
$labels['_header'] = $this->getTitle();
|
||||
|
||||
return static function ($value) use ($labels) {
|
||||
return $labels[$value];
|
||||
};
|
||||
return static fn ($value) => $labels[$value];
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
@ -91,9 +89,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface
|
||||
*/
|
||||
public function initiateQuery(array $requiredModifiers, array $acl, array $data = []): QueryBuilder
|
||||
{
|
||||
$centers = array_map(static function ($el) {
|
||||
return $el['center'];
|
||||
}, $acl);
|
||||
$centers = array_map(static fn ($el) => $el['center'], $acl);
|
||||
|
||||
$qb = $this->calendarRepository->createQueryBuilder('cal');
|
||||
|
||||
|
@ -61,9 +61,7 @@ class StatCalendarAvgDuration implements ExportInterface, GroupedExportInterface
|
||||
$labels = array_combine($values, $values);
|
||||
$labels['_header'] = $this->getTitle();
|
||||
|
||||
return static function ($value) use ($labels) {
|
||||
return $labels[$value];
|
||||
};
|
||||
return static fn ($value) => $labels[$value];
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
|
@ -61,9 +61,7 @@ class StatCalendarSumDuration implements ExportInterface, GroupedExportInterface
|
||||
$labels = array_combine($values, $values);
|
||||
$labels['_header'] = $this->getTitle();
|
||||
|
||||
return static function ($value) use ($labels) {
|
||||
return $labels[$value];
|
||||
};
|
||||
return static fn ($value) => $labels[$value];
|
||||
}
|
||||
|
||||
public function getQueryKeys($data): array
|
||||
|
@ -58,9 +58,7 @@ class AgentFilter implements FilterInterface
|
||||
{
|
||||
$builder->add('accepted_agents', EntityType::class, [
|
||||
'class' => User::class,
|
||||
'choice_label' => function (User $u) {
|
||||
return $this->userRender->renderString($u, []);
|
||||
},
|
||||
'choice_label' => fn (User $u) => $this->userRender->renderString($u, []),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
|
@ -69,11 +69,9 @@ class JobFilter implements FilterInterface
|
||||
{
|
||||
$builder->add('job', EntityType::class, [
|
||||
'class' => UserJob::class,
|
||||
'choice_label' => function (UserJob $j) {
|
||||
return $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
);
|
||||
},
|
||||
'choice_label' => fn (UserJob $j) => $this->translatableStringHelper->localize(
|
||||
$j->getLabel()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
|
@ -69,11 +69,9 @@ class ScopeFilter implements FilterInterface
|
||||
{
|
||||
$builder->add('scope', EntityType::class, [
|
||||
'class' => Scope::class,
|
||||
'choice_label' => function (Scope $s) {
|
||||
return $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
);
|
||||
},
|
||||
'choice_label' => fn (Scope $s) => $this->translatableStringHelper->localize(
|
||||
$s->getName()
|
||||
),
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
]);
|
||||
|
@ -89,14 +89,10 @@ class CalendarToRemoteHandler implements MessageHandlerInterface
|
||||
|
||||
$newInvites = array_filter(
|
||||
array_map(
|
||||
function ($id) {
|
||||
return $this->inviteRepository->find($id);
|
||||
},
|
||||
fn ($id) => $this->inviteRepository->find($id),
|
||||
$calendarMessage->getNewInvitesIds(),
|
||||
),
|
||||
static function (?Invite $invite) {
|
||||
return null !== $invite;
|
||||
}
|
||||
static fn (?Invite $invite) => null !== $invite
|
||||
);
|
||||
|
||||
$this->calendarConnector->syncCalendar(
|
||||
|
@ -77,7 +77,7 @@ class MSGraphChangeNotificationHandler implements MessageHandlerInterface
|
||||
$user = $this->userRepository->find($changeNotificationMessage->getUserId());
|
||||
|
||||
if (null === $user) {
|
||||
$this->logger->warning(__CLASS__ . ' notification concern non-existent user, skipping');
|
||||
$this->logger->warning(self::class . ' notification concern non-existent user, skipping');
|
||||
|
||||
return;
|
||||
}
|
||||
@ -86,7 +86,7 @@ class MSGraphChangeNotificationHandler implements MessageHandlerInterface
|
||||
$secret = $this->mapCalendarToUser->getSubscriptionSecret($user);
|
||||
|
||||
if ($secret !== ($notification['clientState'] ?? -1)) {
|
||||
$this->logger->warning(__CLASS__ . ' could not validate secret, skipping');
|
||||
$this->logger->warning(self::class . ' could not validate secret, skipping');
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -101,7 +101,7 @@ class MSGraphChangeNotificationHandler implements MessageHandlerInterface
|
||||
$this->calendarSyncer->handleCalendarSync($calendar, $notification, $user);
|
||||
$this->em->flush();
|
||||
} else {
|
||||
$this->logger->info(__CLASS__ . ' id not found in any calendar nor calendar range');
|
||||
$this->logger->info(self::class . ' id not found in any calendar nor calendar range');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,14 +58,12 @@ class CalendarMessage
|
||||
$this->previousMainUserId = null !== $calendar->previousMainUser ?
|
||||
$calendar->previousMainUser->getId() : null;
|
||||
$this->newInvitesIds = array_map(static fn (Invite $i) => $i->getId(), $calendar->newInvites);
|
||||
$this->oldInvites = array_map(static function (Invite $i) {
|
||||
return [
|
||||
'inviteId' => $i->getId(),
|
||||
'userId' => $i->getUser()->getId(),
|
||||
'userEmail' => $i->getUser()->getEmail(),
|
||||
'userLabel' => $i->getUser()->getLabel(),
|
||||
];
|
||||
}, $calendar->oldInvites);
|
||||
$this->oldInvites = array_map(static fn (Invite $i) => [
|
||||
'inviteId' => $i->getId(),
|
||||
'userId' => $i->getUser()->getId(),
|
||||
'userEmail' => $i->getUser()->getEmail(),
|
||||
'userLabel' => $i->getUser()->getLabel(),
|
||||
], $calendar->oldInvites);
|
||||
}
|
||||
|
||||
public function getAction(): string
|
||||
|
@ -121,9 +121,7 @@ class RemoteEventConverter
|
||||
'subject' => '[Chill] ' .
|
||||
implode(
|
||||
', ',
|
||||
$calendar->getPersons()->map(function (Person $p) {
|
||||
return $this->personRender->renderString($p, []);
|
||||
})->toArray()
|
||||
$calendar->getPersons()->map(fn (Person $p) => $this->personRender->renderString($p, []))->toArray()
|
||||
),
|
||||
'start' => [
|
||||
'dateTime' => $calendar->getStartDate()->setTimezone($this->remoteDateTimeZone)
|
||||
@ -161,9 +159,7 @@ class RemoteEventConverter
|
||||
{
|
||||
return [
|
||||
'attendees' => $calendar->getInvites()->map(
|
||||
function (Invite $i) {
|
||||
return $this->buildInviteToAttendee($i);
|
||||
}
|
||||
fn (Invite $i) => $this->buildInviteToAttendee($i)
|
||||
)->toArray(),
|
||||
];
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class CalendarRangeSyncer
|
||||
}
|
||||
$calendarRange->preventEnqueueChanges = true;
|
||||
|
||||
$this->logger->info(__CLASS__ . ' remove a calendar range because deleted on remote calendar');
|
||||
$this->logger->info(self::class . ' remove a calendar range because deleted on remote calendar');
|
||||
$this->em->remove($calendarRange);
|
||||
|
||||
break;
|
||||
@ -71,7 +71,7 @@ class CalendarRangeSyncer
|
||||
$notification['resource']
|
||||
)->toArray();
|
||||
} catch (ClientExceptionInterface $clientException) {
|
||||
$this->logger->warning(__CLASS__ . ' could not retrieve event from ms graph. Already deleted ?', [
|
||||
$this->logger->warning(self::class . ' could not retrieve event from ms graph. Already deleted ?', [
|
||||
'calendarRangeId' => $calendarRange->getId(),
|
||||
'remoteEventId' => $notification['resource'],
|
||||
]);
|
||||
@ -82,7 +82,7 @@ class CalendarRangeSyncer
|
||||
$lastModified = RemoteEventConverter::convertStringDateWithTimezone($new['lastModifiedDateTime']);
|
||||
|
||||
if ($calendarRange->getRemoteAttributes()['lastModifiedDateTime'] === $lastModified->getTimestamp()) {
|
||||
$this->logger->info(__CLASS__ . ' change key is equals. Source is probably a local update', [
|
||||
$this->logger->info(self::class . ' change key is equals. Source is probably a local update', [
|
||||
'calendarRangeId' => $calendarRange->getId(),
|
||||
'remoteEventId' => $notification['resource'],
|
||||
]);
|
||||
|
@ -79,7 +79,7 @@ class CalendarSyncer
|
||||
$notification['resource']
|
||||
)->toArray();
|
||||
} catch (ClientExceptionInterface $clientException) {
|
||||
$this->logger->warning(__CLASS__ . ' could not retrieve event from ms graph. Already deleted ?', [
|
||||
$this->logger->warning(self::class . ' could not retrieve event from ms graph. Already deleted ?', [
|
||||
'calendarId' => $calendar->getId(),
|
||||
'remoteEventId' => $notification['resource'],
|
||||
]);
|
||||
@ -96,7 +96,7 @@ class CalendarSyncer
|
||||
);
|
||||
|
||||
if ($calendar->getRemoteAttributes()['lastModifiedDateTime'] === $lastModified->getTimestamp()) {
|
||||
$this->logger->info(__CLASS__ . ' change key is equals. Source is probably a local update', [
|
||||
$this->logger->info(self::class . ' change key is equals. Source is probably a local update', [
|
||||
'calendarRangeId' => $calendar->getId(),
|
||||
'remoteEventId' => $notification['resource'],
|
||||
]);
|
||||
|
@ -190,23 +190,17 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||
]
|
||||
)->toArray();
|
||||
|
||||
$ids = array_map(static function ($item) {
|
||||
return $item['id'];
|
||||
}, $bareEvents['value']);
|
||||
$ids = array_map(static fn ($item) => $item['id'], $bareEvents['value']);
|
||||
$existingIdsInRange = $this->calendarRangeRepository->findRemoteIdsPresent($ids);
|
||||
$existingIdsInCalendar = $this->calendarRepository->findRemoteIdsPresent($ids);
|
||||
|
||||
return array_values(
|
||||
array_map(
|
||||
function ($item) {
|
||||
return $this->remoteEventConverter->convertToRemote($item);
|
||||
},
|
||||
fn ($item) => $this->remoteEventConverter->convertToRemote($item),
|
||||
// filter all event to keep only the one not in range
|
||||
array_filter(
|
||||
$bareEvents['value'],
|
||||
static function ($item) use ($existingIdsInRange, $existingIdsInCalendar) {
|
||||
return ((!$existingIdsInRange[$item['id']]) ?? true) && ((!$existingIdsInCalendar[$item['id']]) ?? true);
|
||||
}
|
||||
static fn ($item) => ((!$existingIdsInRange[$item['id']]) ?? true) && ((!$existingIdsInCalendar[$item['id']]) ?? true)
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -604,9 +598,7 @@ class MSGraphRemoteCalendarConnector implements RemoteCalendarConnectorInterface
|
||||
}
|
||||
|
||||
$this->cacheScheduleTimeForUser[$userId] = array_map(
|
||||
function ($item) {
|
||||
return $this->remoteEventConverter->convertAvailabilityToRemoteEvent($item);
|
||||
},
|
||||
fn ($item) => $this->remoteEventConverter->convertAvailabilityToRemoteEvent($item),
|
||||
$response['value'][0]['scheduleItems']
|
||||
);
|
||||
|
||||
|
@ -66,6 +66,6 @@ class BulkCalendarShortMessageSender
|
||||
$this->em->refresh($calendar);
|
||||
}
|
||||
|
||||
$this->logger->info(__CLASS__ . 'a bulk of messages was sent', ['count_calendars' => $countCalendars, 'count_sms' => $countSms]);
|
||||
$this->logger->info(self::class . 'a bulk of messages was sent', ['count_calendars' => $countCalendars, 'count_sms' => $countSms]);
|
||||
}
|
||||
}
|
||||
|
@ -114,12 +114,8 @@ final class CalendarTypeTest extends TypeTestCase
|
||||
$this->assertEquals(8, $calendar->getCalendarRange()->getId());
|
||||
$this->assertEquals(9, $calendar->getLocation()->getId());
|
||||
$this->assertEquals(true, $calendar->getSendSMS());
|
||||
$this->assertContains(2, $calendar->getUsers()->map(static function (User $u) {
|
||||
return $u->getId();
|
||||
}));
|
||||
$this->assertContains(3, $calendar->getUsers()->map(static function (User $u) {
|
||||
return $u->getId();
|
||||
}));
|
||||
$this->assertContains(2, $calendar->getUsers()->map(static fn (User $u) => $u->getId()));
|
||||
$this->assertContains(3, $calendar->getUsers()->map(static fn (User $u) => $u->getId()));
|
||||
}
|
||||
|
||||
protected function getExtensions()
|
||||
@ -148,25 +144,17 @@ final class CalendarTypeTest extends TypeTestCase
|
||||
) {
|
||||
$transformer = $this->prophesize($classTransformer);
|
||||
$transformer->transform(Argument::type('array'))
|
||||
->will(static function ($args) {
|
||||
return implode(
|
||||
',',
|
||||
array_map(static function ($p) {
|
||||
return $p->getId();
|
||||
}, $args[0])
|
||||
);
|
||||
});
|
||||
->will(static fn ($args) => implode(
|
||||
',',
|
||||
array_map(static fn ($p) => $p->getId(), $args[0])
|
||||
));
|
||||
$transformer->transform(Argument::exact(null))
|
||||
->willReturn([]);
|
||||
$transformer->transform(Argument::type(Collection::class))
|
||||
->will(static function ($args) {
|
||||
return implode(
|
||||
',',
|
||||
array_map(static function ($p) {
|
||||
return $p->getId();
|
||||
}, $args[0]->toArray())
|
||||
);
|
||||
});
|
||||
->will(static fn ($args) => implode(
|
||||
',',
|
||||
array_map(static fn ($p) => $p->getId(), $args[0]->toArray())
|
||||
));
|
||||
$transformer->reverseTransform(Argument::type('string'))
|
||||
->will(static function ($args) use ($objClass) {
|
||||
if (null === $args[0]) {
|
||||
@ -195,9 +183,7 @@ final class CalendarTypeTest extends TypeTestCase
|
||||
) {
|
||||
$transformer = $this->prophesize($classTransformer);
|
||||
$transformer->transform(Argument::type('object'))
|
||||
->will(static function ($args) {
|
||||
return (string) $args[0]->getId();
|
||||
});
|
||||
->will(static fn ($args) => (string) $args[0]->getId());
|
||||
$transformer->transform(Argument::exact(null))
|
||||
->willReturn('');
|
||||
$transformer->reverseTransform(Argument::type('string'))
|
||||
|
@ -63,9 +63,7 @@ final class AddressConverterTest extends TestCase
|
||||
{
|
||||
$engine = $this->prophesize(EngineInterface::class);
|
||||
$translatableStringHelper = $this->prophesize(TranslatableStringHelperInterface::class);
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(static function ($args): string {
|
||||
return ($args[0] ?? ['fr' => 'not provided'])['fr'] ?? 'not provided';
|
||||
});
|
||||
$translatableStringHelper->localize(Argument::type('array'))->will(static fn ($args): string => ($args[0] ?? ['fr' => 'not provided'])['fr'] ?? 'not provided');
|
||||
|
||||
$addressRender = new AddressRender($engine->reveal(), $translatableStringHelper->reveal());
|
||||
|
||||
|
@ -72,17 +72,13 @@ final class CalendarForShortMessageProviderTest extends TestCase
|
||||
Argument::type(DateTimeImmutable::class),
|
||||
Argument::type('int'),
|
||||
Argument::exact(0)
|
||||
)->will(static function ($args) {
|
||||
return array_fill(0, $args[2], new Calendar());
|
||||
})->shouldBeCalledTimes(1);
|
||||
)->will(static fn ($args) => array_fill(0, $args[2], new Calendar()))->shouldBeCalledTimes(1);
|
||||
$calendarRepository->findByNotificationAvailable(
|
||||
Argument::type(DateTimeImmutable::class),
|
||||
Argument::type(DateTimeImmutable::class),
|
||||
Argument::type('int'),
|
||||
Argument::not(0)
|
||||
)->will(static function ($args) {
|
||||
return array_fill(0, $args[2] - 1, new Calendar());
|
||||
})->shouldBeCalledTimes(1);
|
||||
)->will(static fn ($args) => array_fill(0, $args[2] - 1, new Calendar()))->shouldBeCalledTimes(1);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->clear()->shouldBeCalled();
|
||||
@ -108,17 +104,13 @@ final class CalendarForShortMessageProviderTest extends TestCase
|
||||
Argument::type(DateTimeImmutable::class),
|
||||
Argument::type('int'),
|
||||
Argument::exact(0)
|
||||
)->will(static function ($args) {
|
||||
return array_fill(0, 1, new Calendar());
|
||||
})->shouldBeCalledTimes(1);
|
||||
)->will(static fn ($args) => array_fill(0, 1, new Calendar()))->shouldBeCalledTimes(1);
|
||||
$calendarRepository->findByNotificationAvailable(
|
||||
Argument::type(DateTimeImmutable::class),
|
||||
Argument::type(DateTimeImmutable::class),
|
||||
Argument::type('int'),
|
||||
Argument::not(0)
|
||||
)->will(static function ($args) {
|
||||
return [];
|
||||
})->shouldBeCalledTimes(1);
|
||||
)->will(static fn ($args) => [])->shouldBeCalledTimes(1);
|
||||
|
||||
$em = $this->prophesize(EntityManagerInterface::class);
|
||||
$em->clear()->shouldBeCalled();
|
||||
|
@ -192,9 +192,7 @@ class CreateFieldsOnGroupCommand extends Command
|
||||
|
||||
foreach ($languages as $lang) {
|
||||
//todo replace with service to find lang when available
|
||||
$names[] = (isset($cf->getName()[$lang])) ?
|
||||
$cf->getName()[$lang] :
|
||||
'Not available in this language';
|
||||
$names[] = $cf->getName()[$lang] ?? 'Not available in this language';
|
||||
}
|
||||
|
||||
if ($this->validator->validate($cf)) {
|
||||
@ -249,9 +247,7 @@ class CreateFieldsOnGroupCommand extends Command
|
||||
|
||||
foreach ($languages as $lang) {
|
||||
//todo replace with service to find lang when available
|
||||
$row[] = (isset($customFieldGroup->getName()[$lang])) ?
|
||||
$customFieldGroup->getName()[$lang] :
|
||||
'Not available in this language';
|
||||
$row[] = $customFieldGroup->getName()[$lang] ?? 'Not available in this language';
|
||||
}
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ class CustomFieldChoice extends AbstractCustomField
|
||||
public function render($value, CustomField $customField, $documentType = 'html')
|
||||
{
|
||||
//extract the data. They are under a _choice key if they are stored with allow_other
|
||||
$data = (isset($value['_choices'])) ? $value['_choices'] : $value;
|
||||
$data = $value['_choices'] ?? $value;
|
||||
$selected = (is_array($data)) ? $data : [$data];
|
||||
$choices = $customField->getOptions()[self::CHOICES];
|
||||
|
||||
|
@ -58,9 +58,7 @@ class CustomFieldLongChoice extends AbstractCustomField
|
||||
$translatableStringHelper = $this->translatableStringHelper;
|
||||
$builder->add($customField->getSlug(), Select2ChoiceType::class, [
|
||||
'choices' => $entries,
|
||||
'choice_label' => static function (Option $option) use ($translatableStringHelper) {
|
||||
return $translatableStringHelper->localize($option->getText());
|
||||
},
|
||||
'choice_label' => static fn (Option $option) => $translatableStringHelper->localize($option->getText()),
|
||||
'choice_value' => static fn (Option $key): ?int => null === $key ? null : $key->getId(),
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
|
@ -104,7 +104,7 @@ class LoadOption extends AbstractFixture implements OrderedFixtureInterface
|
||||
$manager->persist($parent);
|
||||
|
||||
//Load children
|
||||
$expected_nb_children = mt_rand(10, 50);
|
||||
$expected_nb_children = random_int(10, 50);
|
||||
|
||||
for ($i = 0; $i < $expected_nb_children; ++$i) {
|
||||
$companyName = $this->fakerFr->company;
|
||||
@ -144,7 +144,7 @@ class LoadOption extends AbstractFixture implements OrderedFixtureInterface
|
||||
$manager->persist($parent);
|
||||
|
||||
//Load children
|
||||
$expected_nb_children = mt_rand(10, 50);
|
||||
$expected_nb_children = random_int(10, 50);
|
||||
|
||||
for ($i = 0; $i < $expected_nb_children; ++$i) {
|
||||
$manager->persist($this->createChildOption($parent, [
|
||||
|
@ -25,7 +25,7 @@ class Configuration implements ConfigurationInterface
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_custom_fields');
|
||||
$rootNode = $treeBuilder->getRootNode('chill_custom_fields');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
$classInfo = 'The class which may receive custom fields';
|
||||
$nameInfo = 'The name which will appears in the user interface. May be translatable';
|
||||
|
@ -57,8 +57,6 @@ class OptionRepository extends EntityRepository
|
||||
->getQuery()
|
||||
->getScalarResult();
|
||||
|
||||
return array_map(static function ($r) {
|
||||
return $r['key'];
|
||||
}, $keys);
|
||||
return array_map(static fn ($r) => $r['key'], $keys);
|
||||
}
|
||||
}
|
||||
|
@ -70,9 +70,7 @@ class CustomFieldType extends AbstractType
|
||||
if ('entity' === $options['group_widget']) {
|
||||
$builder->add('customFieldsGroup', EntityType::class, [
|
||||
'class' => 'ChillCustomFieldsBundle:CustomFieldsGroup',
|
||||
'choice_label' => function ($g) {
|
||||
return $this->translatableStringHelper->localize($g->getName());
|
||||
},
|
||||
'choice_label' => fn ($g) => $this->translatableStringHelper->localize($g->getName()),
|
||||
]);
|
||||
} elseif ('hidden' === $options['group_widget']) {
|
||||
$builder->add('customFieldsGroup', HiddenType::class);
|
||||
@ -119,7 +117,7 @@ class CustomFieldType extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => 'Chill\CustomFieldsBundle\Entity\CustomField',
|
||||
'data_class' => \Chill\CustomFieldsBundle\Entity\CustomField::class,
|
||||
]);
|
||||
|
||||
$resolver->setRequired(['type', 'group_widget'])
|
||||
|
@ -38,9 +38,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface
|
||||
|
||||
// @TODO: in the array_map callback, CustomField::getLabel() does not exist. What do we do here?
|
||||
$customFieldsLablels = array_map(
|
||||
static function ($e) {
|
||||
return $e->getLabel();
|
||||
},
|
||||
static fn ($e) => $e->getLabel(),
|
||||
$customFields
|
||||
);
|
||||
|
||||
@ -108,7 +106,7 @@ class JsonCustomFieldToArrayTransformer implements DataTransformerInterface
|
||||
|
||||
//echo json_encode($customFieldsArrayRet);
|
||||
|
||||
return json_encode($customFieldsArrayRet);
|
||||
return json_encode($customFieldsArrayRet, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
public function transform($customFieldsJSON)
|
||||
|
@ -38,7 +38,7 @@ class ChoicesListType extends AbstractType
|
||||
$formData = $form->getData();
|
||||
|
||||
if (null === $formData['slug']) {
|
||||
$slug = uniqid(mt_rand(), true);
|
||||
$slug = uniqid(random_int(0, mt_getrandmax()), true);
|
||||
|
||||
$data['slug'] = $slug;
|
||||
$event->setData($data);
|
||||
|
@ -49,7 +49,7 @@ class CustomFieldType extends AbstractType
|
||||
{
|
||||
$resolver
|
||||
->setRequired(['group'])
|
||||
->addAllowedTypes('group', ['Chill\CustomFieldsBundle\Entity\CustomFieldsGroup']);
|
||||
->addAllowedTypes('group', [\Chill\CustomFieldsBundle\Entity\CustomFieldsGroup::class]);
|
||||
}
|
||||
|
||||
public function getBlockPrefix()
|
||||
|
@ -49,7 +49,7 @@ class CustomFieldsHelper
|
||||
public function isEmptyValue(array $fields, CustomField $customField)
|
||||
{
|
||||
$slug = $customField->getSlug();
|
||||
$rawValue = (isset($fields[$slug])) ? $fields[$slug] : null;
|
||||
$rawValue = $fields[$slug] ?? null;
|
||||
$customFieldType = $this->provider->getCustomFieldByType($customField->getType());
|
||||
|
||||
$deserializedValue = $customFieldType->deserialize($rawValue, $customField);
|
||||
@ -71,7 +71,7 @@ class CustomFieldsHelper
|
||||
public function renderCustomField(array $fields, CustomField $customField, $documentType = 'html')
|
||||
{
|
||||
$slug = $customField->getSlug();
|
||||
$rawValue = (isset($fields[$slug])) ? $fields[$slug] : null;
|
||||
$rawValue = $fields[$slug] ?? null;
|
||||
$customFieldType = $this->provider->getCustomFieldByType($customField->getType());
|
||||
|
||||
return $customFieldType->render($rawValue, $customField, $documentType);
|
||||
|
@ -52,7 +52,7 @@ final class CustomFieldsGroupControllerTest extends WebTestCase
|
||||
// Fill in the form and submit it
|
||||
$form = $crawler->selectButton('Créer')->form([
|
||||
'custom_fields_group[name][fr]' => 'Test',
|
||||
'custom_fields_group[entity]' => 'Chill\PersonBundle\Entity\Person',
|
||||
'custom_fields_group[entity]' => \Chill\PersonBundle\Entity\Person::class,
|
||||
]);
|
||||
|
||||
$crawler = $client->submit($form);
|
||||
|
@ -34,12 +34,12 @@ trait CustomFieldTestHelper
|
||||
$kernel = static::$kernel;
|
||||
|
||||
//check a kernel is accessible
|
||||
$customFieldsGroup = $this->createMock('Chill\CustomFieldsBundle\Entity\CustomFieldsGroup');
|
||||
$customFieldsGroup = $this->createMock(\Chill\CustomFieldsBundle\Entity\CustomFieldsGroup::class);
|
||||
$customFieldsGroup->expects($this->once())
|
||||
->method('getActiveCustomFields')
|
||||
->will($this->returnValue([$field]));
|
||||
|
||||
$request = $this->createMock('Symfony\Component\HttpFoundation\Request');
|
||||
$request = $this->createMock(\Symfony\Component\HttpFoundation\Request::class);
|
||||
$request->expects($this->any())
|
||||
->method('getLocale')
|
||||
->will($this->returnValue($locale));
|
||||
|
@ -41,11 +41,11 @@ final class CustomFieldsTextTest extends WebTestCase
|
||||
$customField = $this->customFieldProvider->getCustomFieldByType('text');
|
||||
|
||||
$this->assertInstanceOf(
|
||||
'Chill\CustomFieldsBundle\CustomFields\CustomFieldInterface',
|
||||
\Chill\CustomFieldsBundle\CustomFields\CustomFieldInterface::class,
|
||||
$customField
|
||||
);
|
||||
$this->assertInstanceOf(
|
||||
'Chill\CustomFieldsBundle\CustomFields\CustomFieldText',
|
||||
\Chill\CustomFieldsBundle\CustomFields\CustomFieldText::class,
|
||||
$customField
|
||||
);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ final class CustomFieldRenderingTwigTest extends KernelTestCase
|
||||
// set locale to fr
|
||||
$prophet = new \Prophecy\Prophet();
|
||||
$request = $prophet->prophesize();
|
||||
$request->willExtend('Symfony\Component\HttpFoundation\Request');
|
||||
$request->willExtend(\Symfony\Component\HttpFoundation\Request::class);
|
||||
$request->getLocale()->willReturn('fr');
|
||||
self::$kernel->getContainer()->get('request_stack')
|
||||
->push($request->reveal());
|
||||
|
@ -48,7 +48,7 @@ final class CustomFieldsGroupRenderingTwigTest extends KernelTestCase
|
||||
// set locale to fr
|
||||
$prophet = new \Prophecy\Prophet();
|
||||
$request = $prophet->prophesize();
|
||||
$request->willExtend('Symfony\Component\HttpFoundation\Request');
|
||||
$request->willExtend(\Symfony\Component\HttpFoundation\Request::class);
|
||||
$request->getLocale()->willReturn('fr');
|
||||
self::$kernel->getContainer()->get('request_stack')
|
||||
->push($request->reveal());
|
||||
|
@ -61,12 +61,8 @@ class DocGeneratorTemplateType extends AbstractType
|
||||
$sub = $builder
|
||||
->create('options', null, ['compound' => true])
|
||||
->addModelTransformer(new CallbackTransformer(
|
||||
static function (array $data) use ($context) {
|
||||
return $context->adminFormTransform($data);
|
||||
},
|
||||
static function (array $data) use ($context) {
|
||||
return $context->adminFormReverseTransform($data);
|
||||
}
|
||||
static fn (array $data) => $context->adminFormTransform($data),
|
||||
static fn (array $data) => $context->adminFormReverseTransform($data)
|
||||
));
|
||||
$context->buildAdminForm($sub);
|
||||
$builder->add($sub);
|
||||
|
@ -44,7 +44,7 @@ final class RelatorioDriver implements DriverInterface
|
||||
{
|
||||
$form = new FormDataPart(
|
||||
[
|
||||
'variables' => json_encode($data),
|
||||
'variables' => json_encode($data, JSON_THROW_ON_ERROR),
|
||||
'template' => new DataPart($template, $templateName ?? uniqid('template_'), $resourceType),
|
||||
]
|
||||
);
|
||||
@ -61,7 +61,7 @@ final class RelatorioDriver implements DriverInterface
|
||||
$content = $e->getResponse()->getContent(false);
|
||||
|
||||
if (400 === $e->getResponse()->getStatusCode()) {
|
||||
$content = json_decode($content, true);
|
||||
$content = json_decode($content, true, 512, JSON_THROW_ON_ERROR);
|
||||
$this->logger->error('relatorio: template error', [
|
||||
'error' => $content['message'] ?? '_not defined',
|
||||
]);
|
||||
|
@ -13,6 +13,7 @@ namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
|
||||
|
||||
use ArrayObject;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\ReadableCollection;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
||||
@ -51,7 +52,9 @@ class CollectionDocGenNormalizer implements ContextAwareNormalizerInterface, Nor
|
||||
return false;
|
||||
}
|
||||
|
||||
return $data instanceof Collection
|
||||
|| (null === $data && Collection::class === ($context['docgen:expects'] ?? null));
|
||||
return $data instanceof ReadableCollection
|
||||
|| (null === $data && Collection::class === ($context['docgen:expects'] ?? null))
|
||||
|| (null === $data && ReadableCollection::class === ($context['docgen:expects'] ?? null))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
|
||||
|
||||
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Doctrine\Common\Collections\ReadableCollection;
|
||||
use ReflectionClass;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
@ -271,6 +272,14 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
|
||||
if ($isTranslatable) {
|
||||
$data[$key] = $this->translatableStringHelper
|
||||
->localize($value);
|
||||
} elseif ($value instanceof ReadableCollection) {
|
||||
// when normalizing collection, we should not preserve keys (to ensure that the result is a list)
|
||||
// this is why we make call to the normalizer again to use the CollectionDocGenNormalizer
|
||||
$data[$key] =
|
||||
$this->normalizer->normalize($value, $format, array_merge(
|
||||
$objectContext,
|
||||
$attribute->getNormalizationContextForGroups($expectedGroups)
|
||||
));
|
||||
} elseif (is_iterable($value)) {
|
||||
$arr = [];
|
||||
|
||||
|
@ -17,6 +17,6 @@ class ObjectReadyException extends RuntimeException
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('object is already ready', 6698856);
|
||||
parent::__construct('object is already ready', 6_698_856);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class RelatedEntityNotFoundException extends RuntimeException
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf('Related entity not found: %s, %s', $relatedEntityClass, $relatedEntityId),
|
||||
99876652,
|
||||
99_876_652,
|
||||
$previous
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class CollectionDocGenNormalizerTest extends KernelTestCase
|
||||
{
|
||||
private NormalizerInterface $normalizer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel();
|
||||
$this->normalizer = self::$container->get(NormalizerInterface::class);
|
||||
}
|
||||
|
||||
public function testNormalizeFilteredArray(): void
|
||||
{
|
||||
$coll = new ArrayCollection([
|
||||
(object) ['v' => 'foo'],
|
||||
(object) ['v' => 'bar'],
|
||||
(object) ['v' => 'baz'],
|
||||
]);
|
||||
|
||||
//filter to get non continuous indexes
|
||||
$criteria = new Criteria();
|
||||
$criteria->where(Criteria::expr()->neq('v', 'bar'));
|
||||
|
||||
$filtered = $coll->matching($criteria);
|
||||
$normalized = $this->normalizer->normalize($filtered, 'docgen', []);
|
||||
|
||||
self::assertIsArray($normalized);
|
||||
self::assertArrayHasKey(0, $normalized);
|
||||
self::assertArrayHasKey(1, $normalized);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ class Configuration implements ConfigurationInterface
|
||||
public function getConfigTreeBuilder()
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('chill_doc_store');
|
||||
$rootNode = $treeBuilder->getRootNode('chill_doc_store');
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
|
||||
// Here you should define the parameters that are allowed to
|
||||
// configure your bundle. See the documentation linked above for
|
||||
|
@ -58,7 +58,7 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
|
||||
* @ORM\Column(type="integer")
|
||||
* @Serializer\Groups({"read", "write"})
|
||||
*/
|
||||
private ?int $id;
|
||||
private ?int $id = null;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
@ -94,7 +94,7 @@ class StoredObject implements AsyncFileInterface, Document, TrackCreationInterfa
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity=DocGeneratorTemplate::class)
|
||||
*/
|
||||
private ?DocGeneratorTemplate $template;
|
||||
private ?DocGeneratorTemplate $template = null;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", options={"default": "ready"})
|
||||
|
@ -72,14 +72,10 @@ class AccompanyingCourseDocumentType extends AbstractType
|
||||
->add('category', EntityType::class, [
|
||||
'placeholder' => 'Choose a document category',
|
||||
'class' => DocumentCategory::class,
|
||||
'query_builder' => static function (EntityRepository $er) {
|
||||
return $er->createQueryBuilder('c')
|
||||
->where('c.documentClass = :docClass')
|
||||
->setParameter('docClass', AccompanyingCourseDocument::class);
|
||||
},
|
||||
'choice_label' => function ($entity = null) {
|
||||
return $entity ? $this->translatableStringHelper->localize($entity->getName()) : '';
|
||||
},
|
||||
'query_builder' => static fn (EntityRepository $er) => $er->createQueryBuilder('c')
|
||||
->where('c.documentClass = :docClass')
|
||||
->setParameter('docClass', AccompanyingCourseDocument::class),
|
||||
'choice_label' => fn ($entity = null) => $entity ? $this->translatableStringHelper->localize($entity->getName()) : '',
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -67,14 +67,10 @@ class PersonDocumentType extends AbstractType
|
||||
->add('category', EntityType::class, [
|
||||
'placeholder' => 'Choose a document category',
|
||||
'class' => DocumentCategory::class,
|
||||
'query_builder' => static function (EntityRepository $er) {
|
||||
return $er->createQueryBuilder('c')
|
||||
->where('c.documentClass = :docClass')
|
||||
->setParameter('docClass', PersonDocument::class);
|
||||
},
|
||||
'choice_label' => function ($entity = null) {
|
||||
return $entity ? $this->translatableStringHelper->localize($entity->getName()) : '';
|
||||
},
|
||||
'query_builder' => static fn (EntityRepository $er) => $er->createQueryBuilder('c')
|
||||
->where('c.documentClass = :docClass')
|
||||
->setParameter('docClass', PersonDocument::class),
|
||||
'choice_label' => fn ($entity = null) => $entity ? $this->translatableStringHelper->localize($entity->getName()) : '',
|
||||
]);
|
||||
|
||||
if ($isScopeConcerned && $this->parameterBag->get('chill_main')['acl']['form_show_scopes']) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user