mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-30 02:25:00 +00:00
Add support for motive descendants in filters and improve Motive entity
- Added `getDescendants` method to `Motive` entity to retrieve all descendants recursively. - Updated `TicketACLAwareRepository` to include descendants when filtering by motives. - Updated API specification to clarify that motive descendants are included in query filters. - Added unit tests for `Motive` entity to validate descendant logic.
This commit is contained in:
@@ -112,7 +112,7 @@ paths:
|
|||||||
- no
|
- no
|
||||||
- name: byMotives
|
- name: byMotives
|
||||||
in: query
|
in: query
|
||||||
description: the motives of the ticket
|
description: the motives of the ticket. All the descendants of the motive are taken into account.
|
||||||
required: false
|
required: false
|
||||||
style: form
|
style: form
|
||||||
explode: false
|
explode: false
|
||||||
|
@@ -195,4 +195,25 @@ class Motive
|
|||||||
{
|
{
|
||||||
return $this->parent;
|
return $this->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the descendants of the current entity.
|
||||||
|
*
|
||||||
|
* This method collects all descendant entities recursively, starting from the current entity
|
||||||
|
* and including all of its children and their descendants.
|
||||||
|
*
|
||||||
|
* @return ReadableCollection&Selectable A collection containing the current entity and all its descendants
|
||||||
|
*/
|
||||||
|
public function getDescendants(): ReadableCollection&Selectable
|
||||||
|
{
|
||||||
|
$collection = new ArrayCollection([$this]);
|
||||||
|
|
||||||
|
foreach ($this->getChildren() as $child) {
|
||||||
|
foreach ($child->getDescendants() as $descendant) {
|
||||||
|
$collection->add($descendant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $collection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -113,10 +113,11 @@ final readonly class TicketACLAwareRepository implements TicketACLAwareRepositor
|
|||||||
if (array_key_exists('byMotives', $params)) {
|
if (array_key_exists('byMotives', $params)) {
|
||||||
$byMotives = $qb->expr()->orX();
|
$byMotives = $qb->expr()->orX();
|
||||||
foreach ($params['byMotives'] as $motive) {
|
foreach ($params['byMotives'] as $motive) {
|
||||||
|
$motivesWithDescendants = $motive->getDescendants()->toArray();
|
||||||
$byMotives->add(
|
$byMotives->add(
|
||||||
$qb->expr()->exists(sprintf(
|
$qb->expr()->exists(sprintf(
|
||||||
'SELECT 1 FROM %s tp_motive_%d WHERE tp_motive_%d.ticket = t
|
'SELECT 1 FROM %s tp_motive_%d WHERE tp_motive_%d.ticket = t
|
||||||
AND tp_motive_%d.motive = :motive_%d AND tp_motive_%d.endDate IS NULL
|
AND tp_motive_%d.motive IN (:motives_%d) AND tp_motive_%d.endDate IS NULL
|
||||||
',
|
',
|
||||||
MotiveHistory::class,
|
MotiveHistory::class,
|
||||||
++$i,
|
++$i,
|
||||||
@@ -126,7 +127,7 @@ final readonly class TicketACLAwareRepository implements TicketACLAwareRepositor
|
|||||||
$i,
|
$i,
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
$qb->setParameter(sprintf('motive_%d', $i), $motive);
|
$qb->setParameter(sprintf('motives_%d', $i), $motivesWithDescendants);
|
||||||
}
|
}
|
||||||
$qb->andWhere($byMotives);
|
$qb->andWhere($byMotives);
|
||||||
}
|
}
|
||||||
|
63
src/Bundle/ChillTicketBundle/tests/Entity/MotiveTest.php
Normal file
63
src/Bundle/ChillTicketBundle/tests/Entity/MotiveTest.php
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<?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\TicketBundle\Tests\Entity;
|
||||||
|
|
||||||
|
use Chill\TicketBundle\Entity\Motive;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @covers \Chill\TicketBundle\Entity\Motive::getWithDescendants
|
||||||
|
*/
|
||||||
|
final class MotiveTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGetDescendantsOnLeafReturnsSelfOnly(): void
|
||||||
|
{
|
||||||
|
$leaf = new Motive();
|
||||||
|
$leaf->setLabel(['fr' => 'Feuille']);
|
||||||
|
|
||||||
|
$collection = $leaf->getDescendants();
|
||||||
|
|
||||||
|
self::assertCount(1, $collection);
|
||||||
|
self::assertSame($leaf, $collection->first());
|
||||||
|
self::assertContains($leaf, $collection->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetWithDescendantsReturnsSelfAndAllDescendants(): void
|
||||||
|
{
|
||||||
|
$parent = new Motive();
|
||||||
|
$parent->setLabel(['fr' => 'Parent']);
|
||||||
|
|
||||||
|
$childA = new Motive();
|
||||||
|
$childA->setLabel(['fr' => 'Enfant A']);
|
||||||
|
$childA->setParent($parent);
|
||||||
|
|
||||||
|
$childB = new Motive();
|
||||||
|
$childB->setLabel(['fr' => 'Enfant B']);
|
||||||
|
$childB->setParent($parent);
|
||||||
|
|
||||||
|
$grandChildA1 = new Motive();
|
||||||
|
$grandChildA1->setLabel(['fr' => 'Petit-enfant A1']);
|
||||||
|
$grandChildA1->setParent($childA);
|
||||||
|
|
||||||
|
$descendants = $parent->getDescendants();
|
||||||
|
$asArray = $descendants->toArray();
|
||||||
|
|
||||||
|
// It should contain the parent itself, both children and the grand child
|
||||||
|
self::assertCount(4, $descendants, 'Expected parent + 2 children + 1 grandchild');
|
||||||
|
self::assertContains($parent, $asArray);
|
||||||
|
self::assertContains($childA, $asArray);
|
||||||
|
self::assertContains($childB, $asArray);
|
||||||
|
self::assertContains($grandChildA1, $asArray);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user