Самый частый вопрос новичка в OctoberCMS – это: "чем создание плагина через Artisan лучше чем через Builder?". В этой инструкции я не буду отвечать на него, я лишь попытаюсь вам обьяснить и показать как создать свой плагин, чтобы вы могли сами дать ответ на этот вопрос.
Я разделил эту инструкцию на две части. Они будут очень объемными, поэтому в первой части я расскажу о том как полностью сделать и подготовить бекенд, во второй я распишу как создать работающий фронтенд с помощью компонентов.
Эта инструкция сделана с целью, кратко и без усложнений показать сам процесс создания плагина, провести "за руку" читателя, чтобы в итоге при своем желании он смог открыть документацию, и опираясь на свой плагин, мог спокойно понять что и где находится.
Я не буду объяснять подробно что такое Artisan и понятие Скаффолдинг, или дотошно дублировать документацию. Не буду писать о том что такое модель данных или таблица в базе данных. Если вы пришли работать с OctoberCMS то у вас должны быть базовые знания понятия MVC. Можете почитать про это в википедии.
Для того чтобы создать свой плагин необходимо иметь доступ к SSH или любой другой консоли окружения вашего сайта, откуда вы сможете выполнить команды такие как php artisan
и т.д.
Помните что версия PHP CLI должна быть => 7.0
Чтобы проверить версию PHP CLI введите в консоль php -v
Вы увидите ответ от PHP примерно такой:
Приступим к созданию плагина.
Я приведу пример создания плагина, который выполняет роль небольшого каталога, с категориями и предметами, которые связаны с одной категорией. В итоге у нас должен получится сайт, который будет выводить все предметы, предметы по категории, и страницу одного предмета.
Открываем консоль и направляемся в корневую папку сайта, и вводим команду:
php artisan create:plugin Author.PluginName
. С помощью данной команды, мы создаем шаблон пустого плагина. После ввода команды, artisan сообщит вам об успешно выполненной команде.
Перейдем в папку плагина, и посмотрим что мы имеем на данный момент.
- Папка
updates
– в этой папке содержится обязательный файл version.yaml, и файлы миграции, которые вы закрепляете за определенной версией своего плагина в файле version.yaml
. (Не будем вдаваться в подробности, в процессе вы самостоятельно поймете что к чему)
- Файл
plugin.php
– основной файл нашего плагина.
Для реализации нашего каталога, нам нужно две модели данных:
- Category – информация о категориях.
- Item – информация о предметах.
При создании моделей, OctoberCMS автоматически создаст миграционные файлы в базу для этих моделей.
Создаем модели
Модели принято называть в единственном числе. Примеры:
- Модель категорий - Category
- Модель предметов - Item
- Модель постов - Post
- Модель комментариев - Comment
Сейчас мы создаем модель для категорий. Поэтому где ModelName
вводим Category
.
Возвращаемся в корневую папку нашего сайта, открываем консоль и вводим команду:
php artisan create:model Author.PluginName ModelName
После ввода команды, artisan сообщит вам об успешно выполненной команде.
Теперь создаем модель для предметов. Точно так-же вводим команду для создания модели и вместо ModelName
вписываем Item
Результат выполнения двух команд должен быть примерно таким-же как у меня на скриншоте. Везде Artisan должен вернуть текст Model created successfully
.
Теперь давайте перейдем в папку с нашими моделями, и посмотрим что Artisan для нас сделал.
Мы видим 2 папки и 2 файла с названиями наших моделей.
В папках будут лежать файлы columns.yaml
и fields.yaml
. Эти два файла необходимы для работы наших моделей в админке OctoberCMS.
Кратко о каждом файле:
columns.yaml
– Содержит параметры и информацию столбцов, которые нужно выводить при просмотре списка предметов в панели администратора.
fields.yaml
– Содержит параметры и информацию полей формы, которые нужно выводить при редактировании или создание предмета в панели администратора.
Всю более подробную матчасть, вы можете прочитать в официальной документации. Русская версия, Английская версия
Теперь давайте откроем файл Item.php, и посмотрим что у нас там есть.
namespace OctoClub\Tutorial\Models;
use Model;
/**
* Item Model
*/
class Item extends Model
{
/**
* @var string The database table used by the model.
*/
public $table = 'octoclub_tutorial_items';
/**
* @var array Guarded fields
*/
protected $guarded = ['*'];
/**
* @var array Fillable fields
*/
protected $fillable = [];
/**
* @var array Relations
*/
public $hasOne = [];
public $hasMany = [];
public $belongsTo = [];
public $belongsToMany = [];
public $morphTo = [];
public $morphOne = [];
public $morphMany = [];
public $attachOne = [];
public $attachMany = [];
}
Это шаблон пустой модели, которую для нас создал Artisan.
Здесь задаются параметры поведения модели данных: связи, мутаторы, события и т.д.
Подробнее о модели вы можете почитать в документации: Английская версия:, Русская версия:.
Прочитав комментарии вы можете увидеть, что Artisan так-же уже вписал название таблицы, которая будет хранится в базе. И так-же он создал пустые переменные в секции Relations
.
Давайте удалим все что нам не нужно.
namespace OctoClub\Tutorial\Models;
use Model;
/**
* Item Model
*/
class Item extends Model
{
/**
* @var string The database table used by the model.
*/
public $table = 'octoclub_tutorial_items';
/**
* @var array Relations
*/
public $belongsTo = [];
}
Как вы видите я удалил все пустые переменные кроме public $belongsTo
, она нам необходима, в ней мы опишем обратную связь с категорией типа "один ко многим". То-есть, у многих предметов есть одна категория.
Раздел в документации по этому типу связи Английская версия, Русская версия
Давайте сразу укажем связь, чтобы больше не возвращаться сюда.
public $belongsTo = [
'category' => ['OctoClub\Tutorial\Models\Category']
];
В массив связи $belongsTo
, я указал название связи 'category' и саму модель категории OctoClub\Tutorial\Models\Category
.
В итоге имеем:
namespace OctoClub\Tutorial\Models;
use Model;
/**
* Item Model
*/
class Item extends Model
{
/**
* @var string The database table used by the model.
*/
public $table = 'octoclub_tutorial_items';
/**
* @var array Relations
*/
public $belongsTo = [
'category' => ['OctoClub\Tutorial\Models\Category']
];
}
Выходим из Item.php и открываем Category.php, так-же удаляем все пустые переменные.
Должно получится что-то вроде такого:
namespace OctoClub\Tutorial\Models;
use Model;
/**
* Category Model
*/
class Category extends Model
{
/**
* @var string The database table used by the model.
*/
public $table = 'octoclub_tutorial_categories';
}
Здесь мы не добавили никаких связей. Нам пока это не нужно.
Мы закончили с моделями, давайте перейдем к самим таблицам, в которых будут хранится наши данные.
Открываем папку /updates
.
там вы должны увидеть что добавились два новых файла миграции:
create_categories_table.php
– файл миграции для модели категорий
create_items_table.php
– файл миграции для модели предметов
Давайте откроем файл миграции для модели категорий – create_categories_table.php
.
namespace OctoClub\Tutorial\Updates;
use Schema;
use October\Rain\Database\Schema\Blueprint;
use October\Rain\Database\Updates\Migration;
class CreateCategoriesTable extends Migration
{
public function up()
{
Schema::create('octoclub_tutorial_categories', function(Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('octoclub_tutorial_categories');
}
}
В нем вы увидите шаблон миграции.
Так-как мы создали модель, миграция содержит в себе вызов класса Schema с методом Up.
Внутри этого метода, мы указываем какие поля добавить при выполнении миграции.
Опять-же я не буду вдаваться в подробности что, как, и зачем.
Подробнее тут: Русская версия, Английская версия.
Для наших категорий, нам нужны такие поля:
- name - Название категории - обязательное поле не может быть пустым
- slug - Ссылка категории - обязательное поле не может быть пустым
- description - описание - необязательное поле, может быть NULL в дефолте
Впишем эти поля между
$table->increments('id');
$table->timestamps();
получаем
$table->increments('id');
$table->string('name');
$table->string('slug');
$table->text('description')->nullable();
$table->timestamps();
->nullable()
– мы указываем потому-что это поле может быть пустым и иметь дефолтное значение NULL
.
Обратите внимание на строку $table->timestamps();
, эта функция добавит для нас в таблицу два поля:
created_at
, updated_at
. Это два поля, в которые автоматически будут записываться когда была создана запись, и когда в последний раз изменена. Это полезно, не будем их удалять.
Сохраняем файл и переходим в файл create_items_table.php
Для наших предметов нам нужны поля:
- category_id - id категории, которой принадлежит предмет - обязательное поле не может быть пустым.
- name - Название предмета - обязательное поле не может быть пустым
- slug - Ссылка предмета - обязательное поле не может быть пустым
- description - описание - необязательное поле, может быть NULL в дефолте
Так-же вставляем между
$table->increments('id');
$table->timestamps();
Получаем:
$table->increments('id');
$table->integer('category_id');
$table->string('name');
$table->string('slug');
$table->text('description')->nullable();
$table->timestamps();
Мы с вами указали в миграционных файлах, какие поля должны содержать наши таблицы для категорий и для предметов.
Теперь мы должны указать OctoberCMS, что у нас в миграционных файлах есть изменения, и при выполнения обновления нужно применить их.
Открываем файл verison.yaml в папке /updates и добавляем новую версию с перечислением файлов, которые нужно выполнить при обновлении.
Должно получится так.
1.0.2:
- Migrations
- create_items_table.php
- create_categories_table.php
1.0.1: First version of Tutorial
Если у вас не проходит миграция или плагин не становится версией 1.0.2.
Не копируйте отсюда данный код, а напишите его самостоятельно, главное чтобы отступы были пробелами а не табами! Возможно может помочь дописать новую версию снизу а не сверху.
И так. На данный момент, мы имеем две модели, с заполненными миграционными файлами. И все что нам нужно сделать сейчас – это выполнить вызвать обновление, которое выполнит эти скрипты.
В корневой папке можете выполнить эту команду:
php artisan october:up
Или, вы можете зайти в админку во вкладку Настройки -> Обновления. И нажать на синюю кнопку проверить обновления. И в открывшимся окне нажать "Принудительно обновить".
Именно вторым шагом я и пошел. И в списке плагинов, появился наш плагин.
Так-же вы видите, что его версия 1.0.2 – именно та, которую мы указали в verison.yaml
Все. Наши таблицы в базе. Модели готовы.
Теперь нам нужен интерфейс для управлениями предметами и категориями в админке.
Для этого нам нужно создать контроллер для каждой модели.
Делается это точно так-же как и с моделями через Artisan. Только название контроллеров необходимо назвать так-же как и модель, только во множественном числе. То-есть у нас будет два контроллера Categories и Items. Создадим их с помощью команды:
php artisan create:controller OctoClub.Tutorial ControllerName
Пример выполнения:
В наших контроллерах ничего настраивать не нужно.
Artisan уже все за нас настроил, создал, и разместил.
Теперь давайте выведем ссылку на наш плагин в верхнем навигационном баре нашей админки.
Переходим в папку нашего плагина, и откроем файл plugin.php
.
Мотаем в самый вниз, пока не увидите заготовленную Artisan'ом функцию registerNavigation()
.
/**
* Registers back-end navigation items for this plugin.
*
* @return array
*/
public function registerNavigation()
{
return []; // Remove this line to activate
return [
'tutorial' => [
'label' => 'Tutorial',
'url' => Backend::url('octoclub/tutorial/mycontroller'),
'icon' => 'icon-leaf',
'permissions' => ['octoclub.tutorial.*'],
'order' => 500,
],
];
}
Для нас даже уже вставили пример того, как нужно добавить свой навигационный пункт.
Стираем строчку return []; // Remove this line to activate
.
и в массиве 'tutorial'
в строке 'url'
заменяем mycontroller на items
.
То-есть мы говорим чтобы открывался список предметов при нажатии на главную иконку плагина. Ведь мы не так часто будет пользоваться разделом категорий.
Сохраняем. в итоге должно получится так:
/**
* Registers back-end navigation items for this plugin.
*
* @return array
*/
public function registerNavigation()
{
return [
'tutorial' => [
'label' => 'Tutorial',
'url' => Backend::url('octoclub/tutorial/items'),
'icon' => 'icon-leaf',
'permissions' => ['octoclub.tutorial.*'],
'order' => 500,
],
];
}
Заходим в панель управления, и видим что наш пункт добавился в верхний бар. И страница с Items открывается при нажатии!
Теперь давайте создадим боковое меню, куда продублируем ссылку на страницу с предметами, и выведем ссылку на страницу с категориями. Делается это там-же где мы делали ссылку в верхний бар.
В данном случае все очень просто. В массив 'tutorial' добавим новую строчку с массивом sideMenu
'sideMenu' => [
]
Внутрь него мы вставляем то-же самое что мы имеем сейчас в качестве основного меню.
Я покажу вам код, я думаю вы поймете, на словах это очень путает...
/**
* Registers back-end navigation items for this plugin.
*
* @return array
*/
public function registerNavigation()
{
return [
'tutorial' => [
'label' => 'Tutorial',
'url' => Backend::url('octoclub/tutorial/items'),
'icon' => 'icon-leaf',
'permissions' => ['octoclub.tutorial.*'],
'order' => 500,
'sideMenu' => [
'items' => [
'label' => 'Предметы',
'url' => Backend::url('octoclub/tutorial/items'),
'icon' => 'icon-leaf',
'permissions' => ['octoclub.tutorial.*'],
'order' => 500,
],
'categories' => [
'label' => 'Категории',
'url' => Backend::url('octoclub/tutorial/categories'),
'icon' => 'icon-leaf',
'permissions' => ['octoclub.tutorial.*'],
'order' => 500,
],
]
],
];
}
Просто обратите внимание, что пункт меню, это небольшой массив, который содержит в себе название, ссылку, иконку и т.д. Боковое меню, это просто переменная у старшего элемента, внутри которого вставляется тот-же самый пункт)
Давайте откроем админку, и посмотрим что получилось.
Ну вот, вы великолепны! Ссылки работают, и корректно подсвечиваются!)
Суммирую данный этап. Мы имеем свои модели данных с настроенной связью между ними, которые не имеют ничего лишнего кроме того что нам нужно. Мы имеем свою панель управления, с работающей навигацией.
Давайте теперь приступим к созданию форм для моделей внутри их контроллеров.
Если вы сейчас нажмете на кнопку 'New item' на странице со списком предметов. То вы увидите что есть всего одно поле ID, которое к тому-же нельзя редактировать. Давайте сюда добавим свои поля модели, которые мы будем редактировать.
Переходим в папку /models
внутри своего плагина, и открываем папку модели Item – /item
, и в ней откроем файл fields.yaml
.
Данный файл, как я уже писал ранее необходим чтобы задать в нем поля, которые будут отображаться при редактировании / создании записи.
Добавление сюда своих полей это очень просто. Достаточно придерживаться отступов, ведь в yml файле они важны.
Это уже готовый код того, как все должно выглядеть в итоге.
# ===================================
# Form Field Definitions
# ===================================
fields:
id:
label: ID
disabled: true
name:
label: Название
span: auto
slug:
label: Ссылка
span: auto
preset:
field: name
type: slug
category:
label: Категория
type: relation
span: auto
description:
label: Описание
type: textarea
span: full
Не буду расписывать как делать пробелы и отступы, лишь кратко расскажу что тут происходит.
Я вставил сюда названия полей из базы данных, которые мы указывали в миграционных файлах, и указал им параметры.
Разберем каждый уникальный параметр отдельно:
label – название
span – параметр внутренней верстки OctoberCMS (можно указать: left, right, auto, full)
preset – это очень хитрый параметр, он слушает поле, которое я указал под ним в параметре field и преобразовывает вводимый текст в тип, который я указал ниже. То-есть когда мы будем вводить в поле name название предмета, OcotberCMS будет автоматически переводить текст в транслит и заменять пробелы на дефисы.
type – это тип поля, здесь я указал relation, это говорит нашей админке, что это поле, в которой я буду выбирать связь с моделью Category, и он сам подтянет мне данные из этой модели, и даст выбрать одну и запись в выпадающем списке.
Если хотите почитать по подробнее что здесь можно сделать с полями, все есть в документации: Русская версия, Английская версия.
Повторюсь еще раз, вложенность очень важна! Вложенность должна быть выполнена не табами а пробелами, учитывайте это в настройках своего редактора!
Сохраним, и откроем страницу создании предмета где раньше было лишь поле ID.
Не дурно! Теперь мы имеем готовую форму нашей модели данных.
Проделаем тоже самое с моделью Category.
fields.yaml
–
# ===================================
# Form Field Definitions
# ===================================
fields:
id:
label: ID
disabled: true
name:
label: Название
span: auto
slug:
label: Ссылка
span: auto
preset:
field: name
type: slug
description:
label: Описание
type: textarea
span: full
Поздравляю, теперь вы можете создать категорию, после перейти к созданию предмета, и увидите что в поле "категория" появилась ваша категория!
После создания по одной записи в каждой модели, зайдя на страницу со списком записей предметов, мы увидим не очень информативное перечисление наших записей где отображается только поле ID...
Исправим эту ситуацию.
Заходим в папку нашей модели предметов/item
и открываем файл columns.yaml
Здесь просто копируем и вставляем поля из fields.yaml внеся небольшие правки и удаления.
Было:
# ===================================
# List Column Definitions
# ===================================
columns:
id:
label: ID
searchable: true
Стало:
# ===================================
# List Column Definitions
# ===================================
columns:
id:
label: ID
searchable: true
sortable: true
name:
label: Название
searchable: true
sortable: true
slug:
label: Ссылка
searchable: true
sortable: true
category:
label: Категория
relation: category
select: name
searchable: true
sortable: true
created_at:
label: Создано
type: date
sortable: true
updated_at:
label: Обновлено
type: date
sortable: true
Здесь мы вывели те-же поля, что и в fields.yaml кроме поля с описанием, и добавили сюда два поля, created_at
, updated_at
. Я объяснял что это за поля когда мы вписывали миграцию, тут они нам и пригодились.
Как вы видите, здесь есть есть свой тип параметров, которые учитываются для отображения списка.
По их названию надеюсь понятно что они делают.
Если хотите подробнее прочитать, все есть в документации: Русская версия, Английская версия.
Абсолютно тоже самое, без изменений вставляем в columns.yaml
в модель категорий кроме:
category:
label: Категория
relation: category
select: name
searchable: true
sortable: true
Теперь мы имеем полностью готовый бекенд для своего плагина!
Можете создавать записи, цеплять их к категориям.
Вторая часть