Relation manager with spatie media library
In this article we will view how to add spatie medialibrary as a relation.
To get started install new Laravel project called filamentmedia
laravel new filamentmedia
New install filament use this command
cd filamentmediacomposer require filament/filament
Filament recommend adding this to your composer.json
's post-update-cmd
:
"post-update-cmd": [ // ... "@php artisan filament:upgrade"],
php artisan make:model Adventure -m
Edit your migration for adventure table
<?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('adventures', function (Blueprint $table) { $table->id(); $table->string('name'); $table->longText('description')->nullable(); $table->integer('price')->nullable(); $table->boolean('published')->default(1); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('adventures'); }};
Now install filament/spatie-laravel-media-library-plugin
composer require filament/spatie-laravel-media-library-plugin
Now publish the migration to create the media table.
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="migrations"
Migrate the table
php artisan migrate
Prepare your Adventure model for attaching media also add attributes to fillable for mass assignment.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Spatie\MediaLibrary\HasMedia;use Spatie\MediaLibrary\InteractsWithMedia; class Adventure extends Model implements HasMedia{ use HasFactory; use InteractsWithMedia; /** * The attributes that are mass assignable. * * @var array<string> */ protected $fillable = ['name', 'description', 'price', 'published'];}
Create Adventure Resource and Relation Manager for media
php artisan make:filament-resource AdventureResource Adventurephp artisan make:filament-relation-manager AdventureResource media name
Navigate to app\Filament\Resources\AdventureResource.php
file. Define form and table.
public static function form(Form $form): Form{ return $form ->schema([ Forms\Components\Card::make() ->schema([ Forms\Components\TextInput::make('name') ->required() ->maxLength(255), Forms\Components\TextInput::make('price'), Forms\Components\RichEditor::make('description') ->columnSpan('full'), Forms\Components\Toggle::make('published') ->default(true) ->columnSpan('full'), Forms\Components\SpatieMediaLibraryFileUpload::make('photos') ->collection('adventures') ->multiple() ->image(), ]) ->columns(2), ]);} public static function table(Table $table): Table{ return $table ->columns([ Tables\Columns\TextColumn::make('name') ->searchable(), Tables\Columns\TextColumn::make('price'), Tables\Columns\IconColumn::make('published') ->boolean(), Tables\Columns\TextColumn::make('created_at') ->date(), ]) ->filters([ // ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\DeleteBulkAction::make(), ]);}
You must register the new media relation manager in your resource's getRelations()
method:
public static function getRelations(): array{ return [ RelationManagers\MediaRelationManager::class ];}
resources\views\filament\forms\components\image-preview.blade.php
<x-dynamic-component :component="$getFieldWrapperView()" :id="$getId()" :label="$getLabel()" :label-sr-only="$isLabelHidden()" :helper-text="$getHelperText()" :hint="$getHint()" :hint-action="$getHintAction()" :hint-color="$getHintColor()" :hint-icon="$getHintIcon()" :required="$isRequired()" :state-path="$getStatePath()"> <div x-data="{ state: $wire.entangle('{{ $getStatePath() }}').defer }"> <img src="{{ $getState() }}" style="height: 185px;" class="object-cover" /> </div></x-dynamic-component>
Navigate to app\Filament\Resources\AdventureResource\RelationManagers\MediaRelationManager.php
file.
public static function form(Form $form): Form{ return $form ->schema([ Forms\Components\Hidden::make('id'), Forms\Components\ViewField::make('file_name') ->label('Image Preview') ->disabled() ->view('filament.forms.components.image-preview') ->afterStateHydrated(function ($component, $get, $state) { $component->state(Storage::url($get('id') . '/' . $state)); }) ->dehydrated(false), Forms\Components\TextInput::make('name') ->required() ->maxLength(255) ->columnSpan('full'), ]);}
Change Title and Label
protected static ?string $title = 'Images'; protected static ?string $modelLabel = 'Images';
Disable CreateAction and add DeleteAction
public static function table(Table $table): Table{ return $table ->columns([ Tables\Columns\ImageColumn::make('file_name') ->label('Image') ->getStateUsing(function ($record) { return $record->getFullUrl(); }), Tables\Columns\TextColumn::make('name'), ]) ->filters([ // ]) ->headerActions([ // Tables\Actions\CreateAction::make(), ]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ Tables\Actions\DeleteBulkAction::make(), ]) ->reorderable('order_column') ->defaultSort('order_column');}
Disable Image preview on edit form. To do this you add this to SpatieMediaLibraryFileUpload
Forms\Components\SpatieMediaLibraryFileUpload::make('photos') ->loadStateFromRelationshipsUsing(fn ($component) => $component->state([]))
This will not load the previously added media to file upload.
To prevent automatic deletion of the previous added file.
Forms\Components\SpatieMediaLibraryFileUpload::make('photos') ->saveRelationshipsUsing(fn ($component) => $component->saveUploadedFiles())
Now the Form Component will look like this
Forms\Components\SpatieMediaLibraryFileUpload::make('photos') ->collection('adventures') ->multiple() ->image() ->loadStateFromRelationshipsUsing(fn ($component) => $component->state([])) ->saveRelationshipsUsing(fn ($component) => $component->saveUploadedFiles()),
To refresh the page after the submission navigate to app\Filament\Resources\AdventureResource\Pages\EditAdventure.php
file.
<?php namespace App\Filament\Resources\AdventureResource\Pages; use App\Filament\Resources\AdventureResource;use Filament\Pages\Actions;use Filament\Resources\Pages\EditRecord; class EditAdventure extends EditRecord{ protected static string $resource = AdventureResource::class; protected function getActions(): array { return [ Actions\DeleteAction::make(), ]; } protected function getRedirectUrl(): string { return $this->getResource()::getUrl('edit', ['record' => $this->record]); }}
No comments yet…