Дата публикации: 12.05.2024 в 21:45

Генерация кода в Laravel на основе SQL таблицы

Иван ЛевченкоИван Левченко
2 комментария

Генерация кода в Laravel на основе SQL таблицы

Всем привет!

В этой статье расскажу про мой пакет laravel-code-builder. Можно сказать, это будет перевод Readme файла, с небольшими дополнениями от меня.

Итак, пакет позволяет на основе SQL таблицы сгенерировать такие сущности, как:

Примеры в списке сгенерированы на основе схемы SQL, полученной из следующей миграции:

Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->string('title')->default('Default');
    $table->text('content');
    $table->foreignIdFor(User::class)
        ->nullable()
        ->constrained()
        ->nullOnDelete()
        ->cascadeOnUpdate();
    $table->smallInteger('sort_number')->default(0);
    $table->boolean('is_active')->default(0);
    $table->timestamps();
    $table->softDeletes();
});

Для чего нужен пакет?

Что мы делаем, когда садимся за разработку нового типового проекта или выполнение новой, казалось бы, простой задачи на основе CRUD? Пишем миграцию, создаем модель, делаем контроллер, формируем blade шаблоны, создаем FormRequest, пишем сервисы/экшены/код на создание/редактирование/удаление и прочие вещи. А если сущностей сразу несколько? Вся эта рутина начинает утомлять и забирать слишком много времени. Пакет laravel-code-builder позволяет начать разработку с уже готовыми рабочими шаблонами кода, которые связаны между собой, и уже могут выполнять базовые CRUD функции. Если проще, данный пакет позволяет значительно сократить количество рутины при написании кода и сосредоточиться на разработке. Для этого всего лишь нужна SQL таблица.

Установка

composer require dev-lnk/laravel-code-builder --dev

Публикуем файл конфигурации:

php artisan vendor:publish --tag=laravel-code-builder

В файле конфигурации доступны 4 опции:

  1. builders - список классов для генерации кода, по умолчанию доступны все классы
  2. generation_path - путь, где будут храниться сгенерированные классы. Если не указано, будет предоставлено диалоговое окно с выбором директории генерации
  3. stub_dir - путь до шаблонов кода stubs, по умолчанию все стабы хранятся в директории пакета
  4. belongs_to - флаг указывает на то, будет ли выполнен поиск внешних ключей для формирования связи BelongsTo(по умолчанию true)

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

Базовая сигнатура команды выглядит следующим образом:

code:build {entity} {table?}

Допустим, мы хотим создать классы для базовой таблицы usersна основе сущности User. Для этого необходимо выполнить следующую команду:

php artisan code:build user

Если вы не указали таблицу, вам будет предложено диалоговое окно с выбором нужной таблицы:

 ┌ Table ───────────────────────────────────────────────────────┐
 │   ○ migrations                                             │ │
 │   ○ password_reset_tokens                                  │ │
 │   ○ products                                               │ │
 │   ○ sessions                                               │ │
 │ ● users                                                    │
 └──────────────────────────────────────────────────────────────┘

Если вы указали таблицу или её часть, диалоговое окно будет иметь следующий вид:

php artisan code:build user us
 ┌ Table ───────────────────────────────────────────────────────┐
 │ ● users                                                      │
 └──────────────────────────────────────────────────────────────┘

Если generation_path не был указан, будет предложено две опции на выбор:

 ┌ Where to generate the result? ───────────────────────────────┐
 │ ● In the project directories                                 │
 │   ○ To the generation folder: `app/Generation`
 └──────────────────────────────────────────────────────────────┘

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

app/Models/User.php was created successfully!
...
 ┌ Controller already exists, are you sure you want to replace it?
Yes
 └─────────────────────────────────────────────────────────────────┘

app/Http/Controllers/UserController.php was created successfully!
...

При варианте генерации в отдельную папку, файлы перезаписываются самостоятельно. Так будут выглядеть все сформированные файлы в директории app/Generation:

Можно сформировать конкретные сущности, например:

php artisan code:build user --table --DTO

Список всех доступных сущностей представлен в Readme пакета.

Поля отношений

Такие отношения как HasOne, HasMany, BelongsToMany невозможно определить с помощью SQL схемы, поэтому их можно указать во время выполнения команды самостоятельно:

php artisan code:build product --has-one=images --has-many=comments --belongs-to-many=properties

В итоге в модели будет сформирован следующий код:

//...
public function comments(): HasMany
{
    return $this->hasMany(Comment::class, 'product_id');
}
    
public function image(): HasOne
{
    return $this->hasOne(Image::class, 'product_id');
}
    
public function properties(): BelongsToMany
{
    return $this->belongsToMany(Property::class);
}
//...

На основе этих данных так же будут сформированы DTO, html форма, FormRequest и другие сущности с уже новыми полями.

Настройка генерации

Вы можете быть не согласны с представленной архитектурой кода, или вам нужно добавить классов в html шаблоны, или просто что-то изменить под свой проект. Для таких целей можно выполнить несколько шагов.

Во-первых, вы можете просто изменить шаблоны stubs, опубликовав их у себя и изменив в файле конфигурации настройкуstub_dir:

php artisan vendor:publish --tag=laravel-code-builder-stubs

Во-вторых, вы можете изменить код формирования классов в конкретном строителе. Например:

use DevLnk\LaravelCodeBuilder\Exceptions\NotFoundCodePathException;
use DevLnk\LaravelCodeBuilder\Services\Builders\AbstractBuilder;
use DevLnk\LaravelCodeBuilder\Services\Builders\Core\Contracts\AddActionBuilderContract;
use DevLnk\LaravelCodeBuilder\Services\StubBuilder;
use Illuminate\Contracts\Filesystem\FileNotFoundException;

class CustomAddActionBuilder extends AbstractBuilder implements AddActionBuilderContract
{
    /**
     * @throws FileNotFoundException
     * @throws NotFoundCodePathException
     */
    public function build(): void
    {
        StubBuilder::make($this->stubFile)
            ->...
        //Ваш код
    }
}

Регистрируем в провайдере в методе register() созданный класс:

$this->app->bind(
    AddActionBuilderContract::class,
    CustomAddActionBuilder::class
);

Если вам нужно полностью сформировать свою схему, то можете создать свою команду, унаследовав LaravelCodeBuildCommand и переопределив метод getCodeStructure:

class MyCodeBuildCommand extends LaravelCodeBuildCommand
{
    protected $signature = 'my-code:build {entity} {table?} {--model} {--request} {--addAction} {--editAction} {--request} {--controller} {--route} {--form} {--table} {--DTO} {--builders} {--has-many=*} {--has-one=*} {--belongs-to-many=*}';

    protected function getCodeStructure(): CodeStructure
    {
        //Your CodeStructure content, maybe from json or xml?
    }
}

Для каких проектов?

Пакет laravel-code-builder полностью поддерживает СУБД MySQL и PostgreSQL, так же был частично протестирован на SQLite. Для работы пакета необходим PHP 8.2 и старше, а также Laravel 10/11.

Итоги

Вот такой пакет получился. Делал его для себя, но уверен, что он поможет и вам ускорить разработку и сделать процесс написания кода более приятным. Вся остальная информация доступна в Readme.

Всем успехов!

Комментарии (2)

Danil Shutsky
Danil Shutsky
15.05.2024 в 20:31
Супер пакет
Анатолий
Анатолий
14.05.2024 в 04:08
Звучит бомбически 🔥 Обязательно его попробую )
ОбщайсяРазвивайсяУчисьРаботай
ОбщайсяРазвивайсяУчисьРаботай
ОбщайсяРазвивайсяУчисьРаботай
ОбщайсяРазвивайсяУчисьРаботай