Дисклеймер: данный гайд написан опытным разработчиком (опыт 3 недели) и состоит из новых творческих фич (костылей) чуть менее, чем полностью.
Если серьезно, то многое из того, что я делал, я не очень то и понимал, но тем не менее, я надеюсь, что другим новичкам какая-то часть информации может оказаться полезной, учитывая скудность информации об октябре.
Задача была простая — в плагине Blog от Rainlab сделать сортировку для модели Post. В нем есть сортировка для Categories, но в постах нет. Сама сортировка не является чем то сложным, даже для меня, но проблемой стал нюанс — я не должен был изменять оригинальный плагин Blog.
Сразу к главной проблеме — трейт Sortable. Он нужен для работы сортировки. Его нельзя добавить динамически. И на этом можно(нужно) было заканчивать. Но я об этом узнал уже после нескольких дней так называемой работы над задачей, так что пришлось искать выход.
Ну и после долгого вступления, присутпим:
Первое — создаем свой плагин, на октоклабе есть гайд, им и пользовался. У меня он называется extendsorting.
В файле Plugin.php есть функция boot, в ней мы хотим добавить контроллеру Posts все необходмое, для реордера, но перед этим, сделаем еще одну вещь — скопируем бехйвор ReorderController.
Он находится в modules\backend\behaviors. Нам нужно будет редактировать и сам php файл, и partials, я на всякий случай скопировал все 🙂
Итак, в файле нашего плагина Plugin.php:
use \Rainlab\Blog\Controllers\Posts as PostsController;
В теле функции boot:
PostsController::extend(function($controller) {
$controller->implement[] = 'bulatov.extendsorting.controllers.reorder2';
$controller->addDynamicProperty('reorderConfig', '$/bulatov/extendsorting/controllers/posts/config_reorder.yaml');
$controller->listConfig = '$/bulatov/extendsorting/controllers/posts/config_list.yaml';
});
Мы динамически добавляем в implement контроллера Posts наш, скопированный, бихэйвор, у меня он называется reorder2, и скопировал я его просто в контроллеры нашего плагина.
Для работы сортировки нужен файл reorderConfig.yaml, так что подсунем его из нашего, пока еще не созданного контроллера.
Listconfig я добавил свой, т.к. не знал, как еще вывести саму кнопку Reorder в главное меню плагина Blog.
Далее нам необходимо создать свой контроллер Posts, унаследовать его от оригинального и самое главное — добавить сами конфигурационные файлы.
config_reorder:
# ===================================
# Reorder Behavior Config
# ===================================
# Reorder Title
title: reorder
list: ~/plugins/rainlab/blog/models/post/columns.yaml
# Attribute name
nameFrom: title
# Model Class name
modelClass: rainlab\blog\Models\Post
# Toolbar widget configuration
toolbar:
# Partial for toolbar buttons
buttons: plugins/bulatov/extendsorting/controllers/posts/reorder_toolbar
В config_list нужно подсунуть свой list_toolbar
toolbar:
# Partial for toolbar buttons
buttons: plugins/bulatov/extendsorting/controllers/posts/list_toolbar
Ну и в тулбаре добавляем кнопку reorder:
<a href="<?= Backend::url('bulatov/extendsorting/posts/reorder') ?>" class="btn btn-default oc-icon-sitemap">
<?= e(trans('Reorder')) ?>
</a>
Не забудьте создать там же еще двай файла — reorder.htm и _reorder_toolbar.htm.
В первом всего одна строка - <?= $this->reorderRender()?>
Во втором нам просто нужна кнопка назад.
Теперь к скопированному бихейвору, который у меня находится в котроллерах под названием reorder2.
В нем есть метод validateModel, который проверяет, подключен ли к модели трейт Sortable.
Я его отредактировал таким образом:
protected function validateModel()
{
$model = $this->controller->reorderGetModel();
$modelTraits = class_uses($model);
$this->sortMode = 'simple';
return $model;
}
Далее самый колхоз — добавление трейта к модели Post. Сам трейт находится в vendor/October/Rain/Database/Traits. Как я уже говорил, это нельзя сделать извне, но мы можем динамически добавить методы, а их всего три. Делаем это все в том же Plugin.php, где расширяли контроллер.
Post::extend(function($model) {
if (Schema::hasColumn('rainlab_blog_posts', 'sort_order')) {
$model - > bindEvent('model.beforeCreate', function() use($model) {
$model - > sort_order = Post::max('id') + 1;
});
}
$model - > addDynamicMethod('bootSortable', function() use($model) {
$model::created(function($model) {
$sortOrderColumn = $model - > getSortOrderColumn();
print_r($sortOrderColumn);
if (is_null($model - > $sortOrderColumn)) {
$model - > setSortableOrder($model - > getKey());
}
});
$model::addGlobalScope(new SortableScope);
});
$model - > addDynamicMethod('setSortableOrder', function($itemIds, $itemOrders = null) use($model) {
if (!is_array($itemIds)) {
$itemIds = [$itemIds];
}
if ($itemOrders === null) {
$itemOrders = $itemIds;
}
if (count($itemIds) != count($itemOrders)) {
throw new Exception('Invalid setSortableOrder call - count of itemIds do not match count of itemOrders');
}
foreach($itemIds as $index => $id) {
$order = $itemOrders[$index];
$model - > newQuery() - > where($model - > getKeyName(), $id) - > update([$model - > getSortOrderColumn() => $order]);
}
});
$model - > addDynamicMethod('getSortOrderColumn', function() use($model) {
return defined('static::SORT_ORDER') ? static::SORT_ORDER : 'sort_order';
});
});
Тут важно упомянуть, что не все работает как надо, потому что при создании нового поста, значение не устанавливается равным id. Какой именно метод не работает я понятия не имею, но подозреваю, что тот, который статический. Я решил это проблему через bindevent, подключаясь к моенту создания нового поста и устанавливая значение равным последний id+1.
Еще нам нужно добавить вариант сортировки в статическое свойство модели Post, делаем это все в том же boot:
Post::$allowedSortingOptions['sort_order'] = 'reorder';
Чуть не забыл, для сортировки нам нужна колонка sort_order в модели Post, но это,наверное, единственное, что я нашел на просторах интернета, так что было легко. Описывать не буду, посмотрите видео про расширение плагинов, там куда нагляднее 🙂
Финал.
Не все работает, как надо, да и пользы от этой фичи тоже не очень то и много. Может, кое что я и забыл упомянуть, добавлю позже. Главное, чтобы эта статья помогла кому то понять, как работает октябрь и расширение плагинов. Всем удачи!