Overview

Namespaces

  • Menu
    • Items
      • Contents
    • Traits

Classes

  • Menu\Items\Contents\Link
  • Menu\Items\Contents\Raw
  • Menu\Items\Item
  • Menu\Items\ItemList
  • Menu\Menu
  • Menu\MenuHandler
  • Menu\MenuServiceProvider
  • Menu\Traits\Content
  • Menu\Traits\MenuObject
  • Overview
  • Namespace
  • Class
  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:  * A container for Items
 21:  */
 22: class ItemList extends MenuObject
 23: {
 24:   /**
 25:    * The name of this ItemList
 26:    *
 27:    * @var string
 28:    */
 29:   public $name;
 30: 
 31:   /**
 32:    * Create a new Item List instance
 33:    *
 34:    * @param string  $name        The ItemList's name
 35:    * @param array   $attributes  Attributes for the ItemList's HMTL element
 36:    * @param string  $element     The HTML element for the ItemList
 37:    *
 38:    * @return void
 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:    * Get the last Item
 50:    *
 51:    * @return Item
 52:    */
 53:   public function onItem()
 54:   {
 55:     return $this->children[sizeof($this->children) - 1];
 56:   }
 57: 
 58:   ////////////////////////////////////////////////////////////////////
 59:   ///////////////////////// PUBLIC INTERFACE /////////////////////////
 60:   ////////////////////////////////////////////////////////////////////
 61: 
 62:   /**
 63:    * Set a particular option in the array
 64:    *
 65:    * @param string $option The option
 66:    * @param mixed  $value  Its new value
 67:    *
 68:    * @return MenuObject
 69:    */
 70:   public function setOption($option, $value)
 71:   {
 72:     // forward item config values to the items
 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:    * Add a link item to the ItemList instance.
 91:    *
 92:    * <code>
 93:    *    // Add a item to the default menu
 94:    *    Menu::add('home', 'Homepage');
 95:    *
 96:    *    // Add a item with a subitem to the default menu
 97:    *    Menu::add('home', 'Homepage', Menu::items()->add('home/sub', 'Subitem'));
 98:    *
 99:    *    // Add a item with attributes for the item's HTML element
100:    *    Menu::add('home', 'Homepage', null, array('class' => 'fancy'));
101:    * </code>
102:    *
103:    * @param string   $url             Url of the link
104:    * @param string   $value           (H)T(ML) inside of the link
105:    * @param ItemList $children        Children
106:    * @param array    $linkAttributes  Attributes for the link
107:    * @param array    $itemAttributes  Attributes for the item
108:    * @param string   $itemElement     The element for the item
109:    * @param string   $beforeContent   String to add before the link
110:    * @param string   $afterContent    String to add after the link
111:    *
112:    * @return ItemList
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:    * Add a raw html item to the ItemList instance.
124:    *
125:    * <code>
126:    *    // Add a raw item to the default main menu
127:    *    Menu::raw('<img src="img/seperator.gif">');
128:    * </code>
129:    *
130:    * @param string   $raw            The raw content
131:    * @param ItemList $children       Children
132:    * @param array    $itemAttributes The item attributes
133:    * @param string   $itemElement    The item element
134:    * @param string   $beforeContent  String to add before the raw content
135:    * @param string   $afterContent   String to add after the raw content
136:    *
137:    * @return ItemList
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:    * Add content to the ItemList
149:    *
150:    * @param Content   $content        Content object
151:    * @param ItemList  $children       Children
152:    * @param array     $itemAttributes Attributes for the item (li)
153:    * @param string    $itemElement    Element for the item (li is default)
154:    * @param string    $beforeContent  String to add before the content
155:    * @param string    $afterContent   String to add after the content
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:     // Set Item as parent of its children
163:     if (!is_null($children)) {
164:       $children->setParent($item);
165:     }
166: 
167:     $this->setChild($item);
168: 
169:     return $item;
170:   }
171: 
172:   /**
173:    * Add an active pattern to the ItemList instance.
174:    *
175:    * <code>
176:    *    // Add a item to the default menu and set an active class for /user/5/edit
177:    *    Menu::add('user', 'Users')->activePattern('\/user\/\d\/edit');
178:    * </code>
179:    *
180:    * @param string   $pattern
181:    *
182:    * @return ItemList
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:    * Add menu items to another ItemList.
195:    *
196:    * <code>
197:    *    // Attach menu items to the default MenuHandler
198:    *    Menu::attach(Menu::items()->add('home', 'Homepage'));
199:    * </code>
200:    *
201:    * @param  ItemList $itemList
202:    *
203:    * @return ItemList
204:    */
205:   public function attach(ItemList $itemList)
206:   {
207:     $this->nestChildren($itemList->getChildren());
208: 
209:     return $this;
210:   }
211: 
212:   /**
213:    * Set the name for this ItemList
214:    *
215:    * @param string  $name
216:    *
217:    * @return ItemList
218:    */
219:   public function name($name)
220:   {
221:     $this->name = $name;
222: 
223:     return $this;
224:   }
225: 
226:   /**
227:    * Get the name of the ItemList
228:    *
229:    * @return string Name of the ItemList
230:    */
231:   public function getName()
232:   {
233:     return $this->name;
234:   }
235: 
236:   ////////////////////////////////////////////////////////////////////
237:   ///////////////////////////// PREFIXES /////////////////////////////
238:   ////////////////////////////////////////////////////////////////////
239: 
240:   /**
241:    * Prefix this ItemList with a string
242:    *
243:    * @param string $prefix
244:    *
245:    * @return ItemList
246:    */
247:   public function prefix($prefix)
248:   {
249:     $this->setOption('item_list.prefix', $prefix);
250: 
251:     return $this;
252:   }
253: 
254:   /**
255:    * Prefix this ItemList with the parent ItemList(s) name(s)
256:    *
257:    * @param boolean $prefixParents
258:    *
259:    * @return ItemList
260:    */
261:   public function prefixParents($prefixParents = true)
262:   {
263:     $this->setOption('item_list.prefix_parents', $prefixParents);
264: 
265:     return $this;
266:   }
267: 
268:   /**
269:    * Prefix this ItemList with the name of the ItemList at the very top of the tree
270:    *
271:    * @param boolean $prefixMenuHandler
272:    *
273:    * @return ItemList
274:    */
275:   public function prefixMenuHandler($prefixMenuHandler = true)
276:   {
277:     $this->setOption('item_list.prefix_handler', $prefixMenuHandler);
278: 
279:     return $this;
280:   }
281: 
282:   /**
283:    * Set the Item's element
284:    *
285:    * @param string $element
286:    */
287:   public function setElement($element = null)
288:   {
289:     $this->element = $element;
290:     return $this;
291:   }
292: 
293:   /**
294:    * Get the Item's element
295:    *
296:    * @return string
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:    * Get all items with the depth as key
309:    *
310:    * @return array
311:    */
312:   public function getItemsWithDepth()
313:   {
314:     return $this->getItemsRecursivelyWithDepth($this->getChildren());
315:   }
316: 
317:   /**
318:    * Get all items for an array of items recursively for a specific depth
319:    *
320:    * @return array
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:    * Get all itemlists with the depth as key
345:    *
346:    * @return array
347:    */
348:   public function getItemListsWithDepth()
349:   {
350:     return $this->getItemListsRecursivelyWithDepth($this);
351:   }
352: 
353:   /**
354:    * Get all itemlists for an itemlsit recursively for a specific depth
355:    *
356:    * @return array
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:    * Get all items
381:    *
382:    * @return \Vespakoen\Menu\MenuHandler
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:    * Get items by their content type
401:    *
402:    * @param  string $contentType The full object name
403:    *
404:    * @return \VEspakoen\Menu\MenuHandler
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:    * Get all itemlists
425:    *
426:    * @return \Vespakoen\Menu\MenuHandler
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:    * Get all itemslists including this one
445:    *
446:    * @return \Vespakoen\Menu\MenuHandler
447:    */
448:   public function getAllItemListsIncludingThisOne()
449:   {
450:     return $this->getAllItemLists()
451:       ->addMenuObject($this);
452:   }
453: 
454:   /**
455:    * Get itemlists at a certain depth
456:    *
457:    * @return \Vespakoen\Menu\MenuHandler
458:    */
459:   public function getItemListsAtDepth($depth)
460:   {
461:     $itemListsWithDepth = $this->getItemListsWithDepth();
462: 
463:     return new MenuHandler($itemListsWithDepth[$depth]);
464:   }
465: 
466:   /**
467:    * Get itemlists in a range of depths
468:    *
469:    * @return \Vespakoen\Menu\MenuHandler
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:    * Get all items at a certain depth
492:    *
493:    * @return \Vespakoen\Menu\MenuHandler
494:    */
495:   public function getItemsAtDepth($depth)
496:   {
497:     $itemsWithDepth = $this->getItemsWithDepth();
498: 
499:     return new MenuHandler($itemsWithDepth[$depth]);
500:   }
501: 
502:   /**
503:    * Get items in a range of depths
504:    *
505:    * @return \Vespakoen\Menu\MenuHandler
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:     // Find the active one
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:     // Collect all items
561:     $activeItem = $this->findActiveItem();
562: 
563:     $separator  = $this->getOption('item_list.breadcrumb_separator');
564: 
565:     // Make the breadcrumbs
566:     $itemList = new ItemList(array(), 'breadcrumbs');
567: 
568:     // Fill her up if we found the active link
569:     if( ! is_null($activeItem)) {
570:       // Add the found item
571:       $itemList->addContent($activeItem->getContent());
572:       // Loop throught the parents until we hit the root
573:       while($nextItem = $activeItem->getParent()) {
574:         if(is_null($nextItem->getParent())) break;
575: 
576:         // Add a separator and the link
577:         if ( ! empty($separator))
578:         {
579:           $itemList->raw($separator);
580:         }
581: 
582:         $itemList->addContent($nextItem->getParent()->getContent());
583: 
584:         // Set the activeItem for the next iteration
585:         $activeItem = $nextItem->getParent();
586:       }
587:     }
588: 
589:     // Correct order
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:    * Find an itemlist by it's name
604:    *
605:    * @return \Vespakoen\Menu\Items\ItemLists|false
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:    * Find an itemlist by it's name
624:    *
625:    * alias for findItemListByName
626:    *
627:    * @return \Vespakoen\Menu\Items\ItemLists|false
628:    */
629:   public function findByName($name)
630:   {
631:     return $this->findItemListByName($name);
632:   }
633: 
634:   /**
635:    * Find an itemlist by it's name
636:    *
637:    * alias for findItemListByName
638:    *
639:    * @return \Vespakoen\Menu\Items\ItemLists|false
640:    */
641:   public function find($name)
642:   {
643:     return $this->findItemListByName($name);
644:   }
645: 
646:   /**
647:    * Find an item by an attribute
648:    *
649:    * @return \Vespakoen\Menu\Items\Item|false
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:    * Find an item by it's link's URL
669:    *
670:    * @return \Vespakoen\Menu\Items\Item|false
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:    * Easily create items while looping over DB results
689:    * that have a reference to the parent (usually via parentId)
690:    *
691:    * <code>
692:    *     Menu::hydrate(function($parentId)
693:    *       {
694:    *         return Page::where('parent_id', $parentId)
695:    *           ->get();
696:    *       },
697:    *       function($children, $page)
698:    *       {
699:    *         $children->add($page->slug, $page->name);
700:    *       });
701:    * </code>
702:    *
703:    * @param Closure $resolver   the callback to resolve results for a given parentId
704:    * @param Closure $decorator  the callback that modifies the ItemList for the given node
705:    * @param integer $idField    the id column that matches with the parentId
706:    * @param integer $parentId   the parentId to start hydrating from
707:    *
708:    * @return ItemList the
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:       // Let the decorator add the item(s) (and maybe set some attributes)
727:       $decorator($this, $item);
728: 
729:       // Grab the newest item
730:       $newestItem = end($this->children);
731: 
732:       // If there is an item, add hydrate it
733:       if($newestItem)
734:       {
735:         // Grab the newest itemlist
736:         $newestItemList = $newestItem->getChildren();
737: 
738:         // Get the id of the item
739:         $parentId = is_object($item) ? $item->$idField : $item[$idField];
740: 
741:         // Hydrate the children
742:         $newestItemList->hydrate($items, $decorator, $idField, $parentIdField, $parentId);
743:       }
744:     }
745: 
746:     return $this;
747:   }
748: 
749:   /**
750:    * Get the evaluated string content of the ItemList.
751:    *
752:    * @param  integer $depth The depth at which the ItemList should be rendered
753:    *
754:    * @return string
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:     // Check for maximal depth
764:     $maxDepth = $this->getOption('max_depth');
765:     if ($maxDepth !== -1 and $depth > $maxDepth) return false;
766: 
767:     // Render contained items
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: 
API documentation generated by ApiGen