Laravel Filament 3 インストール、基本、1対N、N対N 学習メモ

目次
Filament 3 Tutorial インストール、基本、1対N、N対N
前提、準備で環境を構築
docker、WSL2、laravel12、filament3.3、mysql(phpmyadmin)、
主なコマンド集
curl -s https://laravel.build/FilamentCourse | bash
sail artisan make:model Post -m
sail artisan migrate
sail artisan make:filament-resource Post
...
続きは下記URL参照
テーブル、モデル作成
sail artisan make:model Category -m
sail artisan make:model Post -m
create table
0001_01_01_000000_create_users_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
2025_03_20_081710_create_categories_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('categories');
}
};
2025_03_20_081734_create_posts_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('thumbnail')->nullable();
$table->string("title");
$table->string('color');
$table->string('slug')->unique();
$table->foreignId('category_id')->constrained()->cascadeOnDelete();
$table->text('content')->nullable();
$table->json('tags')->nullable();
$table->boolean('published')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('posts');
}
};
2025_03_24_095942_create_post_user_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('post_user', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(\App\Models\Post::class);
$table->foreignIdFor(\App\Models\User::class);
$table->integer('order')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('post_user');
}
};
Model
User.php
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function posts () {
return $this->belongsToMany(Post::class, 'post_user')->withPivot(['order'])->withTimestamps();
}
}
Category.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
//
protected $fillable = [
'name',
'slug',
];
public function posts() {
return $this->hasMany(Post::class);
}
}
Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//
protected $fillable = [
'thumbnail',
'title',
'color',
'slug',
'category_id',
'content',
'tags',
'published',
];
protected $casts = [
'tags' => 'array'
];
public function category () {
return $this->belongsTo(Category::class);
}
public function authors () {
return $this->belongsToMany(User::class, 'post_user')->withPivot(['order'])->withTimestamps();
}
}
Filament/Resources
sail artisan make:filament-resource User
sail artisan make:filament-resource Category
sail artisan make:filament-resource Post
UserResource.php
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\UserResource\Pages;
use App\Filament\Resources\UserResource\RelationManagers;
use App\Models\User;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class UserResource extends Resource
{
protected static ?string $model = User::class;
protected static ?string $navigationIcon = 'heroicon-o-users';
public static function form(Form $form): Form
{
return $form
->schema([
//
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('email')->email(),
Forms\Components\TextInput::make('password')->password(), //->visibleOn('create'), //->readonlyOn('edit'),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
//
Tables\Columns\TextColumn::make('id'),
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('email'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListUsers::route('/'),
'create' => Pages\CreateUser::route('/create'),
'edit' => Pages\EditUser::route('/{record}/edit'),
];
}
}
CategoryResource.php
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\CategoryResource\Pages;
use App\Filament\Resources\CategoryResource\RelationManagers;
use App\Models\Category;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class CategoryResource extends Resource
{
protected static ?string $model = Category::class;
protected static ?string $navigationIcon = 'heroicon-o-folder';
protected static ?string $modelLabel = 'Post Categories';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('slug')->required(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('slug'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//CategoryResource\RelationManagers\PostsRelationManager::class
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListCategories::route('/'),
'create' => Pages\CreateCategory::route('/create'),
'edit' => Pages\EditCategory::route('/{record}/edit'),
];
}
}
PostResource.php
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\PostResource\Pages;
use App\Filament\Resources\PostResource\RelationManagers;
use App\Models\Category;
use App\Models\Post;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class PostResource extends Resource
{
protected static ?string $model = Post::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('Create a Post')
->description('create posts over here.')
// ->aside()
->collapsible()
->schema([
Forms\Components\TextInput::make('title')->required(),
// Forms\Components\TextInput::make('title')->numeric()->minValue(3)->maxValue(10)->required(),
Forms\Components\TextInput::make('slug')->unique(ignoreRecord:true)->required(),
Forms\Components\Select::make('category_id')
->label('Category')
// ->multiple()
// ->options(Category::all()->pluck('name','id'))
->relationship('category','name')
// ->searchable()
->required(),
Forms\Components\ColorPicker::make('color')->required(),
Forms\Components\MarkdownEditor::make('content')->required()->columnSpanFull(),
])->columnSpan(2)->columns(2), //->columnSpanFull(),
Forms\Components\Group::make()->schema([
Forms\Components\Section::make('Image')
->collapsible()
->schema([
Forms\Components\FileUpload::make('thumbnail')->disk('public')->directory('thumbnails'),
])->columnSpan(1),
Forms\Components\Section::make('Meta')
->schema([
Forms\Components\TagsInput::make('tags')->required(),
Forms\Components\Checkbox::make('published'),
]),
Forms\Components\Section::make('Authors')
->schema([
// Forms\Components\Select::make('authors')
Forms\Components\CheckboxList::make('authors')
->label('Go Authors')
->searchable()
// ->multiple()
->relationship('authors', 'name')
]),
]),
])->columns([
'default' => 1,
'md' => 2,
'lg' => 3,
'xl' => 4,
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('id')
->sortable()
->searchable()
->toggleable(isToggledHiddenByDefault:true),
Tables\Columns\ImageColumn::make('thumbnail')
->toggleable(),
Tables\Columns\ColorColumn::make('color')
->toggleable(),
Tables\Columns\TextColumn::make('title')
->sortable()
->searchable()
->toggleable(),
Tables\Columns\TextColumn::make('slug')
->sortable()
->searchable()
->toggleable(),
Tables\Columns\TextColumn::make('category.name')
->sortable()
->searchable()
->toggleable(),
Tables\Columns\TextColumn::make('tags')
->toggleable(),
Tables\Columns\CheckboxColumn::make('published')
->toggleable(),
Tables\Columns\TextColumn::make('created_at')
->label('Published on')
->date()
->sortable()
->searchable()
->toggleable(),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListPosts::route('/'),
'create' => Pages\CreatePost::route('/create'),
'edit' => Pages\EditPost::route('/{record}/edit'),
];
}
}

画像が表示されない場合、リンクを作成する。
sail artisan storage:link



1対N
sail artisan make:filament-relation-manager CategoryResource posts title

app/Filament/Resources/CategoryResource/RelationManagers/PostsRelationManager.php
<?php
namespace App\Filament\Resources\CategoryResource\RelationManagers;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class PostsRelationManager extends RelationManager
{
protected static string $relationship = 'posts';
public function form(Form $form): Form
{
return $form
// ->schema([
// Forms\Components\TextInput::make('title')
// ->required()
// ->maxLength(255),
// ]);
->schema([
Forms\Components\Section::make('Create a Post')
->description('create posts over here.')
// ->aside()
->collapsible()
->schema([
Forms\Components\TextInput::make('title')->required(),
// Forms\Components\TextInput::make('title')->numeric()->minValue(3)->maxValue(10)->required(),
Forms\Components\TextInput::make('slug')->unique(ignoreRecord:true)->required(),
// Forms\Components\Select::make('category_id')
// ->label('Category')
// // ->multiple()
// // ->options(Category::all()->pluck('name','id'))
// ->relationship('category','name')
// // ->searchable()
// ->required(),
Forms\Components\ColorPicker::make('color')->required(),
Forms\Components\MarkdownEditor::make('content')->required()->columnSpanFull(),
])->columnSpan(2)->columns(2), //->columnSpanFull(),
Forms\Components\Group::make()->schema([
Forms\Components\Section::make('Image')
->collapsible()
->schema([
Forms\Components\FileUpload::make('thumbnail')->disk('public')->directory('thumbnails'),
])->columnSpan(1),
Forms\Components\Section::make('Meta')
->schema([
Forms\Components\TagsInput::make('tags')->required(),
Forms\Components\Checkbox::make('published'),
])->columnSpan(1),
]),
])->columns([
'default' => 1,
'md' => 2,
'lg' => 3,
'xl' => 4,
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('title')
->columns([
Tables\Columns\TextColumn::make('title'),
Tables\Columns\TextColumn::make('slug'),
Tables\Columns\CheckboxColumn::make('published')
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make(),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
}

N対N モデルリレーションシップテーブル作成
sail artisan make:migration create_post_user_table

create table
2025_03_24_095942_create_post_user_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('post_user', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(\App\Models\Post::class);
$table->foreignIdFor(\App\Models\User::class);
$table->integer('order')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('post_user');
}
};
sail artisan migrate

sail artisan make:filament-relation-manager PostResource authors name

※ここで「authors」のところを誤って「authos」で進め、「AuthosRelationManager.php」を削除して「AuthorsRelationManager.php」再作成ができたが、sail up -d で起動するが、https://localhostが開けない現象が発生。
下記のキャッシュクリアを実行したところ正常に動作できた。
Laravel キャッシュクリア系コマンド
sail artisan cache:clear
sail artisan config:clear
sail artisan route:clear
sail artisan view:clear
https://qiita.com/Ping/items/10ada8d069e13d729701
テーブルに項目追加
2025_03_24_095942_create_post_user_table.php
に、下記の項目を追加して再度マイグレーションする。
public function up(): void
{
Schema::create('post_user', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(\App\Models\Post::class);
$table->foreignIdFor(\App\Models\User::class);
$table->integer('order')->default(0);
$table->timestamps();
});
}
sail artisan migrate:refresh --step=1
–stepオプションを指定した場合、指定した数だけロールバックが実行する。
N対N
sail artisan make:filament-relation-manager PostResource authors name
App\Filament\Resources\PostResource\RelationManagers/AuthorsRelationManager.php
<?php
namespace App\Filament\Resources\PostResource\RelationManagers;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class AuthorsRelationManager extends RelationManager
{
protected static string $relationship = 'authors';
public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('email')->email(),
Forms\Components\TextInput::make('password')->password(), //->visibleOn('create'), //->readonlyOn('edit'),
Forms\Components\TextInput::make('order')->numeric()->required(),
]);
}
public function table(Table $table): Table
{
return $table
->recordTitleAttribute('name')
->columns([
Tables\Columns\TextColumn::make('name')->sortable(),
Tables\Columns\TextColumn::make('email'),
Tables\Columns\TextColumn::make('order')->sortable(),
])
->filters([
//
])
->headerActions([
Tables\Actions\CreateAction::make(),
Tables\Actions\AttachAction::make(),
// ->form(fn (AttachAction $action): array => [
// $action->getRecordSelect(),
// Forms\Components\TextInput::make('order')->numeric()->required(),
// ]),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DetachAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
}
CategoryResource.php(修正)
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\CategoryResource\Pages;
use App\Filament\Resources\CategoryResource\RelationManagers;
use App\Models\Category;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class CategoryResource extends Resource
{
protected static ?string $model = Category::class;
protected static ?string $navigationIcon = 'heroicon-o-folder';
protected static ?string $modelLabel = 'Post Categories';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('slug')->required(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('slug'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
CategoryResource\RelationManagers\PostsRelationManager::class
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListCategories::route('/'),
'create' => Pages\CreateCategory::route('/create'),
'edit' => Pages\EditCategory::route('/{record}/edit'),
];
}
}
-
前の記事
windows docker WSL2 sail laravel filament phpadmin アプリ構築 メモ 2025.03.18
-
次の記事
EC CUBE 4 WSL2 docker ubuntu 2025.03.25