1: <?php
2: namespace Menu\Items;
3:
4: use Exception;
5:
6: use HtmlObject\Element;
7:
8: use Menu\Menu;
9: use Menu\MenuHandler;
10: use Menu\Items\Contents\Link;
11: use Menu\Items\Contents\Raw;
12: use Menu\Traits\MenuObject;
13:
14: use Illuminate\Support\Collection;
15: use Illuminate\Support\Str;
16:
17: use Underscore\Methods\ArraysMethods;
18:
19: 20: 21:
22: class ItemList extends MenuObject
23: {
24: 25: 26: 27: 28:
29: public $name;
30:
31: 32: 33: 34: 35: 36: 37: 38: 39:
40: public function __construct($items = array(), $name = null, $attributes = array(), $element = null)
41: {
42: $this->children = $items;
43: $this->name = $name;
44: $this->attributes = $attributes;
45: $this->element = $element;
46: }
47:
48: 49: 50: 51: 52:
53: public function onItem()
54: {
55: return $this->children[sizeof($this->children) - 1];
56: }
57:
58:
59:
60:
61:
62: 63: 64: 65: 66: 67: 68: 69:
70: public function setOption($option, $value)
71: {
72:
73: if(Str::startsWith($option, 'item.')) {
74: foreach($this->children as $child) {
75: $child->setOption($option, $value);
76: }
77: }
78: elseif(Str::startsWith($option, 'item_list.')) {
79: $this->options = ArraysMethods::set($this->options, $option, $value);
80: }
81: else
82: {
83: Menu::setOption($option, $value);
84: }
85:
86: return $this;
87: }
88:
89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113:
114: public function add($url, $value, $children = null, $linkAttributes = array(), $itemAttributes = array(), $itemElement = null, $beforeContent = null, $afterContent = null)
115: {
116: $content = new Link($url, $value, $linkAttributes);
117: $item = $this->addContent($content, $children, $itemAttributes, $itemElement, $beforeContent, $afterContent);
118:
119: return $this;
120: }
121:
122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138:
139: public function raw($raw, $children = null, $itemAttributes = array(), $itemElement = null, $beforeContent = null, $afterContent = null)
140: {
141: $content = new Raw($raw);
142: $item = $this->addContent($content, $children, $itemAttributes, $itemElement, $beforeContent, $afterContent);
143:
144: return $this;
145: }
146:
147: 148: 149: 150: 151: 152: 153: 154: 155: 156:
157: public function addContent($content, $children = null, $itemAttributes = array(), $itemElement = null, $beforeContent = null, $afterContent = null)
158: {
159: $item = new Item($this, $content, $children, $itemElement, $beforeContent, $afterContent);
160: $item->setAttributes($itemAttributes);
161:
162:
163: if (!is_null($children)) {
164: $children->setParent($item);
165: }
166:
167: $this->setChild($item);
168:
169: return $item;
170: }
171:
172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183:
184: public function activePattern($pattern)
185: {
186: $pattern = (array) $pattern;
187: $item = end($this->children);
188: $item->setActivePatterns($pattern);
189:
190: return $this;
191: }
192:
193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204:
205: public function attach(ItemList $itemList)
206: {
207: $this->nestChildren($itemList->getChildren());
208:
209: return $this;
210: }
211:
212: 213: 214: 215: 216: 217: 218:
219: public function name($name)
220: {
221: $this->name = $name;
222:
223: return $this;
224: }
225:
226: 227: 228: 229: 230:
231: public function getName()
232: {
233: return $this->name;
234: }
235:
236:
237:
238:
239:
240: 241: 242: 243: 244: 245: 246:
247: public function prefix($prefix)
248: {
249: $this->setOption('item_list.prefix', $prefix);
250:
251: return $this;
252: }
253:
254: 255: 256: 257: 258: 259: 260:
261: public function prefixParents($prefixParents = true)
262: {
263: $this->setOption('item_list.prefix_parents', $prefixParents);
264:
265: return $this;
266: }
267:
268: 269: 270: 271: 272: 273: 274:
275: public function prefixMenuHandler($prefixMenuHandler = true)
276: {
277: $this->setOption('item_list.prefix_handler', $prefixMenuHandler);
278:
279: return $this;
280: }
281:
282: 283: 284: 285: 286:
287: public function setElement($element = null)
288: {
289: $this->element = $element;
290: return $this;
291: }
292:
293: 294: 295: 296: 297:
298: public function getElement()
299: {
300: if (is_null($this->element)) {
301: return $this->getOption('item_list.element');
302: }
303:
304: return $this->element;
305: }
306:
307: 308: 309: 310: 311:
312: public function getItemsWithDepth()
313: {
314: return $this->getItemsRecursivelyWithDepth($this->getChildren());
315: }
316:
317: 318: 319: 320: 321:
322: protected function getItemsRecursivelyWithDepth($items, $depth = 0)
323: {
324: $results = array();
325: foreach($items as $item)
326: {
327: $results[$depth][] = $item;
328:
329: $subItems = $item->getChildren()
330: ->getChildren();
331: foreach($this->getItemsRecursivelyWithDepth($subItems, $depth + 1) as $childrenDepth => $children)
332: {
333: foreach($children as $child)
334: {
335: $results[$childrenDepth][] = $child;
336: }
337: }
338: }
339:
340: return $results;
341: }
342:
343: 344: 345: 346: 347:
348: public function getItemListsWithDepth()
349: {
350: return $this->getItemListsRecursivelyWithDepth($this);
351: }
352:
353: 354: 355: 356: 357:
358: protected function getItemListsRecursivelyWithDepth($itemList, $depth = 0)
359: {
360: $results = array();
361:
362: $results[$depth][] = $itemList;
363:
364: $items = $itemList->getChildren();
365: foreach($items as $item)
366: {
367: foreach($this->getItemListsRecursivelyWithDepth($item->getChildren(), $depth + 1) as $childrenDepth => $children)
368: {
369: foreach($children as $child)
370: {
371: $results[$childrenDepth][] = $child;
372: }
373: }
374: }
375:
376: return $results;
377: }
378:
379: 380: 381: 382: 383:
384: public function getAllItems()
385: {
386: $results = array();
387:
388: foreach($this->getItemsWithDepth() as $depth => $items)
389: {
390: foreach($items as $item)
391: {
392: $results[] = $item;
393: }
394: }
395:
396: return new MenuHandler($results);
397: }
398:
399: 400: 401: 402: 403: 404: 405:
406: public function getItemsByContentType($contentType)
407: {
408: $results = array();
409:
410: $itemList = $this->getAllItems();
411: foreach($itemList->getMenuObjects() as $item)
412: {
413: $content = $item->getContent();
414: if(get_class($content) == $contentType)
415: {
416: $results[] = $item;
417: }
418: }
419:
420: return new MenuHandler($results);
421: }
422:
423: 424: 425: 426: 427:
428: public function getAllItemLists()
429: {
430: $results = array();
431:
432: foreach($this->getItemListsWithDepth() as $depth => $items)
433: {
434: foreach($items as $item)
435: {
436: $results[] = $item;
437: }
438: }
439:
440: return new MenuHandler($results);
441: }
442:
443: 444: 445: 446: 447:
448: public function getAllItemListsIncludingThisOne()
449: {
450: return $this->getAllItemLists()
451: ->addMenuObject($this);
452: }
453:
454: 455: 456: 457: 458:
459: public function getItemListsAtDepth($depth)
460: {
461: $itemListsWithDepth = $this->getItemListsWithDepth();
462:
463: return new MenuHandler($itemListsWithDepth[$depth]);
464: }
465:
466: 467: 468: 469: 470:
471: public function getItemListsAtDepthRange($from, $to)
472: {
473: $itemListsWithDepth = $this->getItemListsWithDepth();
474:
475: $results = array();
476: foreach($itemListsWithDepth as $depth => $itemLists)
477: {
478: if($depth >= $from && $depth <= $to)
479: {
480: foreach($itemLists as $itemList)
481: {
482: $results[] = $itemList;
483: }
484: }
485: }
486:
487: return new MenuHandler($results);
488: }
489:
490: 491: 492: 493: 494:
495: public function getItemsAtDepth($depth)
496: {
497: $itemsWithDepth = $this->getItemsWithDepth();
498:
499: return new MenuHandler($itemsWithDepth[$depth]);
500: }
501:
502: 503: 504: 505: 506:
507: public function getItemsAtDepthRange($from, $to)
508: {
509: $itemsWithDepth = $this->getItemsWithDepth();
510:
511: $results = array();
512: foreach($itemsWithDepth as $depth => $items)
513: {
514: if($depth >= $from && $depth <= $to)
515: {
516: foreach($items as $item)
517: {
518: $results[] = $item;
519: }
520: }
521: }
522:
523: return new MenuHandler($results);
524: }
525:
526: public function reverse()
527: {
528: $this->children = array_reverse($this->children);
529:
530: return $this;
531: }
532:
533: public function findActiveItem()
534: {
535: $items = $this->getAllItems()
536: ->getMenuObjects();
537:
538:
539: foreach($items as $item) {
540: if($item->isActive()) {
541: return $item;
542: }
543: }
544:
545: return null;
546: }
547:
548: public function getSubmenu()
549: {
550: if($activeItem = $this->findActiveItem())
551: {
552: return $activeItem->getChildren();
553: }
554:
555: return new ItemList;
556: }
557:
558: public function breadcrumbs()
559: {
560:
561: $activeItem = $this->findActiveItem();
562:
563: $separator = $this->getOption('item_list.breadcrumb_separator');
564:
565:
566: $itemList = new ItemList(array(), 'breadcrumbs');
567:
568:
569: if( ! is_null($activeItem)) {
570:
571: $itemList->addContent($activeItem->getContent());
572:
573: while($nextItem = $activeItem->getParent()) {
574: if(is_null($nextItem->getParent())) break;
575:
576:
577: if ( ! empty($separator))
578: {
579: $itemList->raw($separator);
580: }
581:
582: $itemList->addContent($nextItem->getParent()->getContent());
583:
584:
585: $activeItem = $nextItem->getParent();
586: }
587: }
588:
589:
590: $itemList->reverse();
591:
592: return $itemList;
593: }
594:
595: public function map($callback)
596: {
597: array_map($callback, $this->children);
598:
599: return $this;
600: }
601:
602: 603: 604: 605: 606:
607: public function findItemListByName($name)
608: {
609: $itemLists = $this->getAllItemListsIncludingThisOne()
610: ->getMenuObjects();
611: foreach($itemLists as $itemList)
612: {
613: if($itemList->getName() == $name)
614: {
615: return $itemList;
616: }
617: }
618:
619: return false;
620: }
621:
622: 623: 624: 625: 626: 627: 628:
629: public function findByName($name)
630: {
631: return $this->findItemListByName($name);
632: }
633:
634: 635: 636: 637: 638: 639: 640:
641: public function find($name)
642: {
643: return $this->findItemListByName($name);
644: }
645:
646: 647: 648: 649: 650:
651: public function findItemByAttribute($key, $value)
652: {
653: $itemLists = $this->getAllItemListsIncludingThisOne()
654: ->getMenuObjects();
655:
656: foreach($itemLists as $itemList)
657: {
658: if($itemList->getAttibute($key) == $value)
659: {
660: return $itemList;
661: }
662: }
663:
664: return false;
665: }
666:
667: 668: 669: 670: 671:
672: public function findItemByUrl($url)
673: {
674: $itemList = $this->getItemsByContentType('Menu\Items\Contents\Link');
675: foreach($itemList->getChildren() as $item)
676: {
677: $content = $item->getContent();
678: if($content->getUrl() == $url)
679: {
680: return $item;
681: }
682: }
683:
684: return false;
685: }
686:
687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709:
710: public function hydrate($resolver, $decorator, $idField = 'id', $parentIdField = 'parent_id', $parentId = 0)
711: {
712: $items = is_callable($resolver) ? $resolver() : $resolver;
713:
714: if($items instanceof Collection)
715: {
716: $items = $items->all();
717: }
718:
719: $itemsForThisLevel = array_filter($items, function($item) use ($parentId, $parentIdField)
720: {
721: return $parentId == (is_object($item) ? (isset($item->$parentIdField) ? $item->$parentIdField : 0) : (isset($item[$parentIdField]) ? $item[$parentIdField] : 0));
722: });
723:
724: foreach($itemsForThisLevel as $item)
725: {
726:
727: $decorator($this, $item);
728:
729:
730: $newestItem = end($this->children);
731:
732:
733: if($newestItem)
734: {
735:
736: $newestItemList = $newestItem->getChildren();
737:
738:
739: $parentId = is_object($item) ? $item->$idField : $item[$idField];
740:
741:
742: $newestItemList->hydrate($items, $decorator, $idField, $parentIdField, $parentId);
743: }
744: }
745:
746: return $this;
747: }
748:
749: 750: 751: 752: 753: 754: 755:
756: public function render($depth = 0)
757: {
758: if( ! is_int($depth))
759: {
760: throw new Exception("The render method doesn't take any arguments anymore, you can now configure your menu via the config file.");
761: }
762:
763:
764: $maxDepth = $this->getOption('max_depth');
765: if ($maxDepth !== -1 and $depth > $maxDepth) return false;
766:
767:
768: $contents = null;
769: if(count($this->children) == 0)
770: {
771: return "";
772: }
773:
774: foreach ($this->children as $item) {
775: $contents .= $item->render($depth + 1);
776: }
777:
778: $element = $this->getElement();
779: if ($element) $contents = Element::create($element, $contents, $this->attributes)->render();
780: return $contents;
781: }
782: }
783: