見出し画像

Laravel11 - ibex/crud-generatorが使えるのかって話

laravelのCRUD generatorはいくつかの種類があるが、最近リリースされたもので比較的マトモなgeneratorがこちらだと思う。ただし、bladeがメイン。cssは基本的にはbreezeのtailwind対応であるが、bootstrapにも対応する

必要条件

インストール

composerで

composer require ibex/crud-generator --dev

configのpublish

php artisan vendor:publish --tag=crud

そうすると config/crud.php が生成される

chatgptによるconfigの翻訳

return [
    /*
    |--------------------------------------------------------------------------
    | スタブのパス (Stubs Path)
    |--------------------------------------------------------------------------
    |
    | CRUDを生成するためのスタブのパスディレクトリです。
    | モデル、コントローラー、ビューのスタブファイルをカスタマイズしたい場合は、
    | ここでスタブのパスを設定します。
    | あるいは、CrudGeneratorのデフォルトスタブを使用することもできます。
    |
    | 例: 'stub_path' => resource_path('stubs/')
    | デフォルト: "default"
    | 使用されるファイル:
    |       Controller.stub
    |       Model.stub
    |       Request.stub
    |       views/
    |           bootstrap/
    |               create.stub
    |               edit.stub
    |               form.stub
    |               form-field.stub
    |               index.stub
    |               show.stub
    |               view-field.stub
    */

    'stub_path' => 'default',

    /*
    |--------------------------------------------------------------------------
    | アプリケーションレイアウト (Application Layout)
    |--------------------------------------------------------------------------
    |
    | この値は、CRUDを作成するときに使用されるアプリケーションのレイアウト名です。
    | CRUDのビューを作成するときに使用されます。デフォルトでは「layouts.app」です。
    |
    */

    'layout' => 'layouts.app',

    'model' => [
        'namespace' => 'App\Models',

        /*
         * モデルやビューに$fillableとして含めないカラム
         */
        'unwantedColumns' => [
            'id',
            'uuid',
            'ulid',
            'password',
            'email_verified_at',
            'remember_token',
            'created_at',
            'updated_at',
            'deleted_at',
        ],
    ],

    'controller' => [
        'namespace' => 'App\Http\Controllers',
        'apiNamespace' => 'App\Http\Controllers\Api',
    ],

    'resources' => [
        'namespace' => 'App\Http\Resources',
    ],

    'livewire' => [
        'namespace' => 'App\Livewire',
    ],

    'request' => [
        'namespace' => 'App\Http\Requests',
    ],
];

使い方

migration

先にtableを作る必要がある。ここではpostsテーブルを作成

% ./vendor/bin/sail artisan make:migration create_posts_table


   INFO  Migration [database/migrations/2024_10_20_014317_create_posts_table.php] created successfully.

内容を以下のようにしてみる

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained();
            $table->string('title');
            $table->text('content');
            $table->timestamps();
        });

DB作る

% ./vendor/bin/sail artisan migrate:fresh --seed

  Dropping all tables .................................................................................. 135.04ms DONE

   INFO  Preparing database.

  Creating migration table .............................................................................. 31.39ms DONE

   INFO  Running migrations.

  0001_01_01_000000_create_users_table ................................................................. 206.80ms DONE
  0001_01_01_000001_create_cache_table .................................................................. 55.88ms DONE
  0001_01_01_000002_create_jobs_table .................................................................. 136.24ms DONE
  2024_10_20_014317_create_posts_table .................................................................. 94.85ms DONE


   INFO  Seeding database.

実行

% ./vendor/bin/sail artisan make:crud posts


 ┌ Which stack would you like to install? ──────────────────────┐
 │ › ● Blade with Bootstrap css                                 │
 │   ○ Blade with Tailwind css                                  │
 │   ○ Livewire with Tailwind css                               │
 │   ○ API only                                                 │
 └──────────────────────────────────────────────────────────────┘

このようにpostsの後に引数を与えないとインタラクティブなモードになる。ここではBlade with Tailwind cssにしてみよう。

そうすると

% ./vendor/bin/sail artisan make:crud posts


 ┌ Which stack would you like to install? ──────────────────────┐
 │ Blade with Tailwind css                                      │
 └──────────────────────────────────────────────────────────────┘

Running Crud Generator ...
Creating Controller ...
Creating Model ...
Creating Request Class ...
Creating Views ...
Creating Layout ...
./composer.json has been updated
Running composer update laravel/breeze
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking laravel/breeze (v2.2.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Downloading laravel/breeze (v2.2.2)
  - Installing laravel/breeze (v2.2.2): Extracting archive
Generating optimized autoload files

そして

Please add route below: i:e; web.php or api.php

Route::resource('posts', PostController::class);

Created Successfully.

となる。この事からわかるのはbreezeは自動的にinstallされるってのと、routeは手動で追加してくださいねということ。

routeの追加

ってわけで追加していくぞっと。

routes/web.php 

<?php

use App\Http\Controllers\ProfileController;
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');

    // Added
    Route::resource('posts', PostController::class);
});

require __DIR__.'/auth.php';

そして /posts にアクセスすると

このようになる。これはviteが初回のrun buildでtailwind cssのコンパイル要素を決定しているため、あとから追加されたviewには対応していないのでこのようなことになっている。これはnpm run buildをしなおすか、npm run devモードで起動すれば対応できるが、ここではbuildをしなおしてみよう。

% ./vendor/bin/sail npm run build

> build
> vite build

vite v5.4.9 building for production...
✓ 54 modules transformed.
public/build/manifest.json             0.27 kB │ gzip:  0.15 kB
public/build/assets/app-Dy842Nb6.css  36.02 kB │ gzip:  6.89 kB
public/build/assets/app-BjCBnTiP.js   79.60 kB │ gzip: 29.66 kB
✓ built in 1.82s

するとcssが正しく再生成されるはずだ

Add newボタンが出てきた

リジェネレーションされたことにより、このようにAdd newが現れた

create画面は


このように流石にUser IDなどは自分で加工しないとうまいこと動かないのが理解できるだろう

修正工程

以上を踏まえて、リレーションを作った場合まともに動作するようには加工が必要である。ちなみにインデントにふんだんにタブコードが入ってるので一括でスペースに置換しちゃってもいいかも。

、で、formの部分は

resources/views/post/form.blade.php に共通項目として配置され、createとeditから使われるようになっているようだ。ここでuser_idの項目をバッサり切る

<div class="space-y-6">

    <!--
    <div>
        <x-input-label for="user_id" :value="__('User Id')"/>
        <x-text-input id="user_id" name="user_id" type="text" class="mt-1 block w-full" :value="old('user_id', $post?->user_id)" autocomplete="user_id" placeholder="User Id"/>
        <x-input-error class="mt-2" :messages="$errors->get('user_id')"/>
    </div>
    -->
    <div>
        <x-input-label for="title" :value="__('Title')"/>
        <x-text-input id="title" name="title" type="text" class="mt-1 block w-full" :value="old('title', $post?->title)" autocomplete="title" placeholder="Title"/>
        <x-input-error class="mt-2" :messages="$errors->get('title')"/>
    </div>
    <div>
        <x-input-label for="content" :value="__('Content')"/>
        <x-text-input id="content" name="content" type="text" class="mt-1 block w-full" :value="old('content', $post?->content)" autocomplete="content" placeholder="Content"/>
        <x-input-error class="mt-2" :messages="$errors->get('content')"/>
    </div>

    <div class="flex items-center gap-4">
        <x-primary-button>Submit</x-primary-button>
    </div>
</div>

リクエストを修正

app/Http/Requests/PostRequest.php 

    public function rules(): array
    {
        return [
            // 'user_id' => 'required',
            'title' => 'required|string',
            'content' => 'required|string',
        ];
    }


storeの修正だが元がこうなってる

    public function store(PostRequest $request): RedirectResponse
    {
        Post::create($request->validated());

        return Redirect::route('posts.index')
            ->with('success', 'Post created successfully.');
    }

ので面倒なので、こうしちゃった。

    public function store(PostRequest $request): RedirectResponse
    {
        // Post::create($request->validated());
        $request->user()->posts()->create($request->validated());

        return Redirect::route('posts.index')
            ->with('success', 'Post created successfully.');
    }


でモデルを見るとPostにはbelongsToが入ってるんだけどUserの方にはhasManyが入ってないっていうね。この辺を相互に追加する方法ってあるのかな。

app/Models/User.php 

    public function posts()
    {
        return $this->hasMany(\App\Models\Post::class);
    }

とした。結果は

とまあこうなる。

viewの修正

resources/views/post/index.blade.php 

<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">{{ $post->user_id }}</td>

ここを

<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
   {{ $post->user->name }}
</td> 

とか

Editの修正

うまいことまとまってるから修正の必要はない。

総評

結局、コードの修正量が多いため、何らかのデモの検証とかに使うにはいいけど実際のproductionレベルでこれが使えるかは不明。あとstubを弄って指定するともうすこし自分のいいようにカスタムできる可能性は残されてはいる。

いいなと思ったら応援しよう!