diff --git a/src/Builder.php b/src/Builder.php index 9e898f6..f49cae7 100755 --- a/src/Builder.php +++ b/src/Builder.php @@ -8,597 +8,602 @@ class Builder { - /** - * @var array - */ - protected $items; - - /** - * @var Collective\Html\HtmlBuilder - */ - protected $html; - - /** - * @var string - */ - protected $name; - - /** - * @var array - */ - protected $config; - - /** - * @var array - */ - protected $reserved = ['route', 'action', 'url', 'prefix', 'parent', 'secure', 'raw']; - - /** - * @var int - */ - protected $lastId; - - /** - * @var Illuminate\Routing\UrlGenerator - */ - protected $url; - - /** - * Create a new Builder instance. - * - * @param string $name - * @param array $config - * @param Collective\Html\HtmlBuilder $html - * @param Illuminate\Routing\UrlGenerator $url - */ - public function __construct($name, $config, HtmlBuilder $html, UrlGenerator $url) - { - $this->name = $name; - $this->config = $config; - $this->html = $html; - $this->url = $url; - $this->items = new Collection; - } - - /** - * Add an item to the defined menu. - * - * @param string $title - * @param array|string $options - * - * @return Item - */ - public function add($title, $options = '') - { - $item = new Item($this, $this->id(), $title, $options); - - $this->items->push($item); - - $this->lastId = $item->id; - - return $item; - } - - /** - * Generate a unique ID for every item added to the menu. - * - * @return int - */ - protected function id() - { - return $this->lastId + 1; - } - - /** - * Extract the valid attributes from the passed options. - * - * @param array $options - * - * @return array - */ - public function extractAttributes($options = array()) - { - if (is_array($options)) { - if (count($this->groupStack) > 0) { - $options = $this->mergeWithLastGroup($options); - } - - return array_except($options, $this->reserved); - } - - return array(); - } - - /** - * Converts the defined attributes into HTML. - * - * @param array $attributes - * - * @return string - */ - public function attributes($attributes = array()) - { - return $this->html->attributes($attributes); - } - - /** - * Insert a divider after the item. - * - * @param array $attributes - * - * @return void - */ - public function divide($attributes = array()) - { - $attributes['class'] = self::formatGroupClass(['class' => 'divider'], $attributes); - - $this->items->last()->divider = $attributes; - } - - /** - * Return the configuration value by key. - * - * @param string $key - * - * @return string - */ - public function config($key) - { - return $this->config[$key]; - } - - /** - * Get the prefix from the last group of the stack. - * - * @return mixed - */ - public function getLastGroupPrefix() - { - if (count($this->groupStack) > 0) { - return array_get(last($this->groupStack), 'prefix', ''); - } - - return null; - } - - /** - * Format the groups class. - * - * @return mixed - */ - public static function formatGroupClass($new, $old) - { - if (isset($new['class'])) { - $classes = trim(trim(array_get($old, 'class')).' '.trim(array_get($new, 'class'))); - - return implode(' ', array_unique(explode(' ', $classes))); - } - - return array_get($old, 'class'); - } - - /* - |-------------------------------------------------------------------------- - | Fetching Methods - |-------------------------------------------------------------------------- - | - */ - - /** - * Fetches and returns all menu items. - * - * @return Collection - */ - public function all() - { - return $this->items; - } - - /** - * Returns all items with no parents. - * - * @return Collection - */ - public function roots() - { - return $this->whereParent(); - } - - /** - * Fetches and returns a menu item by it's slug. - * - * @param string $slug - * - * @return Item - */ - public function get($slug) - { - return $this->whereSlug($slug)->first(); - } - - /** - * Facade method for the get() method. - * - * @param string $slug - * - * @return Item - */ - public function item($slug) - { - return $this->get($slug); - } - - /** - * Fetches and returns a menu item by it's ID. - * - * @param integer $id - * - * @return Item - */ - public function find($id) - { - return $this->whereId($id)->first(); - } - - /** - * Fetches and returns the first menu item. - * - * @return Item - */ - public function first() - { - return $this->items->first(); - } - - /** - * Fetches and returns the last menu item. - * - * @return Item - */ - public function last() - { - return $this->items->last(); - } - - /** - * Fetches and returns all active state menu items. - * - * @return Collection - */ - public function active() - { - $activeItems = array(); - - foreach ($this->items as $item) { - if ($item->data('active')) { - $activeItems[] = $item; - } - } - - return $activeItems; - } - - /* - |-------------------------------------------------------------------------- - | Dispatch Methods - |-------------------------------------------------------------------------- - | - */ - - /** - * Get the action type from the options. - * - * @param array $options - * - * @return string - */ - public function dispatch($options) - { - if (isset($options['url'])) { - return $this->getUrl($options); - } elseif (isset($options['route'])) { - return $this->getRoute($options['route']); - } elseif (isset($options['action'])) { - return $this->getAction($options['action']); - } - - return null; - } - - /** - * Get the action for a "url" option. - * - * @param array|string $options - * - * @return string - */ - protected function getUrl($options) - { - foreach ($options as $key => $value) { - $$key = $value; - } - - $secure = (isset($options['secure']) and $options['secure'] === true) ? true : false; - - if (is_array($url)) { - if (self::isAbsolute($url[0])) { - return $url[0]; - } - - return $this->url->to($prefix.'/'.$url[0], array_slice($url, 1), $secure); - } - - if (self::isAbsolute($url)) { - return $url; - } - - return $this->url->to($prefix.'/'.$url, array(), $secure); - } - - /** - * Get the route action for a "route" option. - * - * @param array|string $route - * - * @return string - */ - protected function getRoute($route) - { - if (is_array($route)) { - return $this->url->route($route[0], array_slice($route, 1)); - } - - return $this->url->route($route); - } - - /** - * Get the controller action for a "action" option. - * - * @param array|string $action - * - * @return string - */ - protected function getAction($action) - { - if (is_array($action)) { - return $this->url->action($action[0], array_slice($action, 1)); - } - - return $this->url->action($action); - } - - /** - * Determines if the given URL is absolute. - * - * @param string $url - * - * @return bool - */ - public static function isAbsolute($url) - { - return parse_url($url, PHP_URL_SCHEME) or false; - } - - /* - |-------------------------------------------------------------------------- - | Filter Methods - |-------------------------------------------------------------------------- - | - */ - - /** - * Filter menu items through a callback. - * - * Since menu items are stored as a collection, this will - * simply forward the callback to the Laravel Collection - * filter() method and return the results. - * - * @param callable $callback - * - * @return Builder - */ - public function filter($callback) - { - if (is_callable($callback)) { - $this->items = $this->items->filter($callback); - } - - return $this; - } - - /** - * Filter menu items recursively. - * - * @param string $attribute - * @param mixed $value - * - * @return Collection - */ - public function filterRecursively($attribute, $value) - { - $collection = new Collection; - - $this->items->each(function($item) use ($attribute, $value, &$collection) { - if (! property_exists($item, $attribute)) { - return false; - } - - if ($item->$attribute == $value) { - $collection->push($item); - - if ($item->hasChildren()) { - $collection = $collection->merge($this->filterRecursively($attribute, $item->id)); - } - } - }); - - return $collection; - } - - /** - * Sorts the menu based on key given in ascending order. - * - * @param string $key - * - * @return Builder - */ - public function sortBy($key) - { - $this->items = $this->items->sortBy(function($item) use ($key) { - return $item->$key; - }); - - return $this; - } - - /** - * Sorts the menu based on key given in descending order. - * - * @param string $key - * - * @return Builder - */ - public function sortByDesc($key) - { - $this->items = $this->items->sortByDesc(function($item) use ($key) { - return $item->$key; - }); - - return $this; - } - - /** - * Filter menu items based on Shinobi permissions. - * - * @return Builder - */ - public function guard() - { - if (class_exists('Caffeinated\Shinobi\Shinobi')) { - $this->filter(function ($item) { - if (! $item->data('can') and ! $item->data('canatleast')) { - return true; - } elseif ($item->data('canatleast')) { - return \Shinobi::canAtLeast($item->data('canatleast')); - } else { - return \Shinobi::can($item->data('can')); - } - }); - } - - return $this; - } - - /* - |-------------------------------------------------------------------------- - | Rendering Methods - |-------------------------------------------------------------------------- - | - */ - - /** - * Renders the menu as an unordered list. - * - * @param array $attributes - * @return string - */ - public function asUl($attributes = array()) - { - return "attributes($attributes)}>{$this->render('ul')}"; - } - - /** - * Generate the menu items as list items, recursively. - * - * @param string $type - * @param int $parent - * @return string - */ - protected function render($type = 'ul', $parent = null) - { - $items = ''; - $itemTag = in_array($type, ['ul', 'ol']) ? 'li' : $type; - - foreach ($this->whereParent($parent) as $item) { - $items .= "<{$itemTag}{$item->attributes()}>"; - - if ($item->link) { - $items .= "attributes($item->link->attr())} href=\"{$item->url()}\">{$item->title}"; - } else { - $items .= $item->title; - } - - if ($item->hasChildren()) { - $items .= "<{$type}>"; - $items .= $this->render($type, $item->id); - $items .= ""; - } - - $items .= ""; - - if ($item->divider) { - $items .= "<{$itemTag}{$this->attributes($item->divider)}>"; - } - } - - return $items; - } - - /** - * Dynamic search method against a menu attribute. - * - * @param string $method - * @param array $args - * - * @return Item|bool - */ - public function __call($method, $args) - { - preg_match('/^[W|w]here([a-zA-Z0-9_]+)$/', $method, $matches); - - if ($matches) { - $attribute = Str::lower($matches[1]); - } else { - throw new BadMethodCallException('Call to undefined method '.$method); - } - - $value = $args ? $args[0] : null; - $recursive = isset($args[1]) ? $args[1] : false; - - if ($recursive) { - return $this->filterRecursively($attribute, $value); - } - - return $this->items->filter(function($item) use ($attribute, $value) { - if (isset($item->data[$attribute]) && $item->data[$attribute] == $value) { - return true; - } - - if (! property_exists($item, $attribute)) { - return false; - } - - if ($item->$attribute == $value) { - return true; - } - - return false; - })->values(); - } - - /** - * Returns menu item by name. - * - * @param string $property - * - * @return Item - */ - public function __get($property) - { - if (property_exists($this, $property)) { - return $this->$property; - } - - return $this->whereSlug($property)->first(); - } + /** + * @var array + */ + protected $items; + + /** + * @var array + */ + protected $groupStack = []; + + /** + * @var Collective\Html\HtmlBuilder + */ + protected $html; + + /** + * @var string + */ + protected $name; + + /** + * @var array + */ + protected $config; + + /** + * @var array + */ + protected $reserved = ['route', 'action', 'url', 'prefix', 'parent', 'secure', 'raw']; + + /** + * @var int + */ + protected $lastId; + + /** + * @var Illuminate\Routing\UrlGenerator + */ + protected $url; + + /** + * Create a new Builder instance. + * + * @param string $name + * @param array $config + * @param Collective\Html\HtmlBuilder $html + * @param Illuminate\Routing\UrlGenerator $url + */ + public function __construct($name, $config, HtmlBuilder $html, UrlGenerator $url) + { + $this->name = $name; + $this->config = $config; + $this->html = $html; + $this->url = $url; + $this->items = new Collection; + } + + /** + * Add an item to the defined menu. + * + * @param string $title + * @param array|string $options + * + * @return Item + */ + public function add($title, $options = '') + { + $item = new Item($this, $this->id(), $title, $options); + + $this->items->push($item); + + $this->lastId = $item->id; + + return $item; + } + + /** + * Generate a unique ID for every item added to the menu. + * + * @return int + */ + protected function id() + { + return $this->lastId + 1; + } + + /** + * Extract the valid attributes from the passed options. + * + * @param array $options + * + * @return array + */ + public function extractAttributes($options = array()) + { + if (is_array($options)) { + if (count($this->groupStack) > 0) { + $options = $this->mergeWithLastGroup($options); + } + + return array_except($options, $this->reserved); + } + + return array(); + } + + /** + * Converts the defined attributes into HTML. + * + * @param array $attributes + * + * @return string + */ + public function attributes($attributes = array()) + { + return $this->html->attributes($attributes); + } + + /** + * Insert a divider after the item. + * + * @param array $attributes + * + * @return void + */ + public function divide($attributes = array()) + { + $attributes['class'] = self::formatGroupClass(['class' => 'divider'], $attributes); + + $this->items->last()->divider = $attributes; + } + + /** + * Return the configuration value by key. + * + * @param string $key + * + * @return string + */ + public function config($key) + { + return $this->config[$key]; + } + + /** + * Get the prefix from the last group of the stack. + * + * @return mixed + */ + public function getLastGroupPrefix() + { + if (count($this->groupStack) > 0) { + return array_get(last($this->groupStack), 'prefix', ''); + } + + return null; + } + + /** + * Format the groups class. + * + * @return mixed + */ + public static function formatGroupClass($new, $old) + { + if (isset($new['class'])) { + $classes = trim(trim(array_get($old, 'class')).' '.trim(array_get($new, 'class'))); + + return implode(' ', array_unique(explode(' ', $classes))); + } + + return array_get($old, 'class'); + } + + /* + |-------------------------------------------------------------------------- + | Fetching Methods + |-------------------------------------------------------------------------- + | + */ + + /** + * Fetches and returns all menu items. + * + * @return Collection + */ + public function all() + { + return $this->items; + } + + /** + * Returns all items with no parents. + * + * @return Collection + */ + public function roots() + { + return $this->whereParent(); + } + + /** + * Fetches and returns a menu item by it's slug. + * + * @param string $slug + * + * @return Item + */ + public function get($slug) + { + return $this->whereSlug($slug)->first(); + } + + /** + * Facade method for the get() method. + * + * @param string $slug + * + * @return Item + */ + public function item($slug) + { + return $this->get($slug); + } + + /** + * Fetches and returns a menu item by it's ID. + * + * @param integer $id + * + * @return Item + */ + public function find($id) + { + return $this->whereId($id)->first(); + } + + /** + * Fetches and returns the first menu item. + * + * @return Item + */ + public function first() + { + return $this->items->first(); + } + + /** + * Fetches and returns the last menu item. + * + * @return Item + */ + public function last() + { + return $this->items->last(); + } + + /** + * Fetches and returns all active state menu items. + * + * @return Collection + */ + public function active() + { + $activeItems = array(); + + foreach ($this->items as $item) { + if ($item->data('active')) { + $activeItems[] = $item; + } + } + + return $activeItems; + } + + /* + |-------------------------------------------------------------------------- + | Dispatch Methods + |-------------------------------------------------------------------------- + | + */ + + /** + * Get the action type from the options. + * + * @param array $options + * + * @return string + */ + public function dispatch($options) + { + if (isset($options['url'])) { + return $this->getUrl($options); + } elseif (isset($options['route'])) { + return $this->getRoute($options['route']); + } elseif (isset($options['action'])) { + return $this->getAction($options['action']); + } + + return null; + } + + /** + * Get the action for a "url" option. + * + * @param array|string $options + * + * @return string + */ + protected function getUrl($options) + { + foreach ($options as $key => $value) { + $$key = $value; + } + + $secure = (isset($options['secure']) and $options['secure'] === true) ? true : false; + + if (is_array($url)) { + if (self::isAbsolute($url[0])) { + return $url[0]; + } + + return $this->url->to($prefix.'/'.$url[0], array_slice($url, 1), $secure); + } + + if (self::isAbsolute($url)) { + return $url; + } + + return $this->url->to($prefix.'/'.$url, array(), $secure); + } + + /** + * Get the route action for a "route" option. + * + * @param array|string $route + * + * @return string + */ + protected function getRoute($route) + { + if (is_array($route)) { + return $this->url->route($route[0], array_slice($route, 1)); + } + + return $this->url->route($route); + } + + /** + * Get the controller action for a "action" option. + * + * @param array|string $action + * + * @return string + */ + protected function getAction($action) + { + if (is_array($action)) { + return $this->url->action($action[0], array_slice($action, 1)); + } + + return $this->url->action($action); + } + + /** + * Determines if the given URL is absolute. + * + * @param string $url + * + * @return bool + */ + public static function isAbsolute($url) + { + return parse_url($url, PHP_URL_SCHEME) or false; + } + + /* + |-------------------------------------------------------------------------- + | Filter Methods + |-------------------------------------------------------------------------- + | + */ + + /** + * Filter menu items through a callback. + * + * Since menu items are stored as a collection, this will + * simply forward the callback to the Laravel Collection + * filter() method and return the results. + * + * @param callable $callback + * + * @return Builder + */ + public function filter($callback) + { + if (is_callable($callback)) { + $this->items = $this->items->filter($callback); + } + + return $this; + } + + /** + * Filter menu items recursively. + * + * @param string $attribute + * @param mixed $value + * + * @return Collection + */ + public function filterRecursively($attribute, $value) + { + $collection = new Collection; + + $this->items->each(function ($item) use ($attribute, $value, &$collection) { + if (! property_exists($item, $attribute)) { + return false; + } + + if ($item->$attribute == $value) { + $collection->push($item); + + if ($item->hasChildren()) { + $collection = $collection->merge($this->filterRecursively($attribute, $item->id)); + } + } + }); + + return $collection; + } + + /** + * Sorts the menu based on key given in ascending order. + * + * @param string $key + * + * @return Builder + */ + public function sortBy($key) + { + $this->items = $this->items->sortBy(function ($item) use ($key) { + return $item->$key; + }); + + return $this; + } + + /** + * Sorts the menu based on key given in descending order. + * + * @param string $key + * + * @return Builder + */ + public function sortByDesc($key) + { + $this->items = $this->items->sortByDesc(function ($item) use ($key) { + return $item->$key; + }); + + return $this; + } + + /** + * Filter menu items based on Shinobi permissions. + * + * @return Builder + */ + public function guard() + { + if (class_exists('Caffeinated\Shinobi\Shinobi')) { + $this->filter(function ($item) { + if (! $item->data('can') and ! $item->data('canatleast')) { + return true; + } elseif ($item->data('canatleast')) { + return \Shinobi::canAtLeast($item->data('canatleast')); + } else { + return \Shinobi::can($item->data('can')); + } + }); + } + + return $this; + } + + /* + |-------------------------------------------------------------------------- + | Rendering Methods + |-------------------------------------------------------------------------- + | + */ + + /** + * Renders the menu as an unordered list. + * + * @param array $attributes + * @return string + */ + public function asUl($attributes = array()) + { + return "attributes($attributes)}>{$this->render('ul')}"; + } + + /** + * Generate the menu items as list items, recursively. + * + * @param string $type + * @param int $parent + * @return string + */ + protected function render($type = 'ul', $parent = null) + { + $items = ''; + $itemTag = in_array($type, ['ul', 'ol']) ? 'li' : $type; + + foreach ($this->whereParent($parent) as $item) { + $items .= "<{$itemTag}{$item->attributes()}>"; + + if ($item->link) { + $items .= "attributes($item->link->attr())} href=\"{$item->url()}\">{$item->title}"; + } else { + $items .= $item->title; + } + + if ($item->hasChildren()) { + $items .= "<{$type}>"; + $items .= $this->render($type, $item->id); + $items .= ""; + } + + $items .= ""; + + if ($item->divider) { + $items .= "<{$itemTag}{$this->attributes($item->divider)}>"; + } + } + + return $items; + } + + /** + * Dynamic search method against a menu attribute. + * + * @param string $method + * @param array $args + * + * @return Item|bool + */ + public function __call($method, $args) + { + preg_match('/^[W|w]here([a-zA-Z0-9_]+)$/', $method, $matches); + + if ($matches) { + $attribute = Str::lower($matches[1]); + } else { + throw new BadMethodCallException('Call to undefined method '.$method); + } + + $value = $args ? $args[0] : null; + $recursive = isset($args[1]) ? $args[1] : false; + + if ($recursive) { + return $this->filterRecursively($attribute, $value); + } + + return $this->items->filter(function ($item) use ($attribute, $value) { + if (isset($item->data[$attribute]) && $item->data[$attribute] == $value) { + return true; + } + + if (! property_exists($item, $attribute)) { + return false; + } + + if ($item->$attribute == $value) { + return true; + } + + return false; + })->values(); + } + + /** + * Returns menu item by name. + * + * @param string $property + * + * @return Item + */ + public function __get($property) + { + if (property_exists($this, $property)) { + return $this->$property; + } + + return $this->whereSlug($property)->first(); + } } diff --git a/src/Item.php b/src/Item.php index e7db856..09e593e 100755 --- a/src/Item.php +++ b/src/Item.php @@ -5,404 +5,404 @@ class Item { - /** - * @var \Caffeinated\Menus\Builder - */ - protected $builder; - - /** - * @var int - */ - public $id; - - /** - * @var string - */ - public $title; - - /** - * @var string - */ - public $slug; - - /** - * @var array - */ - public $divider = array(); - - /** - * @var int - */ - public $parent; - - /** - * @var array - */ - protected $data = array(); - - /** - * @var array - */ - public $attributes = array(); - - /** - * Constructor. - * - * @param \Caffeinated\Menus\Builder $builder - * @param int $id - * @param string $title - * @param array|string $options - */ - public function __construct($builder, $id, $title, $options) - { - $this->builder = $builder; - $this->id = $id; - $this->title = $title; - $this->slug = camel_case(str_slug($title, ' ')); - $this->attributes = $this->builder->extractAttributes($options); - $this->parent = (is_array($options) and isset($options['parent'])) ? $options['parent'] : null; - - $this->configureLink($options); - } - - public function builder() - { - return $this->builder; - } - - /** - * Configures the link for the menu item. - * - * @param array|string $options - * @return null - */ - public function configureLink($options) - { - if (! is_array($options)) { - $path = ['url' => $options]; - } elseif (isset($options['raw']) and $options['raw'] == true) { - $path = null; - } else { - $path = array_only($options, ['url', 'route', 'action', 'secure']); - } - - if (! is_null($path)) { - $path['prefix'] = $this->builder->getLastGroupPrefix(); - } - - $this->link = isset($path) ? new Link($path) : null; - - $this->checkActiveStatus(); - } - - /** - * Adds a sub item to the menu. - * - * @param string $title - * @param array|string $options - * @return \Caffeinated\Menus\Item - */ - public function add($title, $options = '') - { - if (! is_array($options)) { - $url = $options; - $options = array(); - $options['url'] = $url; - } - - $options['parent'] = $this->id; - - return $this->builder->add($title, $options); - } - - /** - * Fetch the formatted attributes for the item in HTML. - * - * @return string - */ - public function attributes() - { - return $this->builder->attributes($this->attributes); - } - - /** - * Get all attributes. - * - * @return array - */ - public function getAttributes() - { - return $this->attributes; - } - - /** - * Assign or fetch the desired attribute. - * - * @param array|string $attribute - * @param string $value - * @return mixed - */ - public function attribute($attribute, $value = null) - { - if (isset($attribute) and is_array($attribute)) { - if (array_key_exists('class', $attribute)) { - $this->attributes['class'] = $this->builder->formatGroupClass(['class' => $attribute['class']], $this->attributes); - unset ($attribute['class']); - } - - $this->attributes = array_merge($this->attributes, $attribute); - - return $this; - } elseif (isset($attribute) and isset($value)) { - if ($attribute == 'class') { - $this->attributes['class'] = $this->builder->formatGroupClass(['class' => $value], $this->attributes); - } else { - $this->attributes[$attribute] = $value; - } - - return $this; - } - - return isset($this->attributes[$attribute]) ? $this->attributes[$attribute] : null; - } - - /** - * Generates a valid URL for the menu item. - * - * @return string - */ - public function url() - { - if (! is_null($this->link)) { - if ($this->link->href) { - return $this->link->href; - } - - return $this->builder->dispatch($this->link->path); - } - } - - /** - * Prepends HTML to the item. - * - * @param string $html - * @return \Caffeinated\Menus\Item - */ - public function prepend($html) - { - $this->title = $html.' '.$this->title; - - return $this; - } - - /** - * Appends HTML to the item. - * - * @param string $html - * @return \Caffeinated\Menus\Item - */ - public function append($html) - { - $this->title = $this->title.' '.$html; - - return $this; - } - - /** - * Appends the specified icon to the item. - * - * @param string $icon - * @param string $type Can be either "fontawesome" or "glyphicon" - * @return \Caffeinated\Menus\Item - */ - public function icon($icon, $type = 'fontawesome') - { - switch ($type) { - case 'fontawesome': - $html = ''; - break; - - case 'glyphicon': - $html = ''; - break; - - case 'entypo': - $html = ''; - break; - - default: - $html = ''; - break; - } - - return $this->data('icon', $html); - } - - /** - * Return the title with the icon prepended automatically. - * - * @return string - */ - public function prependIcon() - { - return $this->prepend($this->data('icon')); - } - - /** - * Return the title with the icon appended automatically. - * - * @return string - */ - public function appendIcon() - { - return $this->append($this->data('icon')); - } - - /** - * Insert a divider after the item. - * - * @param array $attributes - * @return void - */ - public function divide($attributes = array()) - { - $attributes['class'] = $this->builder->formatGroupClass($attributes, ['class' => 'divider']); - - $this->divider = $attributes; - - return $this; - } - - /** - * Determines if the menu item has children. - * - * @return bool - */ - public function hasChildren() - { - return count($this->builder->whereParent($this->id)) or false; - } - - /** - * Returns all children underneath the menu item. - * - * @return \Caffeinated\Menus\Collection - */ - public function children() - { - return $this->builder->whereParent($this->id); - } - - /** - * Set or get an item's metadata. - * - * @param mixed - * @return string|\Caffeinated\Menus\Item - */ - public function data() - { - $args = func_get_args(); - - if (isset($args[0]) and is_array($args[0])) { - $this->data = array_merge($this->data, array_change_key_case($args[0])); - - return $this; - } elseif (isset($args[0]) and isset($args[1])) { - $this->data[strtolower($args[0])] = $args[1]; - - return $this; - } elseif (isset($args[0])) { - return isset($this->data[$args[0]]) ? $this->data[$args[0]] : null; - } - - return $this->data; - } - - /** - * Decide if the item should be active. - * - * @return null - */ - public function checkActiveStatus() - { - $path = ltrim(parse_url($this->url(), PHP_URL_PATH), '/'); - $requestPath = Request::path(); - - if ($this->builder->config['rest_base']) { - $base = (is_array($this->builder->config['rest_base'])) ? implode('|', $this->builder->config['rest_base']) : $this->builder->conf['rest_base']; - - list($path, $requestPath) = preg_replace('@^('.$base.')/@', '', [$path, $requestPath], 1); - } - - if ($this->url() == Request::url() || $this->url() == \URL::secure(Request::path())) { - $this->activate(); - } - } - - public function activate(Item $item = null) - { - $item = (is_null($item)) ? $this : $item; - - $item->active(); - - $item->data('active', true); - - if ($item->parent) { + /** + * @var \Caffeinated\Menus\Builder + */ + protected $builder; + + /** + * @var int + */ + public $id; + + /** + * @var string + */ + public $title; + + /** + * @var string + */ + public $slug; + + /** + * @var array + */ + public $divider = array(); + + /** + * @var int + */ + public $parent; + + /** + * @var array + */ + protected $data = array(); + + /** + * @var array + */ + public $attributes = array(); + + /** + * Constructor. + * + * @param \Caffeinated\Menus\Builder $builder + * @param int $id + * @param string $title + * @param array|string $options + */ + public function __construct($builder, $id, $title, $options) + { + $this->builder = $builder; + $this->id = $id; + $this->title = $title; + $this->slug = camel_case(str_slug($title, ' ')); + $this->attributes = $this->builder->extractAttributes($options); + $this->parent = (is_array($options) and isset($options['parent'])) ? $options['parent'] : null; + + $this->configureLink($options); + } + + public function builder() + { + return $this->builder; + } + + /** + * Configures the link for the menu item. + * + * @param array|string $options + * @return null + */ + public function configureLink($options) + { + if (! is_array($options)) { + $path = ['url' => $options]; + } elseif (isset($options['raw']) and $options['raw'] == true) { + $path = null; + } else { + $path = array_only($options, ['url', 'route', 'action', 'secure']); + } + + if (! is_null($path)) { + $path['prefix'] = $this->builder->getLastGroupPrefix(); + } + + $this->link = isset($path) ? new Link($path) : null; + + $this->checkActiveStatus(); + } + + /** + * Adds a sub item to the menu. + * + * @param string $title + * @param array|string $options + * @return \Caffeinated\Menus\Item + */ + public function add($title, $options = '') + { + if (! is_array($options)) { + $url = $options; + $options = array(); + $options['url'] = $url; + } + + $options['parent'] = $this->id; + + return $this->builder->add($title, $options); + } + + /** + * Fetch the formatted attributes for the item in HTML. + * + * @return string + */ + public function attributes() + { + return $this->builder->attributes($this->attributes); + } + + /** + * Get all attributes. + * + * @return array + */ + public function getAttributes() + { + return $this->attributes; + } + + /** + * Assign or fetch the desired attribute. + * + * @param array|string $attribute + * @param string $value + * @return mixed + */ + public function attribute($attribute, $value = null) + { + if (isset($attribute) and is_array($attribute)) { + if (array_key_exists('class', $attribute)) { + $this->attributes['class'] = $this->builder->formatGroupClass(['class' => $attribute['class']], $this->attributes); + unset($attribute['class']); + } + + $this->attributes = array_merge($this->attributes, $attribute); + + return $this; + } elseif (isset($attribute) and isset($value)) { + if ($attribute == 'class') { + $this->attributes['class'] = $this->builder->formatGroupClass(['class' => $value], $this->attributes); + } else { + $this->attributes[$attribute] = $value; + } + + return $this; + } + + return isset($this->attributes[$attribute]) ? $this->attributes[$attribute] : null; + } + + /** + * Generates a valid URL for the menu item. + * + * @return string + */ + public function url() + { + if (! is_null($this->link)) { + if ($this->link->href) { + return $this->link->href; + } + + return $this->builder->dispatch($this->link->path['url']); + } + } + + /** + * Prepends HTML to the item. + * + * @param string $html + * @return \Caffeinated\Menus\Item + */ + public function prepend($html) + { + $this->title = $html.' '.$this->title; + + return $this; + } + + /** + * Appends HTML to the item. + * + * @param string $html + * @return \Caffeinated\Menus\Item + */ + public function append($html) + { + $this->title = $this->title.' '.$html; + + return $this; + } + + /** + * Appends the specified icon to the item. + * + * @param string $icon + * @param string $type Can be either "fontawesome" or "glyphicon" + * @return \Caffeinated\Menus\Item + */ + public function icon($icon, $type = 'fontawesome') + { + switch ($type) { + case 'fontawesome': + $html = ''; + break; + + case 'glyphicon': + $html = ''; + break; + + case 'entypo': + $html = ''; + break; + + default: + $html = ''; + break; + } + + return $this->data('icon', $html); + } + + /** + * Return the title with the icon prepended automatically. + * + * @return string + */ + public function prependIcon() + { + return $this->prepend($this->data('icon')); + } + + /** + * Return the title with the icon appended automatically. + * + * @return string + */ + public function appendIcon() + { + return $this->append($this->data('icon')); + } + + /** + * Insert a divider after the item. + * + * @param array $attributes + * @return void + */ + public function divide($attributes = array()) + { + $attributes['class'] = $this->builder->formatGroupClass($attributes, ['class' => 'divider']); + + $this->divider = $attributes; + + return $this; + } + + /** + * Determines if the menu item has children. + * + * @return bool + */ + public function hasChildren() + { + return count($this->builder->whereParent($this->id)) or false; + } + + /** + * Returns all children underneath the menu item. + * + * @return \Caffeinated\Menus\Collection + */ + public function children() + { + return $this->builder->whereParent($this->id); + } + + /** + * Set or get an item's metadata. + * + * @param mixed + * @return string|\Caffeinated\Menus\Item + */ + public function data() + { + $args = func_get_args(); + + if (isset($args[0]) and is_array($args[0])) { + $this->data = array_merge($this->data, array_change_key_case($args[0])); + + return $this; + } elseif (isset($args[0]) and isset($args[1])) { + $this->data[strtolower($args[0])] = $args[1]; + + return $this; + } elseif (isset($args[0])) { + return isset($this->data[$args[0]]) ? $this->data[$args[0]] : null; + } + + return $this->data; + } + + /** + * Decide if the item should be active. + * + * @return null + */ + public function checkActiveStatus() + { + $path = ltrim(parse_url($this->url(), PHP_URL_PATH), '/'); + $requestPath = Request::path(); + + if ($this->builder->config['rest_base']) { + $base = (is_array($this->builder->config['rest_base'])) ? implode('|', $this->builder->config['rest_base']) : $this->builder->conf['rest_base']; + + list($path, $requestPath) = preg_replace('@^('.$base.')/@', '', [$path, $requestPath], 1); + } + + if ($this->url() == Request::url() || $this->url() == \URL::secure(Request::path())) { + $this->activate(); + } + } + + public function activate(Item $item = null) + { + $item = (is_null($item)) ? $this : $item; + + $item->active(); + + $item->data('active', true); + + if ($item->parent) { $parent = $this->builder->whereId($item->parent)->first(); $parent->attributes['class'] = $parent->builder->formatGroupClass(['class' => 'opened'], $parent->attributes); - $this->activate($parent); - } - } - - public function active($pattern = null) - { - if (! is_null($pattern)) { - $pattern = ltrim(preg_replace('/\/\*/', '(/.*)?', $pattern), '/'); - - if (preg_match("@{$pattern}\z@", Request::path())) { - $this->activate(); - } - - return $this; - } - - $this->attributes['class'] = $this->builder->formatGroupClass(['class' => 'active'], $this->attributes); - - return $this; - } - - /** - * Returns bool value if item is active or not. - * - * @return bool - */ - public function isActive() - { - return $this->data('active'); - } - - public function can($permissions) - { - return $this->data('can', $permissions); - } - - public function canAtLeast($permissions) - { - return $this->data('canatleast', $permissions); - } - - /** - * Return either a property or attribute item value. - * - * @param string $property - * @return string - */ - public function __get($property) - { - if (property_exists($this, $property)) { - return $this->$property; - } - - return $this->data($property); - } + $this->activate($parent); + } + } + + public function active($pattern = null) + { + if (! is_null($pattern)) { + $pattern = ltrim(preg_replace('/\/\*/', '(/.*)?', $pattern), '/'); + + if (preg_match("@{$pattern}\z@", Request::path())) { + $this->activate(); + } + + return $this; + } + + $this->attributes['class'] = $this->builder->formatGroupClass(['class' => 'active'], $this->attributes); + + return $this; + } + + /** + * Returns bool value if item is active or not. + * + * @return bool + */ + public function isActive() + { + return $this->data('active'); + } + + public function can($permissions) + { + return $this->data('can', $permissions); + } + + public function canAtLeast($permissions) + { + return $this->data('canatleast', $permissions); + } + + /** + * Return either a property or attribute item value. + * + * @param string $property + * @return string + */ + public function __get($property) + { + if (property_exists($this, $property)) { + return $this->$property; + } + + return $this->data($property); + } }