В этой части, мы доведем до ума наш плагин, который мы начали делать в первой части инструкции. И выведем категории и предметы на страницы нашего сайта.
В итоге у нас должен получится сайт, который имеет страницы: каталог (все предметы на странице, в количестве 10 штук с пагинацией), страница категории (все предметы одной категории, в количестве 10 штук с пагинацией), страница предмета.
Приступим к работе.
Первое что нам нужно сделать – это создать компоненты, у нас их будет 3 штуки. Компоненты представляют из себя связующие модули плагинов с CMS страницами в OctoberCMS. Более детально про компоненты, вы можете прочитать в документации: Английская версия, Русская версия.
Наши компоненты:
- Компонент каталога – мы его добавим на страницу с ссылкой
/catalog
. Данный компонент будет выводить все предметы, независимо от категории, с постраничной навигацией (пагинация).
- Компонент категории – данный компонент будет на странице с ссылкой
/catalog/:category
. Данный компонент будет выводить все предметы, в зависимости slug категории, которая будет указана во второй секции ссылки страницы (:category
).
- Компонент предмета – он будет находится на странице с ссылкой
/catalog/:category/:item
. Данный компонент будет выводить один предмет, по slug, который будет указан в секции :item
ссылки страницы. Так-же он будет проверять секцию :category
на принадлежность категории к предмету.
Всех их мы настроим так, чтобы они не показывали нам не существующих страниц (отображение 404 ошибки в компоненте).
Создаем наши компоненты с помощью Artisan. Переходим в корневую папку проекта, открываем консоль, вводим: php artisan create:component Author.PluginName ComponentName
.
Создаем 3 компонента сразу, назовем их Catalog, Category, item.
Artisan для нас создаст шаблонные пустые компоненты, которые находятся в папке /components
нашего плагина. Давайте сразу дадим знать нашему плагину о том что у нас появилось 3 компонента.
Открываем файл plugin.php
в корне нашего плагина.
Мотаем в самый низ, и находим функцию: registerComponents()
. При создании плагина Artisan создал для нас шаблонную функцию с примером того как нужно подключать компоненты.
Удаляем заглушку
return []; // Remove this line to activate
И добавляем компоненты, по примеру, который стоит сразу под заглушкой.
Должно получится так:
/**
* Registers any front-end components implemented in this plugin.
*
* @return array
*/
public function registerComponents()
{
return [
'OctoClub\Tutorial\Components\Catalog' => 'OctoClubCatalog',
'OctoClub\Tutorial\Components\Category' => 'OctoClubCategory',
'OctoClub\Tutorial\Components\Item' => 'OctoClubItem',
];
}
Мы сообщили нашему плагину, что имеем 3 компонента. Давайте перейдем в админку, и подготовим страницы, и добавим в них наши компоненты. Чтобы далее не отвлекаться не на что более, кроме самих компонентов.
Открываем админку и переходим в раздел CMS.
Создаем страницы:
Название: Каталог
Адрес: /catalog
Перетаскиваем на страницу компонент Catalog Component.
Название: Категория
Адрес: /catalog/:category
Перетаскиваем на страницу компонент Category Component.
Название: Предмет
Адрес: /catalog/:category/:item
Перетаскиваем на страницу компонент Item Component.
Шаблоны, выбирайте на свой вкус, они никак не повлияют на работу.
Давайте проверим страницы, если вы откроете ссылку /catalog
на своем сайте, вы увидите то что наш компонент вывел свой дефолтный шаблон.
Если вы попытаетесь открыть например страницу /catalog/default
и /catalog/default/default
, вы увидите такой-же дефолтный шаблон.
Наши страницы готовы, компоненты привязаны, они открываются по правильному url. Давайте приступим к работе над самими компонентами.
Откроем папку с ними /components
в корневой папке нашего плагина.
Вы увидите структуру файлов, которая напоминает структуру у контроллеров и моделей. В php файлах пишем логику, в папках будут лежать наши шаблоны, которые выводятся на страницу в то место, куда вы перетянули компонент в CMS.
Начнем с компонента Catalog
, открываем Catalog.php
, и видим шаблон который для нас любезно вставил Artisan.
namespace OctoClub\Tutorial\Components;
use Cms\Classes\ComponentBase;
class Catalog extends ComponentBase
{
public function componentDetails()
{
return [
'name' => 'Catalog Component',
'description' => 'No description provided yet...'
];
}
public function defineProperties()
{
return [];
}
}
В данном компоненте нам нужно реализовать функционал:
- Вывод определенного количество записей, которые вы укажите в настройках компонента на странице CMS.
- Пагинация.
Давайте сначала укажем настройку, которую позволит в настройках компонента указать количество отображаемых записей.
Если вы не поняли про какую настройку я говорю, и где она будет отображаться, вот скриншот:
То-есть если вы зайдете на CMS страницу, и нажмете на белую плашку своего компонента откроется список доступных настроек. По дефолту вставлена уже одна настройка, это псевдоним. Если хотите узнать больше про компоненты, можете прочитать про них в документации: Английская версия, Русская версия.
В функцию defineProperties()
нашего компонента указываем настройки как по документации:
public function defineProperties()
{
return [
'items' => [
'title' => 'Количество',
'description' => 'Определяет количество предметов на одной странице',
'default' => '10',
],
];
}
Если сохранить, и перейти в раздел CMS и нажать на компонент, вы увидите что наша настройка теперь отображается.
Перейдем к написании логики для вывода данных.
Создаем под функцией defineProperties()
новую функцию onRun()
, данная функция будет вызываться во время построения страницы где есть компонент.
public function onRun()
{
}
Так как нам не нужно слушать url, или что-либо другое, сейчас нам нужно просто получить все предметы, и вывести их с пагинацией. Это очень просто.
$this->page['items'] = \OctoClub\Tutorial\Models\Item::paginate($this->property('items'));
Разберем что делает данная строчка.
$this->page['items']
– это мы указываем переменную, которая будет доступна на странице, к ней можно обращаться с помощью твига {{ items }}
.
\OctoClub\Tutorial\Models\Item::
– это наш класс модели предмета, то-есть мы обращаемся к нашей модели, это можно сократить, и вынести весь путь до нашей модели над классом компонента, в "раздел" use
, и после обращаться просто как Item::
.
paginate($this->property('items'))
– это запрос к модели, через функцию paginate
, то-есть мы забираем все предметы, и разделяем на страницы, с количеством, которые мы указали в переменной $this->property('items')
.
$this->property('items')
– это наш параметр, который мы указываем в настройках, и который мы указали в defineProperties()
.
По сути, одна строчка, это вся логика. Теперь надо перейти в шаблон компонента, и вывести это кликабельным списком.
Открываем папку /catalog
в папке /components
нашего плагина, там мы увидим файл default.htm. Открываем его, удаляем дефолтный шаблон от Artisan и начинаем верстать.
<ul>
{% for item in items %}
<li>
<a href="/catalog/{{ item.category.slug }}/{{ item.slug }}">{{ item.name }}</a>
</li>
{% endfor %}
</ul>
{{ items.render|raw }}
Разберем что мы тут имеем:
{% for item in items %}
{% endfor %}
Это обычный foreach() по массиву предметов из php.
<a href="/catalog/{{ item.category.slug }}/{{ item.slug }}">{{ item.name }}</a>
Здесь я вывел ссылку на определенный предмет, построив полную конструкцию ссылки из корневого каталога/категория предмета/предмет и вывел название предмета. Если вы обратите внимание на {{ item.category.slug }}
, то вы увидите, что я получил ссылку связанной категории через очень простую конструкцию, я обратился к связи category
, которую мы указали в belongsTo
нашей модели Item
. И вытащил из связанной категории её ссылку. Все очень просто!)
{{ items.render|raw }}
Это вывод пагинации, с дефолтным шаблоном ссылок.
Если вам нужны подробности, можете почитать про такой вид пагинации в документации: Английская версия, Русская версия.
Для полной конструкции, давайте выведем еще сюда список всех категорий.
Чтобы это сделать, надо сначала передать массив всех категорий на страницу, сделаем мы это в нашем компоненте Catalog
в функции onRun
.
То-есть функция onRun должна иметь такой финальный вид:
public function onRun()
{
$this->page['items'] = \OctoClub\Tutorial\Models\Item::paginate($this->property('items'));
$this->page['categories'] = \OctoClub\Tutorial\Models\Category::get();
}
Вместо paginate()
у класса Category
, мы указали get()
с помощью нее, мы просто получаем все категории.
Так-же выведем в шаблоне компонента /catalog/default.htm
.
Финальный вид кода компонента:
namespace OctoClub\Tutorial\Components;
use Cms\Classes\ComponentBase;
class Catalog extends ComponentBase
{
public function componentDetails()
{
return [
'name' => 'Catalog Component',
'description' => 'No description provided yet...'
];
}
public function defineProperties()
{
return [
'items' => [
'title' => 'Количество',
'description' => 'Определяет количество предметов на одной странице',
'default' => '10',
],
];
}
public function onRun()
{
$this->page['items'] = \OctoClub\Tutorial\Models\Item::paginate($this->property('items'));
$this->page['categories'] = \OctoClub\Tutorial\Models\Category::get();
}
}
Финальный вид верстки компонента:
<ul>
{% for category in categories %}
<li>
<a href="/catalog/{{ category.slug }}">{{ category.name }}</a>
</li>
{% endfor %}
</ul>
<hr>
<ul>
{% for item in items %}
<li>
<a href="/catalog/{{ item.category.slug }}/{{ item.slug }}">{{ item.name }}</a>
</li>
{% endfor %}
</ul>
{{ items.render|raw }}
Давайте зайдем на страницу, /catalog
и посмотрим что у нас получилось.
Все прекрасно отобразилось. Давайте добавим в админке больше предметов и категорий и посмотрим что мы получили:
Мы видим категории сверху, предметы по-середине, и пагинацию внизу.
Если пощелкать по пагинации, страницы будут листаться, и в url будет добавлен параметр пагинации ?page=2
. Если понажимать по ссылкам, будут открываться дефолтные шаблоны категорий и предмета.
Давайте перейдем к компонентам Category и Item.
Делаются они точно-также, только с небольшим условием, им нужно прослушивать ссылку. Звучит страшно, на деле это такой-же параметр компонента как и количество предметов на странице. Ага все так просто.
Открываем компонент Category.php
, и приступим к созданию параметров в defineProperties()
.
Сразу финальный вид данной функции:
public function defineProperties()
{
return [
'slug' => [
'title' => 'Ссылка категории',
'default' => '{{ :category }}',
],
'items' => [
'title' => 'Количество',
'description' => 'Определяет количество предметов на одной странице',
'default' => '10',
],
];
}
Обратите внимание на параметр slug
, мы указываем какой параметр ссылки должен быть для этого компонента по-дефолту. То-есть вы можете в ссылке указать /category/:hello-world
, и не заходя в код компонента, поменять ссылку в настройках компонента на hello-world
.
Перейдем к функции onRun()
, так-же создаем ее сразу под defineProperties()
как в компоненте Catalog
.
И пишем туда небольшую логику:
public function onRun()
{
$category = \OctoClub\Tutorial\Models\Category::where('slug', $this->property('slug'))->first();
if (empty($category)){
return $this->controller->run('404');
}
$items = \OctoClub\Tutorial\Models\Item::where('category_id', $category->id)->paginate($this->property('items'));
$this->page['category'] = $category;
$this->page['items'] = $items;
}
Опишу словами что мы тут делаем.
- Забираем одну запись категории с совпадающей ссылкой
slug
.
- Проверяем что категория нашлась и переменная
$category
не пустая, если пустая – выдаем 404 страницу.
- Находим все записи предметов, с id категории, которую мы сейчас получили, и заберем их все с пагинацией.
- После мы передаем на страницу переменные {{ category }} и {{ items }}.
Откроем теперь шаблон компонента /category/default.htm
и выведем все что мы имеем.
<hr>
<h2>{{ category.name }}</h2>
<p>{{ category.description }}</p>
<hr>
<ul>
{% for item in items %}
<li>
<a href="/catalog/{{ item.category.slug }}/{{ item.slug }}">{{ item.name }}</a>
</li>
{% else %}
<li>Предметов в этой категории пока нет.</li>
{% endfor %}
</ul>
{{ items.render|raw }}
Тоже самая конструкция только с выводом названия текущей категории, и ее описанием. И плюс к циклу {% for %}
я добавил условие {% else %}
, это нужно чтобы люди могли зайти на категорию, но если предметов не будет, им выведется сообщение что предметов в этой категории пока нет.
Выглядит у меня примерно так:
Если обратите внимание на ссылку, вы увидите нашу конструкцию – /catalog/velosipedy
. Попробуйте открыть ссылку /catalog/askjsdfk
, то-есть с несуществующей категорией. Сайт должен показать ошибку 404.
Перейдем к компоненту Item
. Откроем файл Item.php в папке с компонентами, и делаем все тоже самое как в случае с категорией, только за одним исключением, нам нужно прослушивать два параметра ссылки :category и :item, чтобы проверить принадлежность категории к предмету.
Наш defineProperties()
, с двумя ссылками:
public function defineProperties()
{
return [
'slugCategory' => [
'title' => 'Ссылка категории',
'default' => '{{ :category }}',
],
'slugItem' => [
'title' => 'Ссылка предмета',
'default' => '{{ :item }}',
]
];
}
Наш onRun()
:
public function onRun()
{
$category = \OctoClub\Tutorial\Models\Category::where('slug', $this->property('slugCategory'))->first();
if (empty($category)){
return $this->controller->run('404');
}
$item = \OctoClub\Tutorial\Models\Item::where('category_id', $category->id)->where('slug', $this->property('slugItem'))->first();
if (empty($item)){
return $this->controller->run('404');
}
$this->page['item'] = $item;
}
Что мы тут делаем:
- Забираем одну запись категории с совпадающей ссылкой
slug
.
- Проверяем что категория нашлась и переменная
$category
не пустая, если пустая – выдаем 404 страницу.
- Находим предмет, по id категории, которую мы сейчас получили, и параметром ссылки.
- Проверяем что предмет найден и переменная
$item
не пустая, если пустая – выдаем 404 страницу.
- После мы передаем на страницу переменную
{{ item }}
.
Перейдем к шаблону нашего компонента Item
в файл /item/default.htm
и выведем информацию по одному предмету.
Тут все просто:
<hr>
<h2>{{ item.name }}</h2>
<p>{{ item.description }}</p>
<hr>
Перейти к категории данного предмета: <a href="/catalog/{{ item.category.slug }}">{{ item.category.name }}</a>
<hr>
И откроем страницу любого предмета, и что мы видим:
Так-же тестируем отображения ошибки 404, и открываем ссылки:
/catalog/asd/suzuki
– существующий предмет, но не существующая категория
/catalog/motocikly/asd
– существующая категория, но не существующий предмет
/catalog/velosipedy/suzuki
– сщуествующая категория, и существующий предмет, которые не связаны между собою.
Готово! Теперь вы имеете свой собственный каталог, который написали самостоятельно, используя силу OctoberCMS и Laravel под капотом. Оцените свой плагин, насколько он компактный и интуитивный. Теперь, имея данные знания, вы можете делать практически что угодно.
Изучите документацию, добавьте больше связей, таблиц, и расширяйте свой плагин сколько захотите.
Ссылка на GitHub готового плагина, который мы делали в этой инструкции . Чтобы вы могли сверится с ним, если у вас что-то не получилось.
Всем спасибо за внимание, в дальнейшем я буду писать инструкции как расширять плагины, основываясь на этом плагине.