В прошлом на Хабре публиковалась статья "Orchid CMS — ещё одна CMS на Laravel", а теперь спустя два года и больше 100 релизов попробуем разобрать ошибки и проблемы которые стояли на пути разработки.
1. Позиционирование
В самом заголовке предыдущей статьи и во многих других указывалась, аббревиатура CMS, что во влажных мечтах должно было привлечь больше интереса к разработке. Одновременно с этим, пакет не предлагал ни каких готовых решений в тиражировании веб-сайтов, а только управление в администрировании.
В итоге было только хуже… Само это сочетание отталкивало опытных пользователей устанавливать, а новички ожидали увидеть привычные для себя возможности, вместо, некоторой свободы.
2. Формы
Когда речь заходит об панелях администрирования, впервую очередь имеет значение формы и как с ними работать. Для примера будем рассматривать форму построенную на blade
-шаблонах, которая отображает и позволяет редактировать данные некоторого объекта:
<form action="...">
<input type="text" name="title">
<input type="text" name="price">
<input type="submit" value="Изменить">
</form>
Менеджеры, редакторы и пользователи в целом хотят видеть как можно больше об объекте редактирования и в этом нет ни каких проблем если всю информацию можно взять из одной таблицы, но когда информация очень растянута?
Самым популярным решением, что я видел, было просто разделение формы на несколько классов и файлов с представлением. Они отображались в виде вкладок на форме, информация в которых была бы сгруппирована по связям:
<form action="..." id="main">
<input type="text" name="title">
<input type="text" name="price">
<input type="submit" value="Изменить">
</form>
<form action="..." id="editors">
<!-- Связи с редакторами -->
<input type="submit" value="Применить">
</form>
<form action="..." id="history">
<!-- История изменений -->
<input type="submit" value="Восстановить">
</form>
Именно это и было воспроизведено… Конечно с небольшими изменениями и особенностями, но смысл оставался прежним. В создании отдельного класса группирующего все обработки в виде других форм:
Накладывая на предыдущие примеры, в коде каждая форма выглядела приблизительно следующим образом:
class Example extends Form
{
public $name = 'General';
public function rules(): array
{
return [
'title' => 'required|max:160'
];
}
public function display(): View
{
return view('main');
}
public function persist(Model $model)
{
//..
return $model->save();
}
}
И формирование в контроллере всей группы:
class ExampleController extends Controller
{
public function index(Model $model)
{
$form = new FormGroup([
Example::class
// editors..
// history..
]);
return $form->render($model);
}
}
Кроме разделения самой формы и небольшой организации кода это не принесло ни каких других результатов. В итоге:
- Такой способ ни как не помогал решать дублирование нескольких элементов, например, поле "Заголовок" у объекта мог встречаться множество раз, но полной копии формы ни разу.
- При отличных от "стандартных" действий нужно было создавать отдельный метод контроллера, что напрочь портило весь смысл деления формы.
- Ни каких плюсов в автоматизации построения представления, все так же необходимо указывать вручную.
3. CRUD
Самый спорный вариант, который очень часто фигурирует в новостных лентах и блогах разработчиков. Такой подход используется почти всеми альтернативными пакетами, сделали и его. По примеру одной известной системы где все храниться в одной таблице была создана модель с динамическим JSON
полем:
В отдельном классе описывались необходимые для создания и редактирования поля, а так же базовые действия:
class Example extends Behavior
{
public $name = 'Main';
public function rules(): array
{
return [
'title' => 'required|max:160'
];
}
public function fields(): array
{
return [
Input::make('title')
->type('text')
->max(160)
->required(),
];
}
}
Благодаря такому подходу, написание простейшего кода было быстрым из-за полного отсутствия работы с представлением, но принесло не мало проблем:
- Нестандартные операции не доступны в рамках объекта.
- Динамическое представление, вроде модальных окон с загрузкой информации недоступно.
- При малейшем отклонении, например, построения графиков мы снова возвращаемся к написанию
blade
шаблонов со стилями и сценариями. - Невозможность использовать данные из отличных от базы данных источников.
- Работа только в рамках одной таблицы, это можно было исправить, но в первой и последней реализации так было.
Исправление
Вариант с CRUD был основан на полном автоматизации представления, что в итоге привело к новой реализации. Как некоторый эталон на этот раз был взят продукт Visual Studio LightSwitch, который позволял пользоваться человеку без глубоких знаний разработки.
Естественно такого эффекта нам не нужно и это даже не является целью, при этом почти все технические возможности присутствовали в Laravel или пользовательских реализаций.
Решено было сосредоточится только на одно единственном аспекте — Экраны.
Экран — это все, что пользователь видит на странице и какие действия может совершать.
Все это описывается в одном классе, он не знает откуда берутся данные, это может быть: база данных, API или любые другие внешние источники. Построение внешнего вида основано на слоях и всё, что необходимо было сделать это лишь определить какие данные будут показаны в том или ином шаблоне.
Слой же может представлять из себя некоторый макет, который может быть таблицей, строкой, графиком и т.п. При этом каждый макет может включать в себя другой макет, то есть вложенность. Например экран делится двумя колонками, в левой поля для заполнения, справа справочная таблица и график и т.д.
Для управления над данными отображаемые на экране предусмотрены команды, которые берут на себя обработку.
class ExampleScreen extends Screen
{
public $name = 'Example Screen';
public function query(Model $model): array
{
return [
'model' => $model
];
}
public function commandBar(): array
{
return [
Link::make('Веб-сайт')
->link('http://orchid.software/ru')
->icon('icon-globe-alt'),
];
}
public function layout(): array
{
return [
Layout::rows([
Input::make('model.title')
->type('text')
->max(160)
->required(),
])
];
}
}
Остановка произошла на таком варианте, так как он держит здоровый баланс между полным написанием и автоматической генерации, при этом оставаясь понятным.
Compolomus
Как то закончилось, даже не начавшись
tatu Автор
В четвёртом пункте исправлюсь и научусь правильно рассказывать истории. Если по делу, то очень не хотелось лить воды.
greabock
Да дело не в этом.
Всё нормально с объемом воды и твердых веществ — ровно столько, сколько нужно )
Не хватает какого-то заключения что-ли. Материал обрывается посреди повествования.