vendor/knplabs/knp-menu/src/Knp/Menu/MenuItem.php line 594

Open in your IDE?
  1. <?php
  2. namespace Knp\Menu;
  3. /**
  4.  * Default implementation of the ItemInterface
  5.  */
  6. class MenuItem implements ItemInterface
  7. {
  8.     /**
  9.      * Name of this menu item (used for id by parent menu)
  10.      *
  11.      * @var string
  12.      */
  13.     protected $name;
  14.     /**
  15.      * Label to output, name is used by default
  16.      *
  17.      * @var string|null
  18.      */
  19.     protected $label;
  20.     /**
  21.      * Attributes for the item link
  22.      *
  23.      * @var array<string, string|bool|null>
  24.      */
  25.     protected $linkAttributes = [];
  26.     /**
  27.      * Attributes for the children list
  28.      *
  29.      * @var array<string, string|bool|null>
  30.      */
  31.     protected $childrenAttributes = [];
  32.     /**
  33.      * Attributes for the item text
  34.      *
  35.      * @var array<string, string|bool|null>
  36.      */
  37.     protected $labelAttributes = [];
  38.     /**
  39.      * Uri to use in the anchor tag
  40.      *
  41.      * @var string|null
  42.      */
  43.     protected $uri;
  44.     /**
  45.      * Attributes for the item
  46.      *
  47.      * @var array<string, string|bool|null>
  48.      */
  49.     protected $attributes = [];
  50.     /**
  51.      * Extra stuff associated to the item
  52.      *
  53.      * @var array<string, mixed>
  54.      */
  55.     protected $extras = [];
  56.     /**
  57.      * Whether the item is displayed
  58.      *
  59.      * @var bool
  60.      */
  61.     protected $display true;
  62.     /**
  63.      * Whether the children of the item are displayed
  64.      *
  65.      * @var bool
  66.      */
  67.     protected $displayChildren true;
  68.     /**
  69.      * Child items
  70.      *
  71.      * @var array<string, ItemInterface>
  72.      */
  73.     protected $children = [];
  74.     /**
  75.      * Parent item
  76.      *
  77.      * @var ItemInterface|null
  78.      */
  79.     protected $parent;
  80.     /**
  81.      * whether the item is current. null means unknown
  82.      *
  83.      * @var bool|null
  84.      */
  85.     protected $isCurrent;
  86.     /**
  87.      * @var FactoryInterface
  88.      */
  89.     protected $factory;
  90.     /**
  91.      * Class constructor
  92.      *
  93.      * @param string $name The name of this menu, which is how its parent will
  94.      *                     reference it. Also used as label if label not specified
  95.      */
  96.     public function __construct(string $nameFactoryInterface $factory)
  97.     {
  98.         $this->name $name;
  99.         $this->factory $factory;
  100.     }
  101.     public function setFactory(FactoryInterface $factory): ItemInterface
  102.     {
  103.         $this->factory $factory;
  104.         return $this;
  105.     }
  106.     public function getName(): string
  107.     {
  108.         return $this->name;
  109.     }
  110.     public function setName(string $name): ItemInterface
  111.     {
  112.         if ($this->name === $name) {
  113.             return $this;
  114.         }
  115.         $parent $this->getParent();
  116.         if (null !== $parent && isset($parent[$name])) {
  117.             throw new \InvalidArgumentException('Cannot rename item, name is already used by sibling.');
  118.         }
  119.         $oldName $this->name;
  120.         $this->name $name;
  121.         if (null !== $parent) {
  122.             $names \array_keys($parent->getChildren());
  123.             $items \array_values($parent->getChildren());
  124.             $offset \array_search($oldName$names);
  125.             $names[$offset] = $name;
  126.             if (false === $children \array_combine($names$items)) {
  127.                 throw new \InvalidArgumentException('Number of elements is not matching.');
  128.             }
  129.             $parent->setChildren($children);
  130.         }
  131.         return $this;
  132.     }
  133.     public function getUri(): ?string
  134.     {
  135.         return $this->uri;
  136.     }
  137.     public function setUri(?string $uri): ItemInterface
  138.     {
  139.         $this->uri $uri;
  140.         return $this;
  141.     }
  142.     public function getLabel(): string
  143.     {
  144.         return $this->label ?? $this->name;
  145.     }
  146.     public function setLabel(?string $label): ItemInterface
  147.     {
  148.         $this->label $label;
  149.         return $this;
  150.     }
  151.     public function getAttributes(): array
  152.     {
  153.         return $this->attributes;
  154.     }
  155.     public function setAttributes(array $attributes): ItemInterface
  156.     {
  157.         $this->attributes $attributes;
  158.         return $this;
  159.     }
  160.     public function getAttribute(string $name$default null)
  161.     {
  162.         return $this->attributes[$name] ?? $default;
  163.     }
  164.     public function setAttribute(string $name$value): ItemInterface
  165.     {
  166.         $this->attributes[$name] = $value;
  167.         return $this;
  168.     }
  169.     public function getLinkAttributes(): array
  170.     {
  171.         return $this->linkAttributes;
  172.     }
  173.     public function setLinkAttributes(array $linkAttributes): ItemInterface
  174.     {
  175.         $this->linkAttributes $linkAttributes;
  176.         return $this;
  177.     }
  178.     public function getLinkAttribute(string $name$default null)
  179.     {
  180.         return $this->linkAttributes[$name] ?? $default;
  181.     }
  182.     public function setLinkAttribute(string $name$value): ItemInterface
  183.     {
  184.         $this->linkAttributes[$name] = $value;
  185.         return $this;
  186.     }
  187.     public function getChildrenAttributes(): array
  188.     {
  189.         return $this->childrenAttributes;
  190.     }
  191.     public function setChildrenAttributes(array $childrenAttributes): ItemInterface
  192.     {
  193.         $this->childrenAttributes $childrenAttributes;
  194.         return $this;
  195.     }
  196.     public function getChildrenAttribute(string $name$default null)
  197.     {
  198.         return $this->childrenAttributes[$name] ?? $default;
  199.     }
  200.     public function setChildrenAttribute(string $name$value): ItemInterface
  201.     {
  202.         $this->childrenAttributes[$name] = $value;
  203.         return $this;
  204.     }
  205.     public function getLabelAttributes(): array
  206.     {
  207.         return $this->labelAttributes;
  208.     }
  209.     public function setLabelAttributes(array $labelAttributes): ItemInterface
  210.     {
  211.         $this->labelAttributes $labelAttributes;
  212.         return $this;
  213.     }
  214.     public function getLabelAttribute(string $name$default null)
  215.     {
  216.         return $this->labelAttributes[$name] ?? $default;
  217.     }
  218.     public function setLabelAttribute(string $name$value): ItemInterface
  219.     {
  220.         $this->labelAttributes[$name] = $value;
  221.         return $this;
  222.     }
  223.     public function getExtras(): array
  224.     {
  225.         return $this->extras;
  226.     }
  227.     public function setExtras(array $extras): ItemInterface
  228.     {
  229.         $this->extras $extras;
  230.         return $this;
  231.     }
  232.     public function getExtra(string $name$default null)
  233.     {
  234.         return $this->extras[$name] ?? $default;
  235.     }
  236.     public function setExtra(string $name$value): ItemInterface
  237.     {
  238.         $this->extras[$name] = $value;
  239.         return $this;
  240.     }
  241.     public function getDisplayChildren(): bool
  242.     {
  243.         return $this->displayChildren;
  244.     }
  245.     public function setDisplayChildren(bool $bool): ItemInterface
  246.     {
  247.         $this->displayChildren $bool;
  248.         return $this;
  249.     }
  250.     public function isDisplayed(): bool
  251.     {
  252.         return $this->display;
  253.     }
  254.     public function setDisplay(bool $bool): ItemInterface
  255.     {
  256.         $this->display $bool;
  257.         return $this;
  258.     }
  259.     public function addChild($child, array $options = []): ItemInterface
  260.     {
  261.         if (!$child instanceof ItemInterface) {
  262.             $child $this->factory->createItem($child$options);
  263.         } elseif (null !== $child->getParent()) {
  264.             throw new \InvalidArgumentException('Cannot add menu item as child, it already belongs to another menu (e.g. has a parent).');
  265.         }
  266.         $child->setParent($this);
  267.         $this->children[$child->getName()] = $child;
  268.         return $child;
  269.     }
  270.     public function getChild(string $name): ?ItemInterface
  271.     {
  272.         return $this->children[$name] ?? null;
  273.     }
  274.     public function reorderChildren(array $order): ItemInterface
  275.     {
  276.         if (\count($order) !== $this->count()) {
  277.             throw new \InvalidArgumentException('Cannot reorder children, order does not contain all children.');
  278.         }
  279.         $newChildren = [];
  280.         foreach ($order as $name) {
  281.             if (!isset($this->children[$name])) {
  282.                 throw new \InvalidArgumentException('Cannot find children named '.$name);
  283.             }
  284.             $child $this->children[$name];
  285.             $newChildren[$name] = $child;
  286.         }
  287.         $this->setChildren($newChildren);
  288.         return $this;
  289.     }
  290.     public function copy(): ItemInterface
  291.     {
  292.         $newMenu = clone $this;
  293.         $newMenu->setChildren([]);
  294.         $newMenu->setParent();
  295.         foreach ($this->getChildren() as $child) {
  296.             $newMenu->addChild($child->copy());
  297.         }
  298.         return $newMenu;
  299.     }
  300.     public function getLevel(): int
  301.     {
  302.         return $this->parent $this->parent->getLevel() + 0;
  303.     }
  304.     public function getRoot(): ItemInterface
  305.     {
  306.         $obj $this;
  307.         do {
  308.             $found $obj;
  309.         } while ($obj $obj->getParent());
  310.         return $found;
  311.     }
  312.     public function isRoot(): bool
  313.     {
  314.         return null === $this->parent;
  315.     }
  316.     public function getParent(): ?ItemInterface
  317.     {
  318.         return $this->parent;
  319.     }
  320.     public function setParent(?ItemInterface $parent null): ItemInterface
  321.     {
  322.         if ($parent === $this) {
  323.             throw new \InvalidArgumentException('Item cannot be a child of itself');
  324.         }
  325.         $this->parent $parent;
  326.         return $this;
  327.     }
  328.     public function getChildren(): array
  329.     {
  330.         return $this->children;
  331.     }
  332.     public function setChildren(array $children): ItemInterface
  333.     {
  334.         $this->children $children;
  335.         return $this;
  336.     }
  337.     public function removeChild($name): ItemInterface
  338.     {
  339.         $name $name instanceof ItemInterface $name->getName() : $name;
  340.         if (isset($this->children[$name])) {
  341.             // unset the child and reset it so it looks independent
  342.             $this->children[$name]->setParent(null);
  343.             unset($this->children[$name]);
  344.         }
  345.         return $this;
  346.     }
  347.     public function getFirstChild(): ItemInterface
  348.     {
  349.         if (empty($this->children)) {
  350.             throw new \LogicException('Cannot get first child: there are no children.');
  351.         }
  352.         return \reset($this->children);
  353.     }
  354.     public function getLastChild(): ItemInterface
  355.     {
  356.         if (empty($this->children)) {
  357.             throw new \LogicException('Cannot get last child: there are no children.');
  358.         }
  359.         return \end($this->children);
  360.     }
  361.     public function hasChildren(): bool
  362.     {
  363.         foreach ($this->children as $child) {
  364.             if ($child->isDisplayed()) {
  365.                 return true;
  366.             }
  367.         }
  368.         return false;
  369.     }
  370.     public function setCurrent(?bool $bool): ItemInterface
  371.     {
  372.         $this->isCurrent $bool;
  373.         return $this;
  374.     }
  375.     public function isCurrent(): ?bool
  376.     {
  377.         return $this->isCurrent;
  378.     }
  379.     public function isLast(): bool
  380.     {
  381.         // if this is root, then return false
  382.         if (null === $this->parent) {
  383.             return false;
  384.         }
  385.         return $this->parent->getLastChild() === $this;
  386.     }
  387.     public function isFirst(): bool
  388.     {
  389.         // if this is root, then return false
  390.         if (null === $this->parent) {
  391.             return false;
  392.         }
  393.         return $this->parent->getFirstChild() === $this;
  394.     }
  395.     public function actsLikeFirst(): bool
  396.     {
  397.         // root items are never "marked" as first
  398.         if (null === $this->parent) {
  399.             return false;
  400.         }
  401.         // A menu acts like first only if it is displayed
  402.         if (!$this->isDisplayed()) {
  403.             return false;
  404.         }
  405.         // if we're first and visible, we're first, period.
  406.         if ($this->isFirst()) {
  407.             return true;
  408.         }
  409.         $children $this->parent->getChildren();
  410.         foreach ($children as $child) {
  411.             // loop until we find a visible menu. If its this menu, we're first
  412.             if ($child->isDisplayed()) {
  413.                 return $child->getName() === $this->getName();
  414.             }
  415.         }
  416.         return false;
  417.     }
  418.     public function actsLikeLast(): bool
  419.     {
  420.         // root items are never "marked" as last
  421.         if (null === $this->parent) {
  422.             return false;
  423.         }
  424.         // A menu acts like last only if it is displayed
  425.         if (!$this->isDisplayed()) {
  426.             return false;
  427.         }
  428.         // if we're last and visible, we're last, period.
  429.         if ($this->isLast()) {
  430.             return true;
  431.         }
  432.         $children \array_reverse($this->parent->getChildren());
  433.         foreach ($children as $child) {
  434.             // loop until we find a visible menu. If its this menu, we're first
  435.             if ($child->isDisplayed()) {
  436.                 return $child->getName() === $this->getName();
  437.             }
  438.         }
  439.         return false;
  440.     }
  441.     /**
  442.      * Implements Countable
  443.      */
  444.     public function count(): int
  445.     {
  446.         return \count($this->children);
  447.     }
  448.     /**
  449.      * Implements IteratorAggregate
  450.      */
  451.     public function getIterator(): \Traversable
  452.     {
  453.         return new \ArrayIterator($this->children);
  454.     }
  455.     /**
  456.      * Implements ArrayAccess
  457.      *
  458.      * @param string $offset
  459.      */
  460.     public function offsetExists($offset): bool
  461.     {
  462.         return isset($this->children[$offset]);
  463.     }
  464.     /**
  465.      * Implements ArrayAccess
  466.      *
  467.      * @param string $offset
  468.      *
  469.      * @return ItemInterface|null
  470.      */
  471.     public function offsetGet($offset)
  472.     {
  473.         return $this->getChild($offset);
  474.     }
  475.     /**
  476.      * Implements ArrayAccess
  477.      *
  478.      * @param string      $offset
  479.      * @param string|null $value
  480.      */
  481.     public function offsetSet($offset$value): void
  482.     {
  483.         $this->addChild($offset)->setLabel($value);
  484.     }
  485.     /**
  486.      * Implements ArrayAccess
  487.      *
  488.      * @param string $offset
  489.      */
  490.     public function offsetUnset($offset): void
  491.     {
  492.         $this->removeChild($offset);
  493.     }
  494. }