Menu.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Backend\Model;
  7. use Magento\Backend\Model\Menu\Item;
  8. use Magento\Backend\Model\Menu\Item\Factory;
  9. use Magento\Framework\App\ObjectManager;
  10. use Magento\Framework\Serialize\SerializerInterface;
  11. use Psr\Log\LoggerInterface;
  12. /**
  13. * Backend menu model
  14. *
  15. * @api
  16. * @since 100.0.2
  17. */
  18. class Menu extends \ArrayObject
  19. {
  20. /**
  21. * Path in tree structure
  22. *
  23. * @var string
  24. */
  25. protected $_path = '';
  26. /**
  27. * @var LoggerInterface
  28. */
  29. protected $_logger;
  30. /**
  31. * @var Factory
  32. */
  33. private $menuItemFactory;
  34. /**
  35. * @var SerializerInterface
  36. */
  37. private $serializer;
  38. /**
  39. * Menu constructor
  40. *
  41. * @param LoggerInterface $logger
  42. * @param string $pathInMenuStructure
  43. * @param Factory|null $menuItemFactory
  44. * @param SerializerInterface|null $serializer
  45. */
  46. public function __construct(
  47. LoggerInterface $logger,
  48. $pathInMenuStructure = '',
  49. Factory $menuItemFactory = null,
  50. SerializerInterface $serializer = null
  51. ) {
  52. if ($pathInMenuStructure) {
  53. $this->_path = $pathInMenuStructure . '/';
  54. }
  55. $this->_logger = $logger;
  56. $this->setIteratorClass(\Magento\Backend\Model\Menu\Iterator::class);
  57. $this->menuItemFactory = $menuItemFactory ?: ObjectManager::getInstance()
  58. ->create(Factory::class);
  59. $this->serializer = $serializer ?: ObjectManager::getInstance()->create(SerializerInterface::class);
  60. }
  61. /**
  62. * Add child to menu item
  63. *
  64. * @param Item $item
  65. * @param string $parentId
  66. * @param int $index
  67. * @return void
  68. * @throws \InvalidArgumentException
  69. */
  70. public function add(Item $item, $parentId = null, $index = null)
  71. {
  72. if ($parentId !== null) {
  73. $parentItem = $this->get($parentId);
  74. if ($parentItem === null) {
  75. throw new \InvalidArgumentException("Item with identifier {$parentId} does not exist");
  76. }
  77. $parentItem->getChildren()->add($item, null, $index);
  78. } else {
  79. $index = (int) $index;
  80. if (!isset($this[$index])) {
  81. $this->offsetSet($index, $item);
  82. $this->_logger->info(
  83. sprintf('Add of item with id %s was processed', $item->getId())
  84. );
  85. } else {
  86. $this->add($item, $parentId, $index + 1);
  87. }
  88. }
  89. }
  90. /**
  91. * Retrieve menu item by id
  92. *
  93. * @param string $itemId
  94. * @return Item|null
  95. */
  96. public function get($itemId)
  97. {
  98. $result = null;
  99. /** @var Item $item */
  100. foreach ($this as $item) {
  101. if ($item->getId() == $itemId) {
  102. $result = $item;
  103. break;
  104. }
  105. if ($item->hasChildren() && ($result = $item->getChildren()->get($itemId))) {
  106. break;
  107. }
  108. }
  109. return $result;
  110. }
  111. /**
  112. * Move menu item
  113. *
  114. * @param string $itemId
  115. * @param string $toItemId
  116. * @param int $sortIndex
  117. * @return void
  118. * @throws \InvalidArgumentException
  119. */
  120. public function move($itemId, $toItemId, $sortIndex = null)
  121. {
  122. $item = $this->get($itemId);
  123. if ($item === null) {
  124. throw new \InvalidArgumentException("Item with identifier {$itemId} does not exist");
  125. }
  126. $this->remove($itemId);
  127. $this->add($item, $toItemId, $sortIndex);
  128. }
  129. /**
  130. * Remove menu item by id
  131. *
  132. * @param string $itemId
  133. * @return bool
  134. */
  135. public function remove($itemId)
  136. {
  137. $result = false;
  138. /** @var Item $item */
  139. foreach ($this as $key => $item) {
  140. if ($item->getId() == $itemId) {
  141. unset($this[$key]);
  142. $result = true;
  143. $this->_logger->info(
  144. sprintf('Remove on item with id %s was processed', $item->getId())
  145. );
  146. break;
  147. }
  148. if ($item->hasChildren() && ($result = $item->getChildren()->remove($itemId))) {
  149. break;
  150. }
  151. }
  152. return $result;
  153. }
  154. /**
  155. * Change order of an item in its parent menu
  156. *
  157. * @param string $itemId
  158. * @param int $position
  159. * @return bool
  160. */
  161. public function reorder($itemId, $position)
  162. {
  163. $result = false;
  164. /** @var Item $item */
  165. foreach ($this as $key => $item) {
  166. if ($item->getId() == $itemId) {
  167. unset($this[$key]);
  168. $this->add($item, null, $position);
  169. $result = true;
  170. break;
  171. } elseif ($item->hasChildren() && $result = $item->getChildren()->reorder($itemId, $position)) {
  172. break;
  173. }
  174. }
  175. return $result;
  176. }
  177. /**
  178. * Check whether provided item is last in list
  179. *
  180. * @param Item $item
  181. * @return bool
  182. */
  183. public function isLast(Item $item)
  184. {
  185. return $this->offsetGet(max(array_keys($this->getArrayCopy())))->getId() == $item->getId();
  186. }
  187. /**
  188. * Find first menu item that user is able to access
  189. *
  190. * @return Item|null
  191. */
  192. public function getFirstAvailable()
  193. {
  194. $result = null;
  195. /** @var Item $item */
  196. foreach ($this as $item) {
  197. if ($item->isAllowed() && !$item->isDisabled()) {
  198. if ($item->hasChildren()) {
  199. $result = $item->getChildren()->getFirstAvailable();
  200. if (false == ($result === null)) {
  201. break;
  202. }
  203. } else {
  204. $result = $item;
  205. break;
  206. }
  207. }
  208. }
  209. return $result;
  210. }
  211. /**
  212. * Get parent items by item id
  213. *
  214. * @param string $itemId
  215. * @return Item[]
  216. */
  217. public function getParentItems($itemId)
  218. {
  219. $parents = [];
  220. $this->_findParentItems($this, $itemId, $parents);
  221. return array_reverse($parents);
  222. }
  223. /**
  224. * Find parent items
  225. *
  226. * @param \Magento\Backend\Model\Menu $menu
  227. * @param string $itemId
  228. * @param array &$parents
  229. * @return bool
  230. */
  231. protected function _findParentItems($menu, $itemId, &$parents)
  232. {
  233. /** @var Item $item */
  234. foreach ($menu as $item) {
  235. if ($item->getId() == $itemId) {
  236. return true;
  237. }
  238. if ($item->hasChildren()) {
  239. if ($this->_findParentItems($item->getChildren(), $itemId, $parents)) {
  240. $parents[] = $item;
  241. return true;
  242. }
  243. }
  244. }
  245. return false;
  246. }
  247. /**
  248. * Serialize menu
  249. *
  250. * @return string
  251. */
  252. public function serialize()
  253. {
  254. return $this->serializer->serialize($this->toArray());
  255. }
  256. /**
  257. * Get menu data represented as an array
  258. *
  259. * @return array
  260. * @since 100.2.0
  261. */
  262. public function toArray()
  263. {
  264. $data = [];
  265. foreach ($this as $item) {
  266. $data[] = $item->toArray();
  267. }
  268. return $data;
  269. }
  270. /**
  271. * Unserialize menu
  272. *
  273. * @param string $serialized
  274. * @return void
  275. * @since 100.2.0
  276. */
  277. public function unserialize($serialized)
  278. {
  279. $data = $this->serializer->unserialize($serialized);
  280. $this->populateFromArray($data);
  281. }
  282. /**
  283. * Populate the menu with data from array
  284. *
  285. * @param array $data
  286. * @return void
  287. * @since 100.2.0
  288. */
  289. public function populateFromArray(array $data)
  290. {
  291. $items = [];
  292. foreach ($data as $itemData) {
  293. $item = $this->menuItemFactory->create($itemData);
  294. $items[] = $item;
  295. }
  296. $this->exchangeArray($items);
  297. }
  298. }