From f6801c0c4f75aeb2bc1813cf92ea4f45a2cf72b9 Mon Sep 17 00:00:00 2001 From: Mathieu Jaumotte Date: Fri, 26 Mar 2021 21:54:58 +0100 Subject: [PATCH] make new relation many-to-many between Person and AccompagnyingPeriod --- .../AccompanyingPeriodController.php | 53 +++++----- .../Entity/AccompanyingPeriod.php | 97 ++++++++++++------- .../ChillPersonBundle/Entity/Person.php | 62 ++++++------ .../views/AccompanyingPeriod/list.html.twig | 2 +- .../migrations/Version20210326113045.php | 76 +++++++++++++++ .../translations/validators.fr.yml | 2 +- 6 files changed, 196 insertions(+), 96 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/migrations/Version20210326113045.php diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php index 0db5e170f..21ea358e7 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingPeriodController.php @@ -3,7 +3,7 @@ /* * Chill is a software for social workers * - * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, + * Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, * , * * This program is free software: you can redistribute it and/or modify @@ -23,6 +23,7 @@ namespace Chill\PersonBundle\Controller; use Chill\PersonBundle\Privacy\PrivacyEvent; +use Doctrine\DBAL\Exception; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Form\AccompanyingPeriodType; @@ -56,10 +57,10 @@ class AccompanyingPeriodController extends AbstractController } /** - * @param $person_id * @return Response */ - public function listAction($person_id){ + public function listAction(int $person_id) + { $person = $this->_getPerson($person_id); @@ -76,11 +77,9 @@ class AccompanyingPeriodController extends AbstractController } /** - * @param $person_id - * @param Request $request * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response */ - public function createAction($person_id, Request $request) + public function createAction(int $person_id, Request $request) { $person = $this->_getPerson($person_id); @@ -139,23 +138,24 @@ class AccompanyingPeriodController extends AbstractController } /** - * @param $person_id - * @param $period_id - * @param Request $request * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response|\Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ - public function updateAction($person_id, $period_id, Request $request){ + public function updateAction(int $person_id, int $period_id, Request $request){ $em = $this->getDoctrine()->getManager(); - $accompanyingPeriod = $em->getRepository('ChillPersonBundle:AccompanyingPeriod') - ->find($period_id); + /** @var AccompanyingPeriod $accompanyingPeriod */ + $accompanyingPeriod = $em->getRepository(AccompanyingPeriod::class)->find($period_id); if ($accompanyingPeriod === null) { - return $this->createNotFoundException("Period with id ".$period_id. - " is not found"); + throw $this->createNotFoundException("Period with id " . $period_id . " is not found"); } - $person = $accompanyingPeriod->getPerson(); + /** @var Person $person */ + $person = $em->getRepository(Person::class)->find($person_id); + + if (! $accompanyingPeriod->containsPerson($person)) { + throw new Exception("Accompanying period " . $period_id . " does not contain person " . $person_id); + } $this->denyAccessUnlessGranted(PersonVoter::UPDATE, $person, 'You are not allowed to update this person'); @@ -203,12 +203,10 @@ class AccompanyingPeriodController extends AbstractController } /** - * @param $person_id - * @param Request $request * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response * @throws \Exception */ - public function closeAction($person_id, Request $request) + public function closeAction(int $person_id, Request $request) { $person = $this->_getPerson($person_id); @@ -290,7 +288,6 @@ class AccompanyingPeriodController extends AbstractController } /** - * @param Person $person * @return \Symfony\Component\Validator\ConstraintViolationListInterface */ private function _validatePerson(Person $person) { @@ -307,8 +304,6 @@ class AccompanyingPeriodController extends AbstractController } /** - * @param $person_id - * @param Request $request * @return \Symfony\Component\HttpFoundation\RedirectResponse|Response */ public function openAction($person_id, Request $request) { @@ -388,13 +383,11 @@ class AccompanyingPeriodController extends AbstractController } /** - * @param $person_id - * @param $period_id - * @param Request $request * @return object|\Symfony\Component\HttpFoundation\RedirectResponse|Response */ - public function reOpenAction($person_id, $period_id, Request $request) + public function reOpenAction(int $person_id, int $period_id, Request $request) { + /** @var Person $person */ $person = $this->_getPerson($person_id); $criteria = Criteria::create(); @@ -411,7 +404,7 @@ class AccompanyingPeriodController extends AbstractController $confirm = $request->query->getBoolean('confirm', false); - if ($confirm === true && $period->canBeReOpened()) { + if ($confirm === true && $period->canBeReOpened($person)) { $period->reOpen(); $this->_validatePerson($person); @@ -425,7 +418,7 @@ class AccompanyingPeriodController extends AbstractController 'person_id' => $person->getId() ]); - } elseif ($confirm === false && $period->canBeReOpened()) { + } elseif ($confirm === false && $period->canBeReOpened($person)) { return $this->render('ChillPersonBundle:AccompanyingPeriod:re_open.html.twig', [ 'period' => $period, 'person' => $person @@ -439,12 +432,10 @@ class AccompanyingPeriodController extends AbstractController } /** - * - * @param int $id - * @return Person * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException if the person is not found */ - private function _getPerson($id) { + private function _getPerson(int $id) : Person + { $person = $this->getDoctrine()->getManager() ->getRepository('ChillPersonBundle:Person')->find($id); diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 255cf056f..63a8b67d3 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -3,7 +3,7 @@ /* * Chill is a software for social workers * - * Copyright (C) 2014-2015, Champs Libres Cooperative SCRLFS, + * Copyright (C) 2014-2021, Champs Libres Cooperative SCRLFS, * , * * This program is free software: you can redistribute it and/or modify @@ -22,6 +22,8 @@ namespace Chill\PersonBundle\Entity; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Chill\MainBundle\Entity\User; @@ -65,14 +67,13 @@ class AccompanyingPeriod private $remark = ''; /** - * @var Person + * @var Collection * - * @ORM\ManyToOne( + * @ORM\ManyToMany( * targetEntity="Chill\PersonBundle\Entity\Person", - * inversedBy="accompanyingPeriods", - * cascade={"refresh"}) + * mappedBy="accompanyingPeriods") */ - private $person; + private $persons; /** * @var AccompanyingPeriod\ClosingMotive @@ -101,12 +102,13 @@ class AccompanyingPeriod */ public function __construct(\DateTime $dateOpening) { $this->setOpeningDate($dateOpening); + $this->persons = new ArrayCollection(); } /** * Get id * - * @return integer + * @return integer */ public function getId() { @@ -129,7 +131,7 @@ class AccompanyingPeriod /** * Get openingDate * - * @return \DateTime + * @return \DateTime */ public function getOpeningDate() { @@ -138,12 +140,12 @@ class AccompanyingPeriod /** * Set closingDate - * + * * For closing a Person file, you should use Person::setClosed instead. * * @param \DateTime $dateClosing * @return AccompanyingPeriod - * + * */ public function setClosingDate($closingDate) { @@ -155,7 +157,7 @@ class AccompanyingPeriod /** * Get closingDate * - * @return \DateTime + * @return \DateTime */ public function getClosingDate() { @@ -165,7 +167,7 @@ class AccompanyingPeriod /** * @return boolean */ - public function isOpen(): bool + public function isOpen(): bool { if ($this->getOpeningDate() > new \DateTime('now')) { return false; @@ -198,7 +200,7 @@ class AccompanyingPeriod /** * Get remark * - * @return string + * @return string */ public function getRemark() { @@ -206,29 +208,53 @@ class AccompanyingPeriod } /** - * Set person. + * Set persons + */ + public function setPersons($persons) : AccompanyingPeriod + { + $this->persons = $persons; + + return $this; + } + + /** + * Get Persons + */ + public function getPersons() : Collection + { + return $this->persons; + } + + /** + * Return true if a given Person is associated + */ + public function containsPerson(Person $person) : bool + { + foreach ($this->persons as $p) { + if ($p === $person) { return true; } + } + return false; + } + + /** + * Add person. * * For consistency, you should use Person::addAccompanyingPeriod instead. - * - * @param Person $person - * @return AccompanyingPeriod * @see Person::addAccompanyingPeriod */ - public function setPerson(Person $person = null) + public function addPerson(Person $person = null) : AccompanyingPeriod { - $this->person = $person; + $this->persons[] = $person; return $this; } - + /** - * Get person - * - * @return Person + * Remove person. */ - public function getPerson() + public function removePerson(Person $person) : void { - return $this->person; + $this->persons->removeElement($person); } /** @@ -251,21 +277,24 @@ class AccompanyingPeriod /** * If the period can be reopened. - * - * This function test if the period is closed and if the period is the last - * for the associated person - * - * @return boolean + * + * This function test if the period is closed and if the period is the last + * for the given person */ - public function canBeReOpened() + public function canBeReOpened(Person $person) : bool { if ($this->isOpen() === true) { return false; } - $periods = $this->getPerson()->getAccompanyingPeriodsOrdered(); + dump('parcours fermé: '. $this->getId()); - return end($periods) === $this; + foreach ($this->getPersons() as $p) { + if ($p === $person) { + $periods = $p->getAccompanyingPeriodsOrdered(); + return end($periods) === $this; // retourne TRUE si cette période est la dernière + } + } } /** @@ -294,7 +323,7 @@ class AccompanyingPeriod /** * Returns true if the closing date is after the opening date. - * + * * @return boolean */ public function isClosingAfterOpening() diff --git a/src/Bundle/ChillPersonBundle/Entity/Person.php b/src/Bundle/ChillPersonBundle/Entity/Person.php index 36993ce3b..e0bc73990 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Person.php +++ b/src/Bundle/ChillPersonBundle/Entity/Person.php @@ -206,10 +206,13 @@ class Person implements HasCenterInterface * The person's accompanying periods (when the person was accompanied by the center) * @var ArrayCollection * - * @ORM\OneToMany( + * @ORM\ManyToMany( * targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", - * mappedBy="person", + * inversedBy="persons", * cascade={"persist", "remove", "merge", "detach"}) + * @ORM\JoinTable( + * name="persons_accompanying_periods" + * ) */ private $accompanyingPeriods; //TO-CHANGE in accompanyingHistory @@ -277,19 +280,20 @@ class Person implements HasCenterInterface } /** - * @param AccompanyingPeriod $accompanyingPeriod - * @uses AccompanyingPeriod::setPerson + * Add AccompanyingPeriod + * + * @uses AccompanyingPeriod::addPerson */ - public function addAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod) + public function addAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod) : void { - $accompanyingPeriod->setPerson($this); + $accompanyingPeriod->addPerson($this); $this->accompanyingPeriods->add($accompanyingPeriod); } /** - * @param AccompanyingPeriod $accompanyingPeriod + * Remove AccompanyingPeriod */ - public function removeAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod) + public function removeAccompanyingPeriod(AccompanyingPeriod $accompanyingPeriod) : void { $this->accompanyingPeriods->remove($accompanyingPeriod); } @@ -303,10 +307,8 @@ class Person implements HasCenterInterface * For closing a file, @see this::close * * To check if the Person and its accompanying period is consistent, use validation. - * - * @param AccompanyingPeriod $accompanyingPeriod */ - public function open(AccompanyingPeriod $accompanyingPeriod) + public function open(AccompanyingPeriod $accompanyingPeriod) : void { $this->proxyAccompanyingPeriodOpenState = true; $this->addAccompanyingPeriod($accompanyingPeriod); @@ -320,20 +322,17 @@ class Person implements HasCenterInterface * * To check if the Person and its accompanying period are consistent, use validation. * - * @param accompanyingPeriod * @throws \Exception if two lines of the accompanying period are open. */ - public function close(AccompanyingPeriod $accompanyingPeriod = null) + public function close(AccompanyingPeriod $accompanyingPeriod = null) : void { $this->proxyAccompanyingPeriodOpenState = false; } /** * Return the opened accompanying period. - * - * @return AccompanyingPeriod */ - public function getOpenedAccompanyingPeriod() + public function getOpenedAccompanyingPeriod() : AccompanyingPeriod { if ($this->isOpen() === false) { return null; @@ -349,29 +348,25 @@ class Person implements HasCenterInterface /** * Returns the opened accompanying period. * - * @return AccompanyingPeriod * @deprecated since 1.1 use `getOpenedAccompanyingPeriod instead */ - public function getCurrentAccompanyingPeriod() + public function getCurrentAccompanyingPeriod() : AccompanyingPeriod { return $this->getOpenedAccompanyingPeriod(); } /** - * @return ArrayCollection + * Get AccompanyingPeriods Collection */ - public function getAccompanyingPeriods() + public function getAccompanyingPeriods() : Collection { return $this->accompanyingPeriods; } /** - * Get the accompanying periods of a give person with the - * chronological order. - * - * @return AccompanyingPeriod[] + * Get the accompanying periods of a give person with the chronological order. */ - public function getAccompanyingPeriodsOrdered() + public function getAccompanyingPeriodsOrdered() : array { $periods = $this->getAccompanyingPeriods()->toArray(); @@ -406,11 +401,9 @@ class Person implements HasCenterInterface } /** - * check if the person is opened - * - * @return boolean + * Check if the person is opened */ - public function isOpen() + public function isOpen() : bool { foreach ($this->getAccompanyingPeriods() as $period) { if ($period->isOpen()) { @@ -1051,4 +1044,15 @@ class Person implements HasCenterInterface return true; } + + public function getFullnameCanonical() : string + { + return $this->fullnameCanonical; + } + + public function setFullnameCanonical($fullnameCanonical) : Person + { + $this->fullnameCanonical = $fullnameCanonical; + return $this; + } } diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/list.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/list.html.twig index 4d82813d0..54b03162b 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/list.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingPeriod/list.html.twig @@ -63,7 +63,7 @@ {% endif %} - {% if accompanying_period.canBeReOpened == true %} + {% if accompanying_period.canBeReOpened(person) == true %}
  • {{'Re-open accompanying period'|trans }} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210326113045.php b/src/Bundle/ChillPersonBundle/migrations/Version20210326113045.php new file mode 100644 index 000000000..6db7e1521 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210326113045.php @@ -0,0 +1,76 @@ +connection->prepare($query); + $data->execute(); + foreach ($data as $row) + { + $personId = $row['person_id']; + $accompagnyingPeriodId = $row['accompagnying_period_id']; + $this->datas[] = "($personId, $accompagnyingPeriodId)"; + } + } + + public function up(Schema $schema) : void + { + // create join table + $this->addSql('CREATE TABLE persons_accompanying_periods (person_id INT NOT NULL, accompanyingperiod_id INT NOT NULL, PRIMARY KEY(person_id, accompanyingperiod_id))'); + $this->addSql('CREATE INDEX IDX_49A3871F217BBB47 ON persons_accompanying_periods (person_id)'); + $this->addSql('CREATE INDEX IDX_49A3871F550B0C53 ON persons_accompanying_periods (accompanyingperiod_id)'); + $this->addSql('ALTER TABLE persons_accompanying_periods ADD CONSTRAINT FK_49A3871F217BBB47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE persons_accompanying_periods ADD CONSTRAINT FK_49A3871F550B0C53 FOREIGN KEY (accompanyingperiod_id) REFERENCES chill_person_accompanying_period (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + + // drop column + $this->addSql('DROP INDEX idx_64a4a621217bbb47'); + $this->addSql('ALTER TABLE chill_person_accompanying_period DROP CONSTRAINT fk_64a4a621217bbb47'); + $this->addSql('ALTER TABLE chill_person_accompanying_period DROP person_id'); + } + + public function postUp(Schema $schema) : void + { + // insert datas in new join table + $SQL = "INSERT INTO persons_accompanying_periods (person_id, accompanyingperiod_id) VALUES " . implode(', ', $this->datas); + $this->connection->executeQuery($SQL); + } + + + public function down(Schema $schema) : void + { + // drop join table + //$this->addSql('DROP TABLE persons_accompanying_periods'); + + // INFO this dangerous query is commented, then if you go down you must manually : + // * insert datas in TABLE chill_person_accompanying_period COLUMN person_id ; + // * drop join TABLE persons_accompanying_periods when you are sure it works + + // add column + $this->addSql('ALTER TABLE chill_person_accompanying_period ADD person_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_person_accompanying_period ADD CONSTRAINT fk_64a4a621217bbb47 FOREIGN KEY (person_id) REFERENCES chill_person_person (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX idx_64a4a621217bbb47 ON chill_person_accompanying_period (person_id)'); + } +} diff --git a/src/Bundle/ChillPersonBundle/translations/validators.fr.yml b/src/Bundle/ChillPersonBundle/translations/validators.fr.yml index 22cb4c719..9f2030f02 100644 --- a/src/Bundle/ChillPersonBundle/translations/validators.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/validators.fr.yml @@ -10,7 +10,7 @@ 'Opening date can not be null': 'La date d''ouverure ne peut être nulle' 'Closing date is not valid': 'La date de fermeture n''est pas valide' 'Closing date can not be null': 'La date de fermeture ne peut être nulle' -The date of closing is before the date of opening: La période de fermeture est après la période d'ouverture +The date of closing is before the date of opening: La période de fermeture est avant la période d'ouverture The birthdate must be before %date%: La date de naissance doit être avant le %date% 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33123456789': 'Numéro de téléphone invalide: il doit commencer par le préfixe international précédé de "+", ne comporter que des chiffres et faire moins de 20 caractères. Ex: +31623456789' 'Invalid phone number: it should begin with the international prefix starting with "+", hold only digits and be smaller than 20 characters. Ex: +33623456789': 'Numéro de téléphone invalide: il doit commencer par le préfixe international précédé de "+", ne comporter que des chiffres et faire moins de 20 caractères. Ex: +33623456789'