В прошлом на Хабре публиковалась статья "Orchid CMS — ещё одна CMS на Laravel", а теперь спустя два года и больше 100 релизов попробуем разобрать ошибки и проблемы которые стояли на пути разработки.
В самом заголовке предыдущей статьи и во многих других указывалась, аббревиатура CMS, что во влажных мечтах должно было привлечь больше интереса к разработке. Одновременно с этим, пакет не предлагал ни каких готовых решений в тиражировании веб-сайтов, а только управление в администрировании.
В итоге было только хуже… Само это сочетание отталкивало опытных пользователей устанавливать, а новички ожидали увидеть привычные для себя возможности, вместо, некоторой свободы.
Когда речь заходит об панелях администрирования, впервую очередь имеет значение формы и как с ними работать. Для примера будем рассматривать форму построенную на 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);
}
}
Кроме разделения самой формы и небольшой организации кода это не принесло ни каких других результатов. В итоге:
Самый спорный вариант, который очень часто фигурирует в новостных лентах и блогах разработчиков. Такой подход используется почти всеми альтернативными пакетами, сделали и его. По примеру одной известной системы где все храниться в одной таблице была создана модель с динамическим 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(),
])
];
}
}
Остановка произошла на таком варианте, так как он держит здоровый баланс между полным написанием и автоматической генерации, при этом оставаясь понятным.
К сожалению, не доступен сервер mySQL