Задача: вывести на каждой странице сайта навигацию в трёх местах (шапка, подвал и мобильная версия). Можно по старинке, перед выводом страницы готовить навигацию вместе с остальными данными. А можно сделать в одном месте и забыть. В этом нам поможет View Composer.
Шаблон навигации
// resources/views/templates/menu.blade.php @foreach($menu as $item) <li @if($path == $item->url) class="active" @endif> <a href="{{ $item->url }}">{{ $item->name }}</a> </li> @endforeach
Мы вызываем его из разных мест разных шаблонов:
// resources/views/templates/header.blade.php <nav class="menu"> <div class="container"> <div class="row"> <div class="col"> <ul> @include('templates.menu') </ul> </div> </div> </div> </nav> // resources/views/templates/hamburger.blade.php <div class="hamburger__inner"> <ul class="hamburger__list"> @include('templates.menu') </ul> </div> // resources/views/templates/footer.blade.php <footer class="footer"> <nav class="menu"> <div class="container"> <div class="row"> <div class="col"> <ul> @include('templates.menu') </ul> </div> </div> </div> </nav> </footer>
Давайте привяжем к шаблону навигации необходимые данные. Создадим каталог app/Http/ViewComposers, а внутри него файл MenuComposer.php
namespace App\Http\ViewComposers; use App\Services\MemoryCache; use Illuminate\View\View; use App\Navigation; class MenuComposer { use MemoryCache; public $menu; public $path; /** * Create a menu composer. * * @return void */ public function __construct() { $this->menu = $this->menu(); $this->path = $this->path(); } public function path() { return parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); } public function menu() { // get from cache or database $item = $this->cache(function() { return Navigation::whereActive('1')->orderBy('position')->get(); }); return $item; } /** * Bind data to the view. * * @param View $view * @return void */ public function compose(View $view) { $view->with('menu', $this->menu); $view->with('path', $this->path); } }
Вы, вероятно, обратили внимание на трейт MemoryCache, но с ним разберемcя позже. А сейчас нам нужно зарегистрироваться в сервис-провайдере.
В каталоге app/Providers создаём файл ComposerServiceProvider.php и привязываем ‘templates.menu
‘ к App\Http\ViewComposers\MenuComposer
namespace App\Providers; use Illuminate\Support\ServiceProvider; class ComposerServiceProvider extends ServiceProvider { /** * Bootstrap services. * * @return void */ public function boot() { view()->composer( 'templates.menu', 'App\Http\ViewComposers\MenuComposer' ); } /** * Register services. * * @return void */ public function register() { // } }
Давайте пропишем его в config/app.php в секции провайдеров:
'providers' => [ ... App\Providers\ComposerServiceProvider::class, ],
Готово! Теперь при рендере шаблона menu будет срабатывать MenuComposer.php и создавать переменные $menu и $path.
Но меню у нас вызывается не один раз, зачем нагружать базу данных? Давайте кешировать. Для этого и служит трейт MemoryCache
// app/Services/MemoryCache.php // Author: jlem namespace App\Services; trait MemoryCache { public static $cache = []; /** * Gets the cached result by key, * or executes the closure if cache key does not exist * @param \Closure $closure * @return mixed */ protected function cache(\Closure $closure) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT,2)[1]; $class = $backtrace['class']; $method = $backtrace['function']; $args = $backtrace['args']; $key = $class . '::'. $method . '.' . md5(serialize($args)); if (!array_key_exists($key, self::$cache)) { self::$cache[$key] = call_user_func_array($closure, $args); } return self::$cache[$key]; } public static function clear() { self::$cache = []; } }
Берем данные по ключу, либо, если ключа не существует, выполняем замыкание.
Теперь точно готово! Независимое меню, которое само себе генерирует данные и не нагружает базу данных.
Автор: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.