diff --git a/src/Bundle/ChillActivityBundle/Entity/Activity.php b/src/Bundle/ChillActivityBundle/Entity/Activity.php index dba4f9c62..d77e0250b 100644 --- a/src/Bundle/ChillActivityBundle/Entity/Activity.php +++ b/src/Bundle/ChillActivityBundle/Entity/Activity.php @@ -230,45 +230,39 @@ class Activity implements AccompanyingPeriodLinkedWithSocialIssuesEntityInterfac public function addSocialAction(SocialAction $socialAction): self { - $descendants = $socialAction->getDescendantsWithThis(); - $parent = $socialAction->getParent(); - - $parentKey = array_search($parent, $this->socialActions->toArray()); - - if (false !== $parentKey) { - $this->socialActions[$parentKey] = $socialAction; - // return $this; + if (!$this->socialActions->contains($socialAction)) { + $this->socialActions[] = $socialAction; + $this->ensureSocialActionConsistency(); } - if (count($descendants) > 0) { - foreach ($descendants as $d) { - $inCollection = $this->socialActions->contains($d); - if ($inCollection) { - return $this; - } - } - } - - $this->socialActions[] = $socialAction; return $this; } + private function ensureSocialActionConsistency(): void + { + $ancestors = SocialAction::findAncestorSocialActions($this->getSocialActions()); + + foreach ($ancestors as $ancestor) { + $this->removeSocialAction($ancestor); + } + } + + /** + * Add a social issue + * + * Note: the social issue consistency (the fact that only yougest social issues + * are kept) is processed by an entity listener: + * @see{\Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener} + * + * @param SocialIssue $socialIssue + * @return $this + */ public function addSocialIssue(SocialIssue $socialIssue): self { - $descendants = $socialIssue->getDescendantsWithThis(); - - if (count($descendants) > 0) { - foreach ($descendants as $d) { - $inCollection = $this->socialIssues->contains($d); - if ($inCollection) { - return $this; - } - } + if (!$this->socialIssues->contains($socialIssue)) { + $this->socialIssues[] = $socialIssue; } - $this->socialIssues[] = $socialIssue; - - return $this; } diff --git a/src/Bundle/ChillActivityBundle/Tests/Entity/ActivityTest.php b/src/Bundle/ChillActivityBundle/Tests/Entity/ActivityTest.php index c67c99894..a14ac4a24 100644 --- a/src/Bundle/ChillActivityBundle/Tests/Entity/ActivityTest.php +++ b/src/Bundle/ChillActivityBundle/Tests/Entity/ActivityTest.php @@ -12,12 +12,17 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Tests\Entity; use Chill\ActivityBundle\Entity\Activity; +use Chill\PersonBundle\AccompanyingPeriod\SocialIssueConsistency\AccompanyingPeriodSocialIssueConsistencyEntityListener; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; +use Doctrine\ORM\Event\LifecycleEventArgs; use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; final class ActivityTest extends TestCase { + use ProphecyTrait; public function testHierarchySocialActions(): void { @@ -59,6 +64,9 @@ final class ActivityTest extends TestCase public function testHierarchySocialIssues(): void { + $listener = new AccompanyingPeriodSocialIssueConsistencyEntityListener(); + $event = $this->prophesize(LifecycleEventArgs::class)->reveal(); + $parent = new SocialIssue(); $child = new SocialIssue(); @@ -67,19 +75,23 @@ final class ActivityTest extends TestCase $child->addChild($grandChild); $activity = new Activity(); + $activity->setAccompanyingPeriod(new AccompanyingPeriod()); $activity->addSocialIssue($parent); + $listener->preUpdate($activity, $event); $this->assertCount(1, $activity->getSocialIssues()); $this->assertContains($parent, $activity->getSocialIssues()); $activity->addSocialIssue($grandChild); + $listener->preUpdate($activity, $event); $this->assertCount(1, $activity->getSocialIssues()); $this->assertContains($grandChild, $activity->getSocialIssues()); $this->assertNotContains($parent, $activity->getSocialIssues()); $activity->addSocialIssue($child); + $listener->preUpdate($activity, $event); $this->assertCount(1, $activity->getSocialIssues()); $this->assertContains($grandChild, $activity->getSocialIssues()); @@ -87,6 +99,7 @@ final class ActivityTest extends TestCase $this->assertNotContains($child, $activity->getSocialIssues()); $activity->addSocialIssue($another = new SocialIssue()); + $listener->preUpdate($activity, $event); $this->assertCount(2, $activity->getSocialIssues()); $this->assertContains($grandChild, $activity->getSocialIssues()); @@ -95,4 +108,4 @@ final class ActivityTest extends TestCase $this->assertNotContains($child, $activity->getSocialIssues()); } -} \ No newline at end of file +} diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php index 01f30f140..198950494 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/SocialAction.php @@ -307,4 +307,59 @@ class SocialAction return $this; } + + /** + * Recursive method which return true if the current $action + * is a descendant of the $action given in parameter. + */ + public function isDescendantOf(SocialAction $action): bool + { + if (!$this->hasParent()) { + return false; + } + + if ($this->getParent() === $action) { + return true; + } + + return $this->getParent()->isDescendantOf($action); + } + + /** + * In a SocialIssues's collection, find the elements which are an ancestor of + * other elements. + * + * The difference of the given list (thus, the elements which are **not** kept + * in the returned collection) are the most-grand-child elements of the list. + * + * 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|SocialAction[] $socialActions + * + * @return Collection|SocialAction[] a list with the elements of the given list which are parent of other elements in the given list + */ + public static function findAncestorSocialActions(Collection $socialActions): Collection + { + $ancestors = new ArrayCollection(); + + foreach ($socialActions as $candidateChild) { + if ($ancestors->contains($candidateChild)) { + continue; + } + + foreach ($socialActions as $candidateParent) { + if ($ancestors->contains($candidateParent)) { + continue; + } + + if ($candidateChild->isDescendantOf($candidateParent)) { + $ancestors->add($candidateParent); + } + } + } + + return $ancestors; + } }