Laravelを利用して簡単にダッシュボードやアプリケーションを構築したいと思ったことはありませんか?そんな人にお勧めなのがFilamentです。LaravelのでAdmin Panel(管理画面)を持つアプリケーションをこれから構築する予定の人なら一度はどのようなものか体験してほしいパッケージです。

本文書ではFilamentがどういうものなのか理解してもらうためデフォルトのUserモデルを利用して動作確認を行っています。

Filamentとは

FilamentはTALL(Tailwind CSS, Alpine.js, Laravel, Livewire) Stackで構築されたパッケージで認証機能を持ち、ダッシュボードからフォーム、テーブル、通知などアプリケーションに必須な機能を備えています。細かなカスタマイズを行えるだけではなく100を超えるプラグインを利用して機能の拡張を行うことができます。簡単なアプリであればTALLを構成する技術の理解がなくても数分で作成することができます。活発に開発が行われており現在はバージョン3です。

Filamentのドキュメントを見ると”Panel Builder”, “Form Builder”, “Table Builder”, “Notifications”, “Actions”, “Infolist Builder”, “Widgets”に分かれています。それぞれ独立した機能として個別にインストールを行うことができますがAdmin Panel(管理画面)に関連するPanel Builderをインストールするとそのほかの機能を利用するためすべてのパッケージがインストールされます。FilamentをAdmin Panel(ダッシュボード)として利用するだけではなく、”Form Builder”, “Table Builder”など”Panel Builder”以外の機能をLivewireと一緒に利用することでLaravelのアプリケーション開発を効率化することができます。

環境の構築

プロジェクトの作成

Laravel Filamentの動作確認を行うためにLaravelプロジェクトの作成を行います。


 % laravel new laravel_filament

データベースの作成

本文書ではデータベースにSQLiteを利用します。.envファイルのDB_CONNECTIONの値をデフォルトのmysqlからsqliteに変更してそのほかのDB_の付いた環境変数を削除します。

php artisan migrateコマンドを実行してテーブルを作成します。SQLiteはファイルベースのデータベースなのでファイルが必要ですが作成していない場合はphp artisan migrateコマンドを実行すると作成するか聞かれるので”Yes”を選択してください。

Laravel Filamentの初期設定

Filamentパッケージのインストールを行います。


 % composer require filament/filament:"^3.2" -W

Filamentをインストール後はPanel Builderのインストールを行います。名前にPanelが入っている通りAdmin Panelを作成するために利用する機能です。

Panel Builderのインストールを行うとIDを聞かれるのでadminに設定しておきます。


 % php artisan filament:install --panels

 ┌ What is the ID? ─────────────────────────────────────────────┐
 │ admin                                                        │
 └──────────────────────────────────────────────────────────────┘

php artisan serveコマンドを実行してブラウザから/adminにアクセスすると/admin/loginにリダイレクトされてログイン画面が表示されます。

ログイン画面の表示
ログイン画面の表示

Admin Panelにアクセスするためにはユーザの作成が必要で、ユーザの作成はコマンドで行うことができます。


 % php artisan make:filament-user

 ┌ Name ────────────────────────────────────────────────────────┐
 │ John Doe                                                     │
 └──────────────────────────────────────────────────────────────┘

 ┌ Email address ───────────────────────────────────────────────┐
 │ john@example.com                                             │
 └──────────────────────────────────────────────────────────────┘

 ┌ Password ────────────────────────────────────────────────────┐
 │ ••••••••                                                     │
 └──────────────────────────────────────────────────────────────┘
INFO  Success! john@example.com may now log in at http://laravel_filament.test/admin/login.

作成したユーザでログイン画面からログインするとAdmin PanelのDashboardページが表示されます。

ダッシュボード画面
ダッシュボード画面

右上のボタンをクリックするとダークモードに変更することもできます。

右上をクリックしたドロップダウンメニューでダークモード
右上をクリックしたドロップダウンメニューでダークモード

さらにResponsive Designにも対応しているのでブラウザの幅を狭くすると表示デザインがかわります。

Responsive Designに対応
Responsive Designに対応

文字や背景の色も簡単に変更することができます。詳細はhttps://filamentphp.com/docs/3.x/panels/themesを確認でき、変更はFilamentのServiceProviderファイルであるAdminPanelProvider.phpファイルで変更可能です。

Resource

Resourceの作成

既存のUserモデルをAdmin Panelに表示させるためにresourcesを作成する必要があります。resourcesはUserモデルをCRUD(Create, Read , Update, Delete)するために必要なファイルです。作成することでAdmin Panel上にユーザメニューやユーザ一覧を表示できるようになります。


 % php artisan make:filament-resource User --generate

   INFO  Filament resource [app/Filament/Resources/UserResource.php] created successfully.  

コマンドを実行後にPanelにアクセスを行うとサイドバーにUsersが追加されクリックするとユーザ一覧のテーブルが表示されます。

ユーザ一覧の表示
ユーザ一覧の表示

“New user”ボタンをクリックするとUserモデルのファイルを元にフォームも作成されます。

ユーザの作成画面
ユーザの作成画面

ユーザを作成することも可能です。

追加したユーザの確認
追加したユーザの確認

デフォルトにあるUsersモデルを利用してコマンド一つでResourceを作成すればUser情報を管理するためのアプリケーションの作成は完了です。

Resourceファイルの確認

簡単なカスタマイズ

“php artisan make:filament-resource User –generate”を実行するとapp¥Filament¥Resourcesディレクトリの下にUserResource.phpファイルが作成されます。このファイルによってフォームの設定やテーブルの列についての設定が行われています。

UserResource.phpファイルの中にはform, table, getRelations, getPages関数が存在しますがテーブルに関する設定はtable関数で行われています。UserResource.phpファイルでさまざまなカスタマイズを行うことができます。


public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name')
                ->searchable(),
            Tables\Columns\TextColumn::make('email')
                ->searchable(),
            Tables\Columns\TextColumn::make('email_verified_at')
                ->dateTime()
                ->sortable(),
            Tables\Columns\TextColumn::make('created_at')
                ->dateTime()
                ->sortable()
                ->toggleable(isToggledHiddenByDefault: true),
            Tables\Columns\TextColumn::make('updated_at')
                ->dateTime()
                ->sortable()
                ->toggleable(isToggledHiddenByDefault: true),
        ])
        ->filters([
            //
        ])
        ->actions([
            Tables\Actions\EditAction::make(),
        ])
        ->bulkActions([
            Tables\Actions\BulkActionGroup::make([
                Tables\Actions\DeleteBulkAction::make(),
            ]),
        ]);
}     

例えばユーザ一覧のテーブルでnameとemailのみ表示させたい場合は下記のように書き換えることで実現できます。


public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name')
                ->searchable(),
            Tables\Columns\TextColumn::make('email')
                ->searchable(),
            // Tables\Columns\TextColumn::make('email_verified_at')
            //     ->dateTime()
            //     ->sortable(),
            // Tables\Columns\TextColumn::make('created_at')
            //     ->dateTime()
            //     ->sortable()
            //     ->toggleable(isToggledHiddenByDefault: true),
            // Tables\Columns\TextColumn::make('updated_at')
            //     ->dateTime()
            //     ->sortable()
            //     ->toggleable(isToggledHiddenByDefault: true),
        ])
        ->filters([
            //
        ])
        ->actions([
            Tables\Actions\EditAction::make(),
        ])
        ->bulkActions([
            Tables\Actions\BulkActionGroup::make([
                Tables\Actions\DeleteBulkAction::make(),
            ]),
        ]);
}    

ブラウザで確認するとName, Emailのみ表示されるようになります。

NameとEmailのみ表示
NameとEmailのみ表示

新しいユーザをAdmin Panelから作成させないようにするためにはUserResource/Pages/ListUsers.phpファイルのgetHeaderActions関数を削除することで”New user”ボタンが表示されなくなります。


namespace App\Filament\Resources\UserResource\Pages;

use App\Filament\Resources\UserResource;
// use Filament\Actions;
use Filament\Resources\Pages\ListRecords;

class ListUsers extends ListRecords
{
    protected static string $resource = UserResource::class;

    // protected function getHeaderActions(): array
    // {
    //     return [
    //         Actions\CreateAction::make(),
    //     ];
    // }
}
   
"New User"ボタンを非表示
“New User”ボタンを非表示

“Edit”のリンクをクリックするとName, Email, Email verified at, Passwordのinput要素が表示されますがNameのみ更新可能としたい場合はUserResource.phpのform関数でForms\Components\TextInput::make(‘name’)のみ残します。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('name')
                ->required(),
            // Forms\Components\TextInput::make('email')
            //     ->email()
            //     ->required(),
            // Forms\Components\DateTimePicker::make('email_verified_at'),
            // Forms\Components\TextInput::make('password')
            //     ->password()
            //     ->required(),
        ]);
}  

更新画面ではNameのinput要素のみ表示されるようになります。

更新画面でNameのinput要素のみ表示
更新画面でNameのinput要素のみ表示

自動ページリフレッシュ

app¥Filament以下のリソースファイルを更新後毎回ブラウザの再読み込みを手動で行うのは開発効率が悪いので自動でページのリフレッシュが行えるように設定を行います。

app¥Filamentディレクトリ以下のファイルを更新すると自動でページのリフレッシュが行われるようにvite.config.jsファイルの更新を行います。


import { defineConfig } from "vite";
import laravel, { refreshPaths } from "laravel-vite-plugin";

export default defineConfig({
    plugins: [
        laravel({
            input: ["resources/css/app.css", "resources/js/app.js"],
            refresh: [...refreshPaths, "app/Livewire/**", "app/Filament/**"],
        }),
    ],
});

これだけの設定では自動でページリフレッシュは行われません。npm installコマンドを実行してJavaScriptライブラリのダウンロードを行い、npm run devコマンドでビルドを行います。ビルドを行った後に作成されるapp.jsファイルが読み込まれるようにAdminPanelProvier.phpファイルにregister関数を追加します。


public function register():void
{
    parent::register();
    FilamentView::registerRenderHook('panels::body.end',fn():string => Blade::render("@vite('resources/js/app.js')"));
}

register関数の中ではregisterRenderHookを利用してAdmin Panelsが利用するLayoutファイルのbodyの閉じタグの前にresource/js/app.jsファイルを読み込んでいます。

ブラウザ上でページのソースを確認するとbodyの閉じタグの前に下記のコードが追加されます。


<script type="module" src="http://127.0.0.1:5173/@vite/client" data-navigate-track="reload"></script><script type="module" src="http://127.0.0.1:5173/resources/js/app.js" data-navigate-track="reload"></script>

日本語化設定

Admin Panelsの日本語化設定

Admin Panelsの画面はすべて英語で表示されていましたがapp.phpファイルでlocaleの値をデフォルトの’en’から’ja’に変更することで日本語化することができます。


    'locale' => 'ja',

設定後ダッシュボードのページにアクセスするとメニューもDashboardからダッシュボードに変わっていることが確認できます。

日本語化設定の反映確認
日本語化設定の反映確認

Userページにアクセスするとボタンなども日本語化されていることが確認できます。

Userページでの日本語の確認
Userページでの日本語の確認

日本語化された内容を変更したい場合には言語ファイルを利用して変更することができます。


 % php artisan vendor:publish --tag=filament-panels-translations

   INFO  Publishing [filament-panels-translations] assets.  

  Copying directory [vendor/filament/filament/resources/lang] to [lang/vendor/filament-panels] .. DONE

lang¥vendor¥filament-panelsディレクトリが作成され、その下にjaディレクトリが作成されるのでその下にファイルの内容を更新することで表示されている日本語を変更することができます。

モデル名の日本語化設定

Admin PanelsのサイドバーのメニューではDashboardはダッシュボードに変わりましたがUserは英語のままです。UserページのタイトルもUserのままです。Userをユーザに変更する方法を確認します。

FilamentのUserResource.phpファイルで$modelLabelの設定を行います。


class UserResource extends Resource
{
    protected static ?string $model = User::class;

    protected static ?string $modelLabel = 'ユーザ';
//略

設定後Userページを確認するとUserからユーザに変更になっています。

Userの日本語化の確認
Userの日本語化の確認

テーブルの列名の日本語化

Userページの中で列名のNameとEmailが英語で表示されています。列の名前によっては日本語に変更する必要はありませんが日本語にしたい場合にはUserResource.phpファイルで行います。labelメソッドを追加して引数に対応する日本語を設定します。


public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('name')
                ->label('名前')
                ->searchable(),
            Tables\Columns\TextColumn::make('email')
                ->label('メールアドレス')
                ->searchable(),

設定後はUserページ全体が日本語化されていることが確認できます。

Userページ全体の日本語化の確認
Userページ全体の日本語化の確認

フォームの基礎

モデルの作成

フォームの理解を深めるために新たにCategoryモデルの作成を行います。


  % php artisan make:model Category -m

   INFO  Model [app/Models/Category.php] created successfully.  

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

app/ModelsディレクトリにCategory.phpファイル、migrationsディレクトリにマイグレーションファイルが作成されます。マイグレーションファイルにname列を追加します。


public function up(): void
{
    Schema::create('categories', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table-'>timestamps();
    });
}

name列を追加後”php artisan migrate”コマンドを実行します。データベースにはcategoriesテーブルが追加されます。

Resourceの作成

作成したCategoryモデルのResourcesの作成を行います。


 % php artisan make:filament-resource Category --generate

   INFO  Filament resource [app/Filament/Resources/CategoryResource.php] created successfully.  

Admin PanelsにアクセスするとCategoriesが追加されていることが確認できます。

Categoryページ
Categoryページ

Categoryの文字列を日本語化します。CategoryResource.phpファイルで設定します。


protected static ?string $modelLabel = 'カテゴリー';
Categoryの日本語化
Categoryの日本語化

Mass Assignments

新規のカテゴリーを追加するために”作成”ボタンをクリックします。カテゴリーの作成画面が表示されるのでNameに文字列を入力して”作成”ボタンをクリックします。

カテゴリーの作成画面
カテゴリーの作成画面

クリックするとIlluminate \ Database \ Eloquent \ MassAssignmentExceptionエラーが発生します。メッセージにあるように問題は回避するためにCategory.phpファイルに$fillableを追加する必要があります。


Add [name] to fillable property to allow mass assignment on [App\Models\Category].

app¥Models¥Category.phpファイルに$fillableを下記のように追加することでエラーは消えます。


namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
    ];
    
}

各モデルファイルでの$fillableの設定ではくapp/Providers/AppServiceProvider.phpのファイルの中でも”mass assignments protection”を無効にすることができます。


<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Model;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Model::unguard();
    }
}

設定後再度カテゴリーの作成を行うと問題は解消してカテゴリーを追加することができます。

カテゴリー追加後の一覧画面
カテゴリー追加後の一覧画面

入力要素のカスタマイズ

カテゴリーの作成画面に戻りinput要素のカスタマイズを行っていきます。ここでの設定はinput要素に限定した設定ではなく他の要素でも利用することができます。

カテゴリーの作成画面
カテゴリーの作成画面

Nameの文字列を名前に変更します。AppServiceProvider.phpファイルのform関数の中でlabelメソッドを追加して引数に”名前”を設定します。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('name')
                ->label('名前')
                ->required(),
        ]);
}
labelの日本語化
labelの日本語化

labelだけではなくPlaceHolderや入力の説明文なでもメソッドを利用して追加することができます。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\TextInput::make('name')
                ->label('名前')
                ->placeholder('カテゴリー名')
                ->helperText('カテゴリー名を入力してください')
                ->required(),
        ]);
}
PlaceHolderなどの設定
PlaceHolderなどの設定

その他の設定はhttps://filamentphp.com/docs/3.x/forms/fields/getting-startedで確認できます。

TextInputの設定

input要素はTextInputコンポーネントのmakeメソッドにテーブルの列名を指定するだけで作成することができます。


public static function form(Form $form): Form
{
return $form
    ->schema([
        Forms\Components\TextInput::make('name'),
    ]);
}
TextInputコンポーネントによるinput要素の作成
TextInputコンポーネントによるinput要素の作成

requiredメソッドを追加するだけでinput要素にrequired属性が設定されlabelの右側に*(アスタリスク)が表示されます。


public static function form(Form $form): Form
{
return $form
    ->schema([
        Forms\Components\TextInput::make('name')
                     ->required(),
    ]);
}

requiredを設定することでブラウザのrequired属性が有効になり、何も入力せず”作成”ボタンをクリックすると”このフィールドを入力してください”というメッセージが表示されます。

ブラウザ機能によるバリデーション
ブラウザ機能によるバリデーション

そのほかにもemail, password, numericなどのメソッドでブラウザのフォームバリデーション機能を利用することができます。TextInputメソッドがどのようなメソッドを持つのかはドキュメント以外ではvendor¥flilament¥forms¥src¥Components¥TextInput.phpファイルで確認できます。

作成後のリダイレクト

カテゴリーを作成するとデフォルトでは右上にメッセージが表示され更新ページにリダイレクトされます。更新ページではなく一覧ページにリダイレクトさせたい場合にはCategoryResource¥Pages¥CreateCategory.phpファイルにgetRedirectUrl関数を追加します。


namespace App\Filament\Resources\CategoryResource\Pages;

use App\Filament\Resources\CategoryResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;

class CreateCategory extends CreateRecord
{
    protected static string $resource = CategoryResource::class;

    protected function getRedirectUrl(): string
    {
        return $this->getResource()::getUrl('index');
    }

}

Laravelのバリデーションルール設定

ブラウザのフォームバリデーションだけではなくLaravelのバリデーションルールの設定方法を確認します。requiredルールであれば下記のように指定することができます。requiredメソッドを設定しているとブラウザ側のバリデーションが先に実行されるので動作確認のためにrequiredメソッドは削除しておきます。通常はブラウザのバリデーションも設定しておきます。


public static function form(Form $form): Form
{
return $form
    ->schema([
        Forms\Components\TextInput::make('name')
                     ->rules('required'),
    ]);
}

バリデーションに失敗した場合にはメッセージがinput要素の下に表示されます。

Laravelのバリデーションルールの設定
Laravelのバリデーションルールの設定

メッセージが英語で表示されています。バリデーションメッセージはFilamentの設定ではなくLaravelのバリデーションメッセージの日本語化で対応します。

日本語化

lang¥jaディレクトリの下にvalidation.phpファイルが必要になりますがデフォルトでは存在しないのでphp artisanコマンドで作成します。


% php artisan lang:publish

   INFO  Language files published successfully. 

実行するとlang¥enディレクトリが作成されその下にauth.php, pagination.php, passwords.php, validataion.phpファイルが作成されます。enディレクトリの名前をjaに変更します。jaディレクトリのvalidation.phpファイルを開いてrequiredの値を日本語に変更します。:attributeはそのままです。


'required' => ':attribute フィールドは必須の項目です',

labelメソッドには名前を設定します。


public static function form(Form $form): Form
{
return $form
    ->schema([
        Forms\Components\TextInput::make('name')
            ->rules('required')
            ->label('名前'),
    ]);
}

再度バリデーションを実行すると日本語化されていることがわかります。

バリデーションエラーの日本語化
バリデーションエラーの日本語化

Relationship

モデル間でRelationshipを持っている場合のフォームの設定について確認していきます。

モデルの作成

新たにFrameworkモデルを追加します。


 % php artisan make:model -m Framework

   INFO  Model [app/Models/Framework.php] created successfully.  

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

FrameworkはCategoryとRelationshipを持っているのでmigrationファイルでは以下のように設定します。


public function up(): void
{
    Schema::create('frameworks', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->text('description')->nullable();
        $table->foreignId('category_id')->constrained();
        $table->timestamps();
    });
}

設定後はphp artisan migrateコマンドを実行してframeworksテーブルを作成します。

Category.phpとFramework.phpファイルにRelationShipsを定義します。


namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Category extends Model
{
    use HasFactory;

    public function frameworks(): HasMany
    {
        return $this->hasMany(Framework::class);
    }
}


namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Framework extends Model
{
    use HasFactory;

    public function category(): BelongsTo
    {
        return $this->belongsTo(Category::class);
    }
}

Resourceの作成

作成したProductモデルのResourcesの作成を行います。


% php artisan make:filament-resource Category --generate

作成されたFrameworkResource.phpファイルで$modelLabelの設定を行います。


protected static ?string $modelLabel = 'フレームワーク';

Admin Panelsにアクセスするとフレームワークが追加されます。

フレームワークページの確認
フレームワークページの確認

フレークワークを追加するために”作成”ボタンをクリックします。デフォルトではCategory Idは手動で入力する必要があります。

Frameworkの作成画面
Frameworkの作成画面

Selectの設定

input要素ではくselect要素によってCategoryIdが設定できるように変更を行います。TextInputコンポーネントからSelectコンポーネントに変更を行います。relationshipのnameにはFramework.phpファイルで設定したRelationshipsの名前を設定します。titleAttributeにはCategoryモデルのnameを指定しています。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\Select::make('category_id')
                ->relationship(name: 'category', titleAttribute: 'name'),
            Forms\Components\TextInput::make('name')
                ->required(),
            Forms\Components\Textarea::make('description')
                ->columnSpanFull(),
        ]);
}

ブラウザで確認するとinput要素からselect要素に変わり、プルダウンメニューでCategoryを選択できるようになります。

selectのプルダウンメニューの確認
selectのプルダウンメニューの確認

“JavaScript”を選択してフレームワークを作成後に一覧を確認します。Category idには選択したJavaScriptのidの1が表示されています。

追加後のフレームワーク一覧
追加後のフレームワーク一覧

Category idではなくCategoryの名前が表示されるようにFrameworkResource.phpファイルのtable関数を変更します。デフォルトではTextColumn::makeの引数にはcategory_idが設定されていましたがcategory.nameに変更しています。


public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('category.name'),
            Tables\Columns\TextColumn::make('name')
                ->searchable(),
            Tables\Columns\TextColumn::make('created_at')
                ->dateTime()
                ->sortable()
                ->toggleable(isToggledHiddenByDefault: true),
            Tables\Columns\TextColumn::make('updated_at')
                ->dateTime()
                ->sortable()
                ->toggleable(isToggledHiddenByDefault: true),
        ])
//略
}

idからカテゴリーの名前に変わっていることが確認できます。

カテゴリーの名前の表示
カテゴリーの名前の表示

メニューへの新しいデータの追加

Selectのプルダウンメニューの中に選択したい項目がない場合に項目を追加することができます。

createOptionFormメソッドを追加します。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\Select::make('category_id')
                ->relationship(name: 'category', titleAttribute: 'name')
                ->createOptionForm([
                    Forms\Components\TextInput::make('name')
                        ->label('カテゴリー名')
                        ->required(),
                ]),
            Forms\Components\TextInput::make('name')
                ->required(),
            Forms\Components\RichEditor::make('description')
                ->columnSpanFull(),
        ]);
}

設定後はselect要素の右側に”+”が表示されます。

select要素の右にプラスボタンが表示
select要素の右にプラスボタンが表示

“+”ボタンをクリックするとModalが表示されカテゴリーを追加することができます。追加したカテゴリーはプロダウンメニューに追加され選択することができます。

Modalの表示
Modalの表示

selectの選択項目の更新

createOptionFormメソッドを利用することでメニューの項目を増やすことができました。editOptionFormメソッドを利用するとメニューの編集を行うことができます。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\Select::make('category_id')
                ->relationship(name: 'category', titleAttribute: 'name')
                ->createOptionForm([
                    Forms\Components\TextInput::make('name')
                        ->label('カテゴリー名')
                        ->required(),
                ])                    
                ->editOptionForm([
                    Forms\Components\TextInput::make('name')
                        ->label('カテゴリー名')
                        ->required(),
                ]),
            Forms\Components\TextInput::make('name')
                ->required(),
            Forms\Components\RichEditor::make('description')
                ->columnSpanFull(),
        ]);
}

プルダウンメニューから選択を行うと編集のアイコンが表示されます。

編集のアイコン表示
編集のアイコン表示

アイコンをクリックすると編集画面がModalで表示され編集を行うことができます。

編集のModal表示
編集のModal表示

フォームの基礎(2)

Rich Editor

FrameworkのdescriptionについてはTextareaコンポーネントを利用していましたがRich Editorを利用することができます。設定はTextareaコンポーネントををRichEditorコンポーネントに変更するだけです。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\Select::make('category_id')
                ->relationship(name: 'category', titleAttribute: 'name'),
            Forms\Components\TextInput::make('name')
                ->required(),
            Forms\Components\RichEditor::make('description')
                ->columnSpanFull(),
        ]);
}

RichEditor設定後の作成画面は文字の太さやリスト表示などで記述することができます。

Rich Editorの表示
Rich Editorの表示

Rich Editorを利用して一部の文字を太文字に設定します。

Rich Editorを利用して入力
Rich Editorを利用して入力

Tableの表示設定

descriptionはフレームワークの一覧ページでは見表示に設定が行われていないので表示されるようにFrameworkResource.phpファイルのtable関数で設定を行います。


public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('category.name'),
            Tables\Columns\TextColumn::make('name')
                ->searchable(),
            Tables\Columns\TextColumn::make('description'),
//略

設定後確認するとRich Editorで記述したdescriptionはHTMLタグを含んで登録されていることがわかります。

descriptionの表示
descriptionの表示

HTMLとして表示させたい場合にはhtmlメソッドを利用します。


Tables\Columns\TextColumn::make('description')
    ->html(),
HTMLとして表示
HTMLとして表示

descriptionの文字列が長い場合はlimitメソッドやwordメソッドを利用して表示させる長さを調整することができます。


Tables\Columns\TextColumn::make('description')
    ->limit(20),
limitメソッドによる表示文字列の長さの設定
limitメソッドによる表示文字列の長さの設定

descriptionメソッド

Category列にdescriptionを一緒に表示させることもできます。descriptionという列名とdescriptionメソッドと同じ名前なので混乱するかもしれませんが下記のようにdescriptionメソッドを利用します。ここではたまたまdescription列をClousureの戻り値に設定していますがidやnameを戻すこともできます。


public static function table(Table $table): Table
{
    return $table
        ->columns([
            Tables\Columns\TextColumn::make('category.name')
                ->description(function(Framework $record):string{
                    return $record->description;
                }),

デフォルトでは下に表示されます。

descriptionメソッドを利用した表示
descriptionメソッドを利用した表示

上に表示させたい場合はdescriptionの第二引数のpositionの値を’above’にします。デフォルトは’below’です。

Modalによるフォーム表示

カテゴリーのように入力項目が少ない場合には作成画面に移動してフォームに入力するのではなくModalを利用することができます。

CategoryResource.phpファイルのgetPages関数のcreateの1行をコメントします。


//略
public static function getPages(): array
{
    return [
        'index' => Pages\ListCategories::route('/'),
        // 'create' => Pages\CreateCategory::route('/create'),
        'edit' => Pages\EditCategory::route('/{record}/edit'),
    ];
}

作成画面ではなくModal上にフォームが表示されカテゴリーを追加することができます。

Modalの表示
Modalの表示

Slide Overによるフォームの表示

Modalによるフォーム設定では作成画面をModal表示にしましたが同様の方法で編集画面もModal表示にすることができます。


//略
public static function getPages(): array
{
    return [
        'index' => Pages\ListCategories::route('/'),
        // 'create' => Pages\CreateCategory::route('/create'),
        // 'edit' => Pages\EditCategory::route('/{record}/edit'),
    ];
}

ModalではなくSlide Overのフォームも利用することができます。


public static function table(Table $table): Table
{
    return $table
        ->columns([
//略
        ->actions([
            Tables\Actions\EditAction::make()->slideOver(),
        ])
//略
}

設定後編集ボタンをクリックすると右側からSlideしてフォームが表示されます。

Slide Overのフォームの表示
Slide Overのフォームの表示

ファイルのアップロード

フォームからのファイルのアップロードについて確認していくためFrameworkモデルのスキーマに追加設定を行います。migrationファイルにアップロードしたファイルのパスを保存するためのimage列を追加します。


public function up(): void
{
    Schema::create('frameworks', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->text('image')->nullable();
        $table->text('description')->nullable();
        $table->foreignId('category_id')->constrained();
        $table->timestamps();
    });
}

frameworksテーブルを再作成するためにphp artisan migrate:rollbackを行い、再度php artisan migrateコマンドを実行します。

アップロードしたファイルはデフォルトではstorage/app/publicディレクトリに保存されます。外部に保存した画像を表示できるように”php artisan storage:link”コマンドを実行してpublicディレクトリにシンボリックリンクを貼ります。


 % php artisan storage:link

   INFO  The [public/storage] link has been connected to [storage/app/public]. 

FrameworkResource.phpファイルのform関数にFileUploadコンポーネントを利用してフォームにファイルをアップロードするためのフィールドを追加します。


public static function form(Form $form): Form
{
    return $form
        ->schema([
            Forms\Components\Select::make('category_id')
                ->relationship(name: 'category', titleAttribute: 'name')
                ->createOptionForm([
                    Forms\Components\TextInput::make('name')
                        ->label('カテゴリー名')
                        ->required(),
                ])                    
                ->editOptionForm([
                    Forms\Components\TextInput::make('name')
                        ->label('カテゴリー名')
                        ->required(),
                ]),
            Forms\Components\FileUpload::make('image'),  //追加      
            Forms\Components\TextInput::make('name')
                ->required(),
            Forms\Components\RichEditor::make('description')
                ->columnSpanFull(),
        ]);
}
ファイルをアップロードするためのフィールドが表示
ファイルをアップロードするためのフィールドが表示
ファイルのアップロード機能についてはfilepondというJavaScriptのライブラリを利用しています。

ファイルをDrag&Dropするとプレビューが表示されます。

アップロードしたファイルの確認
アップロードしたファイルの確認

“作成”ボタンをクリックして保存するとstorage/app/publicディレクトリに01HNH3TM4TA8Z2SMZE4D7CRRDQ.pngという名前のファイルが保存されていることが確認できます。frameworksのimage列にも同じ情報が保存されています。

デフォルトではファイル名にはランダムな名前が設定されるのでアップロードしたファイルの名前のまま保存したい場合にはpreserveFilenamesメソッドを利用することができます。


Forms\Components\FileUpload::make('image')->preserveFilenames()

保存するディレクトリを指定したい場合にはdirectoryメソッドを追加します。


Forms\Components\FileUpload::make('image')->directory('images')->preserveFilenames()

デフォルトではどのようなファイルでも選択してアップロードできるのimageメソッドを追加することで画像ファイルのみアップロード可能となります。その他にも複数ファイルのアップロード、ファイルのサイズの変更等も可能なのでドキュメントを参考にしてください。

TALL Stackの構成するTailwind CSS、 Alpine.js、Livewire、Laravelについて知らなくてもアプリケーションを構築することができることがわかりました。