. */ namespace App\Repository; use App\Entity\Base\AbstractPartsContainingDBElement; use App\Entity\Base\PartsContainingRepositoryInterface; use App\Entity\Parts\Part; use InvalidArgumentException; /** * @template TEntityClass of AbstractPartsContainingDBElement * @extends StructuralDBElementRepository */ abstract class AbstractPartsContainingRepository extends StructuralDBElementRepository implements PartsContainingRepositoryInterface { /** @var int The maximum number of levels for which we can recurse before throwing an error */ private const RECURSION_LIMIT = 50; /** * Returns all parts associated with this element. * * @param object $element the element for which the parts should be determined * @param array $order_by The order of the parts. Format ['name' => 'ASC'] * * @return Part[] */ abstract public function getParts(object $element, array $order_by = ['name' => 'ASC']): array; /** * Gets the count of the parts associated with this element. * * @param object $element the element for which the parts should be determined * @return int */ abstract public function getPartsCount(object $element): int; /** * Returns the count of the parts associated with this element and all its children. * Please be aware that this function is pretty slow on large trees! * @return int */ public function getPartsCountRecursive(AbstractPartsContainingDBElement $element): int { return $this->getPartsCountRecursiveWithDepthN($element, self::RECURSION_LIMIT); } /** * The implementation of the recursive function to get the parts count. * This function is used to limit the recursion depth (remaining_depth is decreased on each call). * If the recursion limit is reached (remaining_depth <= 0), a RuntimeException is thrown. * @internal This function is not intended to be called directly, use getPartsCountRecursive() instead. * @return int */ protected function getPartsCountRecursiveWithDepthN(AbstractPartsContainingDBElement $element, int $remaining_depth): int { if ($remaining_depth <= 0) { throw new \RuntimeException('Recursion limit reached!'); } $count = $this->getPartsCount($element); //If the element is its own parent, we have a loop in the tree, so we stop here. if ($element->getParent() === $element) { return 0; } foreach ($element->getChildren() as $child) { $count += $this->getPartsCountRecursiveWithDepthN($child, $remaining_depth - 1); } return $count; } protected function getPartsByField(object $element, array $order_by, string $field_name): array { if (!$element instanceof AbstractPartsContainingDBElement) { throw new InvalidArgumentException('$element must be an instance of AbstractPartContainingDBElement!'); } $repo = $this->getEntityManager()->getRepository(Part::class); return $repo->findBy([$field_name => $element], $order_by); } protected function getPartsCountByField(object $element, string $field_name): int { if (!$element instanceof AbstractPartsContainingDBElement) { throw new InvalidArgumentException('$element must be an instance of AbstractPartContainingDBElement!'); } $repo = $this->getEntityManager()->getRepository(Part::class); return $repo->count([$field_name => $element]); } }