Blog

Relation manager with spatie media library

Jul 11, 2023
Sushant Aryal
Admin panel, Form builder

Introduction

In this article we will view how to add spatie medialibrary as a relation.

Installation

To get started install new Laravel project called filamentmedia

laravel new filamentmedia

New install filament use this command

cd filamentmedia
composer require filament/filament

Filament recommend adding this to your composer.json's post-update-cmd:

"post-update-cmd": [
// ...
"@php artisan filament:upgrade"
],

Create Model

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

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 Resource and Relation

Create Adventure Resource and Relation Manager for media

php artisan make:filament-resource AdventureResource Adventure
php 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
];
}

Create View Field to preview image

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()),

Refresh

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…