Магазин на Laravel 7, часть 7. Меню каталога товаров и популярные бренды в левой колонке
07.10.2020
Теги: Composer • Laravel • MySQL • PHP • Web-разработка • БазаДанных • ИнтернетМагазин • КаталогТоваров • Переменная • Практика • Фреймворк • ШаблонСайта
Два меню с сайдбаре
На всех страницах сайта в левой колонке показывается меню каталога и список популярных брендов. Это значит, что эти данные мы должны получать всегда, и отправлять их в layout-шаблон. Именно для таких случаев в Laravel предусмотрено готовое решение — View Composers. Но лучше мы создадим два маленьких шаблона (roots.blade.php
и brands.blade.php
) в директории layout/part
, чтобы не прегружать layout-шаблон — и с помощью композера будет отправлять в них данные.
<h4>Разделы каталога</h4> <ul> @foreach($items as $item) <li> <a href="{{ route('catalog.category', ['slug' => $item->slug]) }}">{{ $item->name }}</a> </li> @endforeach </ul>
<h4>Популярные бренды</h4> <ul> @foreach($items as $item) <li> <a href="{{ route('catalog.brand', ['slug' => $item->slug]) }}">{{ $item->name }}</a> <span class="badge badge-dark float-right">{{ $item->products_count }}</span> </li> @endforeach </ul>
И будем подключать эти два шаблона внутри layout-шаблона:
<div class="row"> <div class="col-md-3"> @include('layout.part.roots') @include('layout.part.brands') <!-- <h4>Разделы каталога</h4> <p>Здесь будут корневые разделы</p> <h4>Популярные бренды</h4> <p>Здесь будут популярные бренды</p> --> </div> <div class="col-md-9"> @yield('content') </div> </div>
Теперь создадим поставщика услуг ComposerServiceProvider
:
> php artisan make:provider ComposerServiceProvider
И сразу добавим его в массив providers
файла конфигурации config/app.php
:
return [ /* ... */ 'providers' => [ /* ... */ App\Providers\ComposerServiceProvider::class, /* ... */ ], /* ... */ ];
Далее редактируем созданный app/Providers/ComposerServiceProvider.php
:
namespace App\Providers; use App\Brand; use App\Category; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; class ComposerServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { // ..... } /** * Bootstrap services. * * @return void */ public function boot() { View::composer('layout.part.roots', function($view) { $view->with(['items' => Category::roots()]); }); View::composer('layout.part.brands', function($view) { $view->with(['items' => Brand::popular()]); }); } }
В модель Category
добавляем метод roots()
, а в модель Brand
— метод popular()
:
namespace App; use Illuminate\Database\Eloquent\Model; class Category extends Model { /* ... */ /** * Возвращает список корневых категорий каталога товаров */ public static function roots() { return self::where('parent_id', 0)->get(); } }
namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\DB; class Brand extends Model { /* ... */ /** * Возвращает список популярных брендов каталога товаров. * Следовало бы отобрать бренды, товары которых продаются * чаще всего. Но поскольку таких данных у нас еще нет, * просто получаем 5 брендов с наибольшим кол-вом товаров */ public static function popular() { return self::withCount('products')->orderByDesc('products_count')->limit(5)->get(); } }
Все, можно проверять, что получилось в итоге:
При желании, мы можем показывать в меню каталога не только корневые категории, но и дочерние. Для этого добавим в модель Category
новую связь таблицы categories
с таблицей categories
.
namespace App; use Illuminate\Database\Eloquent\Model; class Category extends Model { /* ... */ /** * Связь «один ко многим» таблицы `categories` с таблицей `categories` */ public function children() { return $this->hasMany(Category::class, 'parent_id'); } /** * Возвращает список корневых категорий каталога товаров */ public static function roots() { return self::where('parent_id', 0)->get(); } }
Теперь в шаблоне roots.blade.php
мы можем обратиться к виртуальному свойству children
, чтобы получить список дочерних категорий:
<h4>Разделы каталога</h4> <div id="catalog-sidebar"> <ul> @foreach($items as $item) <li> <a href="{{ route('catalog.category', ['slug' => $item->slug]) }}">{{ $item->name }}</a> @if ($item->children->count()) <ul> @foreach($item->children as $child) <li> <a href="{{ route('catalog.category', ['slug' => $child->slug]) }}"> {{ $child->name }} </a> </li> @endforeach </ul> @endif </li> @endforeach </ul> </div>
Добавляем javascript
Давайте добавим возможность сворачивать и разворачивать меню каталога в сайдбаре. При загрузке страницы видны будут только корневые разделы каталога. При клике по иконке с плюсом будут показаны дочерние категории. При повторном клике по иконке (но уже с минусом) дочерние категории будут скрыты.
<h4>Разделы каталога</h4> <div id="catalog-sidebar"> <ul> @foreach($items as $item) <li> <a href="{{ route('catalog.category', ['slug' => $item->slug]) }}">{{ $item->name }}</a> @isset($item->children) <span class="badge badge-dark"> <i class="fa fa-plus"></i> <!-- бейдж с плюсом или минусом --> </span> <ul> @foreach($item->children as $child) <li> <a href="{{ route('catalog.category', ['slug' => $child->slug]) }}"> {{ $child->name }} </a> </li> @endforeach </ul> @endisset </li> @endforeach </ul> </div>
Создадим файл site.js
в директории public/js
и добавим в него следующий код:
jQuery(document).ready(function($) { $('#catalog-sidebar > ul ul').hide(); $('#catalog-sidebar .badge').on('click', function () { var $badge = $(this); var closed = $badge.siblings('ul') && !$badge.siblings('ul').is(':visible'); if (closed) { $badge.siblings('ul').slideDown('normal', function () { $badge.children('i').removeClass('fa-plus').addClass('fa-minus'); }); } else { $badge.siblings('ul').slideUp('normal', function () { $badge.children('i').removeClass('fa-minus').addClass('fa-plus'); }); } }); });
Подключим этот js-файл в layout-шаблоне и посмотрим результат:
Жадная загрузка
Сейчас для построения меню у нас выполняется пять запросов к базе данных, которые получают корневые категории + дочерние категории для каждой корневой.
SELECT * FROM `categories` WHERE `parent_id` = 0
SELECT * FROM `categories` WHERE `categories`.`parent_id` = 1 AND `categories`.`parent_id` IS NOT NULL SELECT * FROM `categories` WHERE `categories`.`parent_id` = 2 AND `categories`.`parent_id` IS NOT NULL SELECT * FROM `categories` WHERE `categories`.`parent_id` = 3 AND `categories`.`parent_id` IS NOT NULL SELECT * FROM `categories` WHERE `categories`.`parent_id` = 4 AND `categories`.`parent_id` IS NOT NULL
Происходит это из-за так называемой ленивой загрузки данных (отложенная загрузка). Именно она используется при обращении к виртуальному свойству children
, доступному после реализации связи таблицы categories
с таблицей categories
. Использовать ленивую загрузку очень удобно, поскольку она работает только тогда, когда мы ее вызываем.
Но в данном случае вместо отложенной загрузки нужно использовать другой вариант загрузки связанных данных — жадную загрузку. Этот вариант выбирает данные всегда, вне зависимости от того, потребуются ли они в дальнейшем. Но мы точно знаем, что дочерние категории нам потребуются, так что будем их выбирать из базы данных сразу.
class Category extends Model { /* ... */ /** * Возвращает список корневых категорий каталога товаров */ public static function roots() { return self::where('parent_id', 0)->with('children')->get(); } }
Теперь вместо пяти запросов к базе данных будет выполнено только два:
SELECT * FROM `categories` WHERE `parent_id` = 0 SELECT * FROM `categories` WHERE `categories`.`parent_id` IN (1, 2, 3, 4)
- Магазин на Laravel 7, часть 24. Фильтр товаров категории по цене, новинкам и лидерам продаж
- Магазин на Laravel 7, часть 23. Главная страница сайта, новинки, лидеры продаж и распродажа
- Магазин на Laravel 7, часть 21. Добавляем профили и используем их при оформлении заказа
- Магазин на Laravel 7, часть 20. Показ отдельной страницы и верхнее меню всех страниц
- Магазин на Laravel 7, часть 18. Панель управления, пользователи и CRUD страниц сайта
- Магазин на Laravel 7, часть 17. Панель управления, работа с заказами, изменение статуса
- Магазин на Laravel 7, часть 16. Панель управления, CRUD-операции для товаров каталога
Поиск: Composer • Laravel • Web-разработка • Интернет магазин • Каталог товаров • Переменная • Практика • Фреймворк • Шаблон сайта