Laravel11 - ibex/crud-generatorが使えるのかって話
laravelのCRUD generatorはいくつかの種類があるが、最近リリースされたもので比較的マトモなgeneratorがこちらだと思う。ただし、bladeがメイン。cssは基本的にはbreezeのtailwind対応であるが、bootstrapにも対応する
必要条件
php: ^8.2 (8.2以上)
laravel/framework: ^10.30|^11.0 (10.30以降および11)
インストール
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が現れた
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を弄って指定するともうすこし自分のいいようにカスタムできる可能性は残されてはいる。