Статьи

Конструктор динамических меню для Bootstrap 3: элемент и ссылка

В первой части мы создали прототип конечного продукта и написали основной класс Menu, который служит менеджером меню – контейнер для хранения всех подразделений (элементов и ссылок). В этой части мы создадим оставшиеся классы и продемонстрируем использование построителя меню.

Вещь

Представляет наши пункты меню как независимые объекты.

Создайте новый файл item.php и вставьте следующий код:

item.php

 class Item { protected $manager; protected $id; protected $pid; protected $meta; protected $attributes = array(); public $link; //... 
  • $manager хранит ссылку на менеджер меню (объект Menu ). Это позволяет нам использовать методы менеджера меню в контексте Item .
  • $id хранит $id элемента.
  • $pid хранит идентификатор родительского элемента, если он есть, в противном случае он будет установлен в null .
  • $meta массив для хранения дополнительных данных с каждым элементом.
  • $attributes – массив атрибутов HTML.
  • $link хранит экземпляр класса Link .

__construct (менеджер, заголовок, URL, атрибуты, pid)

Инициализирует атрибуты.

 public function __construct($manager, $title, $url, $attributes = array(), $pid = 0) { $this->manager = $manager; $this->id = $this->id(); $this->pid = $pid; $this->title = $title; $this->attributes = $attributes; // Create an object of type Link $this->link = new Link($title, $url); } 

добавить (название, параметры)

Класс Class также имеет метод add() (как и менеджер меню). На самом деле этот метод не создает элементы самостоятельно. Он получает аргументы, добавляет ключ pid к $options и вызывает add() менеджера меню.

 public function add($title, $options) { if( !is_array($options) ) { $options = array('url' => $options); } $options['pid'] = $this->id; return $this->manager->add( $title, $options ); } 

Это дает нам возможность создавать вложенные элементы более семантическим способом, а не явно определять pid :

 $menu = new Menu; $about = $menu->add('About', 'about'); // We write it this way $about->add('What we do?', 'what-we-do'); // instead of: // $menu->add('What we do?', array('url' => 'what-we-do', 'pid' => $about->get_id())); 

Я бы()

Создает уникальный идентификатор для элемента. Мы используем этот идентификатор, чтобы обратиться к пункту позже.

 protected function id() { return $this->manager->length() + 1; } 

На самом деле id() вызывает length() менеджера меню и увеличивает его на 1.

get_id ()

Нам также нужно создать метод получения, чтобы при необходимости возвращать идентификатор:

 public function get_id() { return $this->id; } 

get_pid ()

Предметы могут иметь pid (идентификатор родителя). Значение pid может быть нулевым или идентификатором другого элемента.

Элементы с нулевым значением pid – это элементы корневого уровня.

Нам также нужно создать геттер для возврата pid элемента:

 public function get_pid() { return $this->pid; } 

HasChildren ()

Проверяет, есть ли у элемента дети или нет.

 public function hasChildren() { return (count($this->manager->whereParent($this->id))) ? true : false; } 

Он вызывает whereParent() через менеджера.

дети()

Принимает детей из пункта.

 public function children() { return $this->manager->whereParent($this->id); } 

атрибуты (ключ, значение)

Получает или задает атрибуты элемента.

 public function attributes() { $args = func_get_args(); if(is_array($args[0])) { $this->attributes = array_merge($this->attributes, $args[0]); return $this; } elseif(isset($args[0]) && isset($args[1])) { $this->attributes[$args[0]] = $args[1]; return $this; } elseif(isset($args[0])) { return isset($this->attributes[$args[0]]) ? $this->attributes[$args[0]] : null; } return $this->attributes; } 

Как вы видите, attributes() возвращает разные типы результатов в соответствии с заданными аргументами:

  • Устанавливает атрибут, если указаны оба $key и $value .
  • Устанавливает массив атрибутов, если $key является массивом.
  • Получает атрибут, если задан только $key .
  • Получает все атрибуты, если аргумент не указан.

мета ($ key, $ value)

Мета хранит дополнительные данные об элементе. Это могут быть любые данные от порядка размещения до требуемых разрешений.

 public function meta() { $args = func_get_args(); if(is_array($args[0])) { $this->meta = array_merge($this->meta, $args[0]); return $this; } elseif(isset($args[0]) && isset($args[1])) { $this->meta[$args[0]] = $args[1]; return $this; } elseif(isset($args[0])) { return isset($this->meta[$args[0]]) ? $this->meta[$args[0]] : null; } return $this->meta; } 

meta() работает точно так же, как attributes() .

Теперь давайте перейдем к классу Link .

Class Link – это простой класс, состоящий из нескольких методов получения и установки.

Link имеет три атрибута:

  • текстовая ссылка текст
  • URL ссылки URL
  • атрибуты ссылка атрибуты

link.php

 class Link { public $text; public $url; public $attributes; //.... 

__construct (текст, URL, атрибуты)

Когда мы создаем объект типа Link , метод конструктора связывает аргументы с атрибутами, перечисленными выше:

 public function __construct($text, $url, $attributes = array()) { $this->text = $text; $this->url = $url; $this->attributes = $attributes; } 

строка get_url ()

Возвращает ссылку URL.

 public function get_url() { return $this->url; } 

строка get_text ()

Возвращает текст ссылки

 public function get_text() { return $this->text; } 

Вы столкнетесь с ситуациями, когда вам нужно добавить или добавить какой-либо контент к тексту привязки, например, знак вставки для выпадающих меню или графический значок. Чтобы достичь этого, мы создадим две простые функции, которые делают для нас именно то, что нужно.

Append (содержание)

append добавляет контент к тексту ссылки:

 public function append($content) { $this->text .= $content; return $this; } 

перед именем (содержание)

prepend добавляет контент к ссылке:

 public function prepend($content) { $this->text = $content . $this->text; return $this; } 

атрибуты (ключ, значение)

Как Items Было бы замечательно, если бы мы могли определить атрибуты HTML для якорей.

 public function attributes($key = null, $value = null) { $args = func_get_args(); if(is_array($args[0])) { $this->attributes = array_merge($this->attributes, $args[0]); return $this; } elseif(isset($args[0]) && isset($args[1])) { $this->attributes[$args[0]] = $args[1]; return $this; } elseif(isset($args[0])) { return isset($this->attributes[$args[0]]) ? $this->attributes[$args[0]] : null; } return $this->attributes; } 

Я думаю, что этот метод знаком вам, так как мы создали его ранее.

На этом наш Menu Builder готов!

использование

Мы обычно создаем один PHP-файл для каждого определения класса, поэтому, чтобы использовать наш конструктор меню, нам нужно включать каждый файл в начале нашего скрипта.

Вместо того, чтобы включить все три файла, я собираюсь воспользоваться функцией автозагрузки классов в PHP: __autoload(string $class) . Эта функция помогает нам избежать написания длинного списка включений в начале каждого сценария.

__autoload() автоматически вызывается, если вы пытаетесь использовать класс или интерфейс, который еще не определен.

__autoload получает имя класса в качестве аргумента.

Вот как мы собираемся использовать это:

 function __autoload($class) { require_once(strtolower($class) . '.php'); } 

Назовите этот файл autoload.php и autoload.php его в свой скрипт.

Обратите внимание, что это, вероятно, не идеально. В реальном проекте о ваших потребностях в автозагрузке будет заботиться Composer или автозагрузчик фреймворка. Вы можете увидеть это по предоставленной нами ссылке на Github – проект полностью разработан и настроен для использования вместе с Laravel.

Далее, давайте создадим меню для тестирования нашего построителя меню:

 <?php require_once('autoload.php'); $menu = new Menu; $about = $menu->add('About', 'about'); // since this item has sub items we append a caret icon to the hyperlink text $about->link->append(' <span class="caret"></span>'); // we can attach HTML attributes to the hyper-link as well $about->link->attributes(['class' => 'link-item', 'target' => '_blank']); // Adding an attribute to the item wrapper itself $about->attributes('data-model', 'info'); $about->add('Who we are?', array('url' => 'who-we-are', 'class' => 'navbar-item whoweare')); $about->add('What we do?', array('url' => 'what-we-do', 'class' => 'navbar-item whatwedo')); $about->add('Goals', array('url' => 'goals', 'display' => false)); $menu->add('Portfolio', 'portfolio'); $menu->add('Contact', 'contact'); // we're only going to hide items with `display` set to **false** $menu->filter( function($item){ if( $item->meta('display') === false) { return false; } return true; }); // Now we can render the menu as various HTML entities: echo $menu->asUl( attribute('class' => 'ausomw-ul') ); //OR echo $menu->asOl( attribute('class' => 'ausomw-ol') ); // OR echo $menu->asDiv( attribute('class' => 'ausomw-div') ); ?> 

Выполнено!

Bootstrap 3 Navbar

Последний шаг – использовать наш конструктор меню для создания динамических загрузочных навигационных панелей.

Прежде всего, нам нужно создать функцию, которая заполняет наши элементы в удобном для Bootstrap формате, потому что существующий метод render не делает этого за нас.

Я называю эту функцию bootstrapItems() (я не мог придумать лучшего имени, не стесняйтесь называть его как угодно).

Вы можете поместить эту функцию в любой файл, который вам нравится, если он загружен при запуске приложения. В качестве альтернативы вы можете расширить класс Menu и добавить этот метод в класс. В этом примере я помещаю его в autoloader.php (как вспомогательную функцию), чтобы он всегда был доступен для меня.

 function bootstrapItems($items) { // Starting from items at root level if( !is_array($items) ) { $items = $items->roots(); } foreach( $items as $item ) { ?> <li <?php if($item->hasChildren()): ?> class="dropdown" <?php endif ?>> <a href="<?php echo $item->link->get_url() ?>" <?php if($item->hasChildren()): ?> class="dropdown-toggle" data-toggle="dropdown" <?php endif ?>> <?php echo $item->link->get_text() ?> <?php if($item->hasChildren()): ?> <b class="caret"></b> <?php endif ?></a> <?php if($item->hasChildren()): ?> <ul class="dropdown-menu"> <?php bootstrapItems( $item->children() ) ?> </ul> <?php endif ?> </li> <?php } } 

Так как это только для образовательных целей, я не использовал здесь шаблонизатор, так как он выходит за рамки этого урока. Вы можете использовать выбранный вами шаблонизатор, чтобы отделить логику от представления и сделать ваш код более читабельным.

Давайте посмотрим, что BootstrapItems делает за кулисами.

Прежде всего, он проверяет, является ли данный аргумент массивом или нет. Если это не так, он выбирает элементы на корневом уровне и перебирает их. На каждой итерации он также проверяет, есть ли у текущего элемента дочерние элементы, и если у элемента есть дочерние элементы, он будет вызывать себя, передавая дочерние элементы в качестве параметра. Этот процесс повторяется до тех пор, пока все элементы не будут переведены на самый глубокий уровень.

Хорошо, теперь, когда мы можем генерировать элементы в удобном для Bootstrap формате, давайте зарегистрируем некоторые элементы:

 <?php require_once('autoload.php'); // $menu #1 $main = new Menu; $main->add('<span class="glyphicon glyphicon-home"></span>', ''); $about = $main->add('about', 'about'); $about->add('Who we are?', 'who-we-are?'); $about->add('What we do?', 'what-we-do?'); $main->add('Services', 'services'); $main->add('Portfolio', 'portfolio'); $main->add('Contact', 'contact'); // menu #2 $user = new Menu; $user->add('login', 'login'); $profile = $user->add('Profile', 'profile'); $profile->add('Account', 'account') ->link->prepend('<span class="glyphicon glyphicon-user"></span> '); $profile->add('Settings', 'settings') ->link->prepend('<span class="glyphicon glyphicon-cog"></span> '); ?> 

А вот наш стандартный код:

 <nav class="navbar navbar-default" role="navigation"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Sitepoint</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <?php echo bootstrapItems($main); ?> </ul> <form class="navbar-form navbar-left" role="search"> <div class="form-group"> <input type="text" class="form-control" placeholder="Search"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> <ul class="nav navbar-nav navbar-right"> <?php echo bootstrapItems($user); ?> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> 

Поскольку у нас есть два разных меню, мы вызываем BootstrapItems два раза в нашем шаблоне Bootstrap.

Не забудьте загрузить jquery и bootstrap файлы CSS и JS на вашу страницу, прежде чем проверять результат!

Вывод

Мы реализовали менеджер меню, элементы и ссылки в трех определениях классов для гибкости. Мы также сохранили ссылку на менеджера вместе с каждым элементом. Эта ссылка позволила нам получить доступ к менеджеру из контекста элемента.

Вы можете использовать этот конструктор меню в любой форме, которая вам нравится, если вы используете правильные методы.

Если вы используете Laravel 4, вы можете получить laravel-menu, которое реализовано на основе методов, описанных в этом руководстве, и при этом предоставляет больше возможностей, в противном случае смотрите полный код нашего построителя Menu Builder: fleximenu .

Удачного кодирования!