children = new ArrayCollection(); } /****************************************************************************** * StructuralDBElement constructor. *****************************************************************************/ /** * Check if this element is a child of another element (recursive). * * @param StructuralDBElement $another_element the object to compare * IMPORTANT: both objects to compare must be from the same class (for example two "Device" objects)! * * @return bool True, if this element is child of $another_element. * * @throws \InvalidArgumentException if there was an error */ public function isChildOf(self $another_element): bool { $class_name = \get_class($this); //Check if both elements compared, are from the same type // (we have to check inheritance, or we get exceptions when using doctrine entities (they have a proxy type): if (!is_a($another_element, $class_name) && !is_a($this, \get_class($another_element))) { throw new \InvalidArgumentException('isChildOf() only works for objects of the same type!'); } if (null === $this->getParent()) { // this is the root node return false; } //If this' parents element, is $another_element, then we are finished return ($this->parent->getID() === $another_element->getID()) || $this->parent->isChildOf($another_element); //Otherwise, check recursivley } /** * Checks if this element is an root element (has no parent). * * @return bool True if the this element is an root element. */ public function isRoot(): bool { return null === $this->parent; } /****************************************************************************** * * Getters * ******************************************************************************/ /** * Get the parent of this element. * * @return StructuralDBElement|null The parent element. Null if this element, does not have a parent. */ public function getParent(): ?self { return $this->parent; } /** * Get the comment of the element. * * @return string the comment */ public function getComment(): ?string { return $this->comment; } /** * Get the level. * * The level of the root node is -1. * * @return int the level of this element (zero means a most top element * [a subelement of the root node]) */ public function getLevel(): int { /* * Only check for nodes that have a parent. In the other cases zero is correct. */ if (0 === $this->level && null !== $this->parent) { $element = $this->parent; while (null !== $element) { /** @var StructuralDBElement $element */ $element = $element->parent; ++$this->level; } } return $this->level; } /** * Get the full path. * * @param string $delimeter the delimeter of the returned string * * @return string the full path (incl. the name of this element), delimeted by $delimeter */ public function getFullPath(string $delimeter = self::PATH_DELIMITER_ARROW): string { if (!\is_array($this->full_path_strings)) { $this->full_path_strings = []; $this->full_path_strings[] = $this->getName(); $element = $this; $overflow = 20; //We only allow 20 levels depth while (null !== $element->parent && $overflow >= 0) { $element = $element->parent; $this->full_path_strings[] = $element->getName(); //Decrement to prevent mem overflow. --$overflow; } $this->full_path_strings = array_reverse($this->full_path_strings); } return implode($delimeter, $this->full_path_strings); } /** * Gets the path to this element (including the element itself). * * @return self[] An array with all (recursivily) parent elements (including this one), * ordered from the lowest levels (root node) first to the highest level (the element itself) */ public function getPathArray(): array { $tmp = []; $tmp[] = $this; //We only allow 20 levels depth while (!end($tmp)->isRoot() && \count($tmp) < 20) { $tmp[] = end($tmp)->parent; } return array_reverse($tmp); } /** * Get all subelements of this element. * * @param bool $recursive if true, the search is recursive * * @return static[] all subelements as an array of objects (sorted by their full path) */ public function getSubelements(): iterable { return $this->children; } public function getChildren(): iterable { return $this->children; } /** * @return bool */ public function isNotSelectable(): bool { return $this->not_selectable; } /****************************************************************************** * * Setters * ******************************************************************************/ /** * Sets the new parent object. * * @param self $new_parent The new parent object * * @return StructuralDBElement */ public function setParent(?self $new_parent): self { /* if ($new_parent->isChildOf($this)) { throw new \InvalidArgumentException('You can not use one of the element childs as parent!'); } */ $this->parent = $new_parent; return $this; } /** * Set the comment. * * @param string $new_comment the new comment * * @return StructuralDBElement */ public function setComment(?string $new_comment): self { $this->comment = $new_comment; return $this; } public function setChildren(array $element): self { $this->children = $element; return $this; } /** * @return StructuralDBElement */ public function setNotSelectable(bool $not_selectable): self { $this->not_selectable = $not_selectable; return $this; } public function clearChildren(): self { $this->children = new ArrayCollection(); return $this; } }