Всем привет.

Расскажу о CRUD генераторе для Laravel. Для студенческой работы мне надо было сделать много CRUD формочек, последний раз когда у меня была такая задача (в 2017), я использовал Yii2 и Gii.

В этот раз я решил сделать решение на базе Laravel, но своего встроенного генератора для GUI в Ларавель не завезли, пришлось искать «стороннее» решение.

Гугл выдал четыре варианта, первым шёл CRUDBooster, с ним я бился пару часов, ни чего не получилось, код генериться, GUI нет.

Следующим был Craftable, с этим товарищем тоже пришлось повозиться, даже просто что бы его установить, но какой то GUI он генерил и я решил на этом варианте остановиться.

Ниже я расскажу о работе с Craftable и покажу пару скриншотов GUI.

Начало работы с Craftable


После установки обязательно выполните команды (в корневой директории проекта):

npm install && npm run dev

И после каждого изменения ресурсов в части JS («resources/js») надо выполнять команду:

npm run dev

При выполнении этой команды создаются новые бандлы для JS и CSS («public/js/admin.js» и «public/css/admin.css»), файлы бандлов лучше сразу добавить в ".gitignore", поскольку эти файлы автоматически генерируются, и нет смысла вести историю их изменений.

Вход в админку


Можно пользоваться встроенной админской учёткой, имя — «administrator@brackets.sk», пароль смотрим в скрипте миграции («protected $password = 'PjucggYEOK';») «database/migrations/2020_03_24_214249_fill_default_admin_user_and_permissions.php», часть имени файла «2020_03_24_214249_» у вас будет другой.

Можно зарегистрировать нового пользователя используя регистрацию из коробки Ларавеля.
После этого заходим в админку "/admin", и в меню выбираем «Manage access» ("/admin/admin-users"), и разрешаем доступ в админку для новой учётки.

Как работает генерация


Создаём таблицу базы данных, допустим «posts» и выполняем команду:

php artisan admin:generate posts

Craftable сгенерирует для нас:

  • Модель
  • Контроллер
  • Классы FormRequest для всех действий (action) где требуется обработка данных формы
  • Таблицу с разрешениями на действия и соответствующие проверки в контроллере
  • Шаблоны представлений для всех CRUD действий (/resources/views/admin/%имя-вашей-таблицы%/)
  • Переводы для всех подписей
  • Классы для «посева» тестовых записей базы данных

Так же в методе контроллера index(), будет добавлен код для фильтрации значений базы данных, это очень удобно — если не надо, то выкинуть не сложно, если надо, то уже есть место куда писать логику фильтрации.

Не смотря на то что Craftable генерирует записи в таблицу Разрешений («permissions»), в админке нет странички для управления этими разрешениями. Без этого управлять разрешениями ролей не удобно, но конечно можно вообще не генерировать разрешения.
После генерации модели, выполняем:

npm run dev

Автоматически эта команда не выполняется.

Подробней о генерации в документации.

Представление для списка




image

Представление для создания записи




Представление для редактирования




Типы данных


Таблицу для генерации CRUD удобно делать с помощью миграции, потому что Craftable генерирует представления и правила валидации параметров формы исходя из типа колонки, при этом такие колонки как decimal не поддерживаются, и будут проигнорированы.

То есть:

  1. делаем миграцию, для создания колонки используем «integer()»,
  2. генерируем модель,
  3. откатываем миграцию,
  4. в миграции в описании таблицы заменяем «integer()» на «decimal()»,
  5. накатываем миграцию.

В итоге имеем и сгенерированный CRUD, и таблицу с нужными типами данных.

Типы которые поддерживаются:

  • string
  • text
  • date
  • datetime
  • time
  • boolean

Переводы


По умолчанию доступен только один язык, и в админке ни где нет возможности добавить ещё один язык для переводов. Что бы добавить ещё один язык, надо открыть файл "/config/translatable.php" и добавить желаемый язык.

Язык для интерфейса выбирается в профиле пользователя.

Craftable генерирует переводы для всех подписей и сообщений, это с одной стороны круто, а с другой стороны не очень.

Первый раз у меня в представлениях появились подстановки для переводов:

trans('admin.wood-specie.actions.index')

и сами переводы:


    'wood-specie' => [
        'title' => 'Wood Specie',

        'actions' => [
            'index' => 'Wood Specie',
            'create' => 'New Wood Specie',
            'edit' => 'Edit :name',
        ],

        'columns' => [
            'id' => 'ID',
            'title' => 'Title',
            'calculation_period' => 'Calculation period',
            'timber_harvesting_age' => 'Timber harvesting age',
            'main_harvesting_age' => 'Main harvesting age',

        ],
    ],

И это было очень круто!

Но для остальных страничек в представлениях генерировались подстановки, но сами переводы не генерировались (возможно потому что когда я генерировал первую модель, был задан только один язык, а когда генерировал остальные страницы, языков уже было два).

Поэтому пришлось открыть Translations (/admin/translations) и делать переводы в ручную, если кодов для перевода нет, то надо нажать кнопку «Re-scan translations» или выполнить команду:

php artisan admin-translations:scan-and-save

Когда переводы генерируются в файл их можно сохранить в репозитории, когда переводы создаются через «Re-scan translations», то они записываются в базу данных и накатывать их (при разворачивании нового «сервера») придётся SQL скриптом (или написать скрипт для генерации файла с переводами из записей базы данных).

Подробней о локализации в документации.

Использование Vue


Craftable генерирует JS скрипты для клиент сайд рендеринга с помощью Vue, это:

  1. index.js
  2. Listing.js
  3. Form.js

Если вам не нужны представления для списка (Listing.js) или для одиночной записи (Form.js), то вы можете удалить соответствующий файл и внести изменения в index.js.

Поскольку Vue получает наши шаблоны представлений как части компонента, то ни каких тегов
<script>
в представлении быть не должно и весь JS код должен быть в соответствующем файле.

Если мы добавляем какое то свойство в нашу модель, и не делаем принудительную генерацию:

php artisan admin:generate --force posts

то нам необходимо в ручную добавить это новое свойство в Form.js.

Использование multiselect


В документации Craftable есть пример редактирования свойства, которое является внешним ключом, то есть в таблице храниться внешний ключ, но для редактирования мы используем не значение идентификатора, для редактирования мы используем выпадающий список со значениями из родительской таблицы.

У меня этот пример заработал не сразу.

Допустим у нас есть свойство «bonitet», значения для выпадающего списка мы передаём через атрибут компонента «bonitets», что бы задать список значений мы должны писать:

        <multiselect
            v-model="form.bonitet"
            :options="$attrs.bonitets"
            :multiple="false"
            :searchable="false"
            :allow-empty="false"
            track-by="id"
            label="title"
            tag-placeholder="{{ __('Select Bonitet') }}"
            placeholder="{{ __('Bonitet') }}">
        </multiselect>

В примере из документации не используется $attrs, но у меня без этого компонент имел пустой список.

Если мы открываем страницу редактирования существующей записи, то для того что бы в выпадающем списке было выбрано текущее значение, то нам надо задать это значение на стороне сервера (PHP):


    public function edit(ForestResource $forestResource)
    {
        $this->authorize('admin.forest-resource.edit', $forestResource);

        $bonitet = $forestResource->bonitet()->get()[0];
        $forestResource->bonitet = $bonitet;

        return view('admin.forest-resource.edit', [
            'forestResource' => $forestResource,
            'specieTitle' => $forestResource->woodSpecie()->get()[0]->title,
            'timberClassTitle' => $forestResource->timberClass()->get()[0]->title,
            'bonitets' => Bonitet::all(),
        ]);
    }

Ключевые строки это:


        $bonitet = $forestResource->bonitet()->get()[0];
        $forestResource->bonitet = $bonitet;

Конечно в модели у вас должно быть определено отношение bonitet():


    public function bonitet()
    {
        return $this->belongsTo(Bonitet::class);
    }

Кастомизация


Представления


Часть представлений, например меню админки, будет расположено в ресурсах «resources/views/admin/layout/sidebar.blade.php», другие части представления будут браться из исходников собственно Craftable — «vendor/brackets/admin-ui/resources/views/admin/partials/header.blade.php». Соответственно если мы хотим переделать эти части оформления, то мы копируем шаблоны представлений из исходников Craftable и меняем «ссылки», было:


@extends('brackets/admin-ui::admin.layout.default')

стало:

@extends('admin.layout.default')

Роуты


Все роуты которые добавляет Craftable будут требовать проверку на роль 'admin', будут иметь префикс «admin/», и неймспейс 'Admin':


/* Auto-generated admin routes */
Route::middleware(['auth:' . config('admin-auth.defaults.guard'), 'admin'])->group(static function () {
    Route::prefix('admin')->namespace('Admin')->name('admin/')->group(static function() {
        Route::prefix('cutting-areas')->name('cutting-areas/')->group(static function() {
            Route::get('/','CuttingAreaController@index')->name('index');
        });
    });
});

У Craftable нет настройки для отключения генерации этих проверок, поэтому если вам это не требуется, то вы удаляете это в ручную.

Заключение


Работать с Craftable мне было удобно.

Репозиторий с примерами использования Craftable