From 09e5cc1545aee73d2491594098ced0463717ee22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Sat, 21 Aug 2021 23:18:55 +0200 Subject: [PATCH] create api for social issue consistency --- ...dLinkedWithSocialIssuesEntityInterface.php | 28 ++++ ...odSocialIssueConsistencyEntityListener.php | 66 +++++++++ .../AccompanyingPeriodWork.php | 4 +- .../Entity/SocialWork/SocialIssue.php | 53 +++++++ ...cialIssueConsistencyEntityListenerTest.php | 140 ++++++++++++++++++ .../Entity/SocialWork/SocialIssueTest.php | 51 +++++++ 6 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 src/Bundle/ChillPersonBundle/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodLinkedWithSocialIssuesEntityInterface.php create mode 100644 src/Bundle/ChillPersonBundle/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListener.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php create mode 100644 src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php diff --git a/src/Bundle/ChillPersonBundle/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodLinkedWithSocialIssuesEntityInterface.php b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodLinkedWithSocialIssuesEntityInterface.php new file mode 100644 index 000000000..58f1dc32a --- /dev/null +++ b/src/Bundle/ChillPersonBundle/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodLinkedWithSocialIssuesEntityInterface.php @@ -0,0 +1,28 @@ +ensureConsistencyEntity($entity); + } + + public function preUpdate(AccompanyingPeriodLinkedWithSocialIssuesEntityInterface $entity, LifecycleEventArgs $eventArgs) + { + $this->ensureConsistencyEntity($entity); + } + + public function prePersistAccompanyingPeriod(AccompanyingPeriod $period, LifecycleEventArgs $eventArgs) + { + $this->ensureConsistencyAccompanyingPeriod($period); + } + + public function preUpdateAccompanyingPeriod(AccompanyingPeriod $period, LifecycleEventArgs $eventArgs) + { + $this->ensureConsistencyAccompanyingPeriod($period); + } + + private function ensureConsistencyEntity(AccompanyingPeriodLinkedWithSocialIssuesEntityInterface $entity): void + { + // remove issues parents on the entity itself + $ancestors = SocialIssue::findAncestorSocialIssues($entity->getSocialIssues()); + foreach ($ancestors as $ancestor) { + $entity->removeSocialIssue($ancestor); + } + + $period = $entity->getAccompanyingPeriod(); + + foreach ($entity->getSocialIssues() as $issue) { + // the entity itself test if the social issue is already associated, or not + $period->addSocialIssue($issue); + } + + $this->ensureConsistencyAccompanyingPeriod($period); + } + + private function ensureConsistencyAccompanyingPeriod(AccompanyingPeriod $period): void + { + $ancestors = SocialIssue::findAncestorSocialIssues($period->getSocialIssues()); + + foreach ($ancestors as $ancestor) { + $period->removeSocialIssue($ancestor); + } + } +} diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php index b0c60de82..39aaf309b 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php @@ -2,11 +2,13 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodLinkedWithSocialIssuesEntityInterface; use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\Person; use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Chill\ThirdPartyBundle\Entity\ThirdParty; use DateTimeInterface; use Doctrine\Common\Collections\ArrayCollection; @@ -470,4 +472,4 @@ use Symfony\Component\Validator\Constraints as Assert; return $this; } -} + } diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php index ae1c573f5..f9956e57f 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialIssue.php @@ -233,4 +233,57 @@ class SocialIssue return $recursiveSocialActions; } + + /** + * Recursive method which return true if the current $issue is a descendant + * of the $issue given in parameter. + * + * @param SocialIssue $issue + * @return bool + */ + public function isDescendantOf(SocialIssue $issue): bool + { + if (!$this->hasParent()) { + return false; + } + + if ($this->getParent() === $issue) { + return true; + } + + return $this->getParent()->isDescendantOf($issue); + } + + /** + * In a SocialIssues's collection, find the elements which are an ancestor of + * other elements. + * + * Removing those elements of the Collection (which is not done by this method) + * will ensure that only the most descendent elements are present in the collection, + * (any ancestor of another element are present). + * + * @param Collection|SocialIssue[] $socialIssues + * @return Collection|SocialIssue[] + */ + public static function findAncestorSocialIssues(Collection $socialIssues): Collection + { + $ancestors = new ArrayCollection(); + + foreach ($socialIssues as $candidateChild) { + if ($ancestors->contains($candidateChild)) { + continue; + } + foreach ($socialIssues as $candidateParent) { + if ($ancestors->contains($candidateParent)) { + continue; + } + + if ($candidateChild->isDescendantOf($candidateParent)) { + $ancestors->add($candidateParent); + } + } + } + + return $ancestors; + } } diff --git a/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php new file mode 100644 index 000000000..a94280dd0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/AccompanyingPeriod/SocialIssueConsistency/AccompanyingPeriodSocialIssueConsistencyEntityListenerTest.php @@ -0,0 +1,140 @@ +setParent($parent), + $grandChild = (new SocialIssue())->setParent($child), + $grandGrandChild = (new SocialIssue())->setParent($grandChild), + ]; + $period = new AccompanyingPeriod(); + $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener(); + + foreach ($arraySocialIssues as $issue) { + $period->addSocialIssue($issue); + } + + $consistency->prePersistAccompanyingPeriod($period, $this->generateLifecycleArgs()); + + $this->assertCount(1, $period->getSocialIssues()); + $this->assertSame($grandGrandChild, $period->getSocialIssues()->first()); + } + + public function testPreUpdate() + { + $socialIssues = new ArrayCollection([ + $parent = new SocialIssue(), + $child = (new SocialIssue())->setParent($parent), + $grandChild = (new SocialIssue())->setParent($child), + $grandGrandChild = (new SocialIssue())->setParent($grandChild), + ]); + $period = (new AccompanyingPeriod())->addSocialIssue($unrelated = new SocialIssue()); + $entity = $this->generateClass($period, $socialIssues); + $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener(); + + $consistency->preUpdate($entity, $this->generateLifecycleArgs()); + + $this->assertCount(2, $period->getSocialIssues()); + $this->assertContains($grandGrandChild, $period->getSocialIssues()); + $this->assertContains($unrelated, $period->getSocialIssues()); + + $this->assertCount(1, $entity->getSocialIssues()); + $this->assertContains($grandGrandChild, $entity->getSocialIssues()); + } + + public function testPrePersist() + { + $socialIssues = new ArrayCollection([ + $parent = new SocialIssue(), + $child = (new SocialIssue())->setParent($parent), + $grandChild = (new SocialIssue())->setParent($child), + $grandGrandChild = (new SocialIssue())->setParent($grandChild), + ]); + $period = (new AccompanyingPeriod())->addSocialIssue($unrelated = new SocialIssue()); + $entity = $this->generateClass($period, $socialIssues); + $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener(); + + $consistency->prePersist($entity, $this->generateLifecycleArgs()); + + $this->assertCount(2, $period->getSocialIssues()); + $this->assertContains($grandGrandChild, $period->getSocialIssues()); + $this->assertContains($unrelated, $period->getSocialIssues()); + + $this->assertCount(1, $entity->getSocialIssues()); + $this->assertContains($grandGrandChild, $entity->getSocialIssues()); + + } + + public function testPreUpdateAccompanyingPeriod() + { + $arraySocialIssues = [ + $parent = new SocialIssue(), + $child = (new SocialIssue())->setParent($parent), + $grandChild = (new SocialIssue())->setParent($child), + $grandGrandChild = (new SocialIssue())->setParent($grandChild), + ]; + $period = new AccompanyingPeriod(); + $consistency = new AccompanyingPeriodSocialIssueConsistencyEntityListener(); + + foreach ($arraySocialIssues as $issue) { + $period->addSocialIssue($issue); + } + + $consistency->prePersistAccompanyingPeriod($period, $this->generateLifecycleArgs()); + + $this->assertCount(1, $period->getSocialIssues()); + $this->assertSame($grandGrandChild, $period->getSocialIssues()->first()); + } + + protected function generateLifecycleArgs(): LifecycleEventArgs + { + return $this->createMock(LifecycleEventArgs::class); + } + + protected function generateClass(AccompanyingPeriod $period, Collection $socialIssues): AccompanyingPeriodLinkedWithSocialIssuesEntityInterface + { + return new class($period, $socialIssues) implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterface + { + public Collection $socialIssues; + public AccompanyingPeriod $period; + + public function __construct($period, $socialIssues) + { + $this->period = $period; + $this->socialIssues = $socialIssues; + } + + public function getAccompanyingPeriod(): AccompanyingPeriod + { + return $this->period; + } + + public function getSocialIssues(): Collection + { + return $this->socialIssues; + } + + public function removeSocialIssue(SocialIssue $issue): AccompanyingPeriodLinkedWithSocialIssuesEntityInterface + { + $this->socialIssues->removeElement($issue); + + return $this; + } + }; + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php b/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php new file mode 100644 index 000000000..16bff1199 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Entity/SocialWork/SocialIssueTest.php @@ -0,0 +1,51 @@ +setParent($parent); + $grandChild = (new SocialIssue())->setParent($child); + $grandGrandChild = (new SocialIssue())->setParent($grandChild); + $unrelated = new SocialIssue(); + + $this->assertTrue($grandGrandChild->isDescendantOf($parent)); + $this->assertTrue($grandGrandChild->isDescendantOf($grandChild)); + $this->assertTrue($grandGrandChild->isDescendantOf($child)); + $this->assertFalse($grandGrandChild->isDescendantOf($unrelated)); + + $this->assertTrue($grandChild->isDescendantOf($parent)); + $this->assertTrue($grandChild->isDescendantOf($child)); + $this->assertFalse($grandChild->isDescendantOf($unrelated)); + $this->assertFalse($grandChild->isDescendantOf($grandChild)); + + $this->assertFalse($unrelated->isDescendantOf($parent)); + + $this->assertFalse($child->isDescendantOf($grandChild)); + } + + public function testFindSocialIssuesAncestors() + { + $socialIssues = new ArrayCollection([ + $parent = new SocialIssue(), + $child = (new SocialIssue())->setParent($parent), + $grandChild = (new SocialIssue())->setParent($child), + $grandGrandChild = (new SocialIssue())->setParent($grandChild), + $unrelated = new SocialIssue(), + ]); + + $ancestors = SocialIssue::findAncestorSocialIssues($socialIssues); + + $this->assertCount(3, $ancestors); + $this->assertContains($parent, $ancestors); + $this->assertContains($child, $ancestors); + $this->assertContains($grandChild, $ancestors); + } +}