Skip to content

Instantly share code, notes, and snippets.

@monoman81
Last active February 8, 2024 06:49
Show Gist options
  • Save monoman81/8a063cfaeb16963b0558eb11a47f0ae3 to your computer and use it in GitHub Desktop.
Save monoman81/8a063cfaeb16963b0558eb11a47f0ae3 to your computer and use it in GitHub Desktop.
Using Filament with Spatie Media Library
//Model Brand.php.
<?php
namespace App\Models\Admin;
use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
class Brand extends Model implements HasMedia
{
use HasFactory, InteractsWithMedia, Sluggable;
protected $fillable = [
'name',
'abbrv',
];
public function sluggable(): array
{
return [
'slug' => [
'source' => 'name',
]
];
}
//With this method i attach a single logo to the model
public function attachLogo($path, $fileName = '') : self
{
if ($fileName === '')
{
$extension = Str::afterLast($path, '.');
$fileName = strtolower(str_replace(['#', '/', '\\', ' '], '-', $this->name)).'_'.$this->id.'.'.$extension;
}
$this->addMedia($path)
->usingFileName($fileName)
->usingName($this->name.'_'.$this->id)
->toMediaCollection('brands');
return $this;
}
public function registerMediaCollections(): void
{
$this->addMediaCollection('brands')
->useDisk('brands')
->acceptsMimeTypes([
'image/jpeg',
'image/png',
'image/svg+xml',
'image/webp',
'image/gif',
'image/svg',
])
->singleFile();
}
}
//App\Filament\Resources\Admin\BrandResource\BrandResource.php
//Here we define the Form and the Record List of the Resource.
//The only thing of notice is how the logo is showed in the record list using the callback 'getValueUsing => '
<?php
namespace App\Filament\Resources\Admin;
use App\Filament\Resources\Admin\BrandResource\Pages;
use App\Filament\Resources\Admin\BrandResource\RelationManagers;
use App\Filament\Roles;
use App\Models\Admin\Brand;
use Filament\Resources\Forms\Components;
use Filament\Resources\Forms\Form;
use Filament\Resources\Resource;
use Filament\Resources\Tables\Columns;
use Filament\Resources\Tables\Filter;
use Filament\Resources\Tables\Table;
class BrandResource extends Resource
{
public static $icon = 'heroicon-o-collection';
public static $model = Brand::class;
public static $label = 'Marcas';
public static $navigationSort = 1;
public static function form(Form $form)
{
return $form->schema([
Components\TextInput::make('name')
->autofocus()
->rules(['required', 'max:100'])
->maxLength(100)
->placeholder('Nombre')
->label('Nombre'),
Components\TextInput::make('abbrv')
->rules(['required', 'max:6'])
->maxLength(6)
->placeholder('Ingresa una abreviatura para la marca. La abreviatura se usará para generar el SKU de los productos')
->label('Abreviatura')
->hint('Máximo 6 caracteres'),
Components\FileUpload::make('logo')
->image()
->label('Logo'),
]);
}
public static function table(Table $table)
{
return $table->columns([
Columns\Text::make('name')
->primary()
->label('Nombre')
->searchable()
->sortable(),
Columns\Text::make('abbrv')
->label('Abbrv'),
Columns\Image::make('logo')
->label('Logo')
->size(80)
->getValueUsing(function($record) {
return $record->getFirstMediaUrl('brands');
}),
])->filters([
//
]);
}
public static function relations()
{
return [
//
];
}
public static function routes()
{
return [
Pages\ListBrands::routeTo('/', 'index'),
Pages\CreateBrand::routeTo('/create', 'create'),
Pages\EditBrand::routeTo('/{record}/edit', 'edit'),
];
}
}
//App\Filament\Resources\Admin\BrandResource\Pages\CreateBrand.php
<?php
namespace App\Filament\Resources\Admin\BrandResource\Pages;
use App\Filament\Resources\Admin\BrandResource;
use Filament\Forms\Components\FileUpload;
use Filament\Resources\Pages\CreateRecord;
class CreateBrand extends CreateRecord
{
public static $resource = BrandResource::class;
public static $showRoute = 'edit';
//This method gives me the real path of the temporary file that Livewire uploaded to the server. I need this path so i can
//attach the media with my model using Spatie Media Library.
public function getLogoRealPath()
{
$temporaryUploadedFile = $this->getTemporaryUploadedFile('record.logo'); //Upload File Field named 'logo'
if ($temporaryUploadedFile)
return $temporaryUploadedFile->getRealPath();
return '';
}
//Overriding the create method of the CreateRecord class
//I had to do this because how the method handles the uploaded file doesn't allowed me to use any hook to intercept the deleting
//of the temporary file. Once the model is created, the temporary file is long time gone, so i don't have how to attach the media
//to my model with the spatie media library.
//All the other methods and hooks are called as the original class in the same order. Only is removed the method that deletes the
//temporary file and it is added the code to attach the media to the model.
public function create($another = false)
{
$this->callHook('beforeValidate');
$this->validateTemporaryUploadedFiles();
$this->validate();
$this->callHook('afterValidate');
$this->callHook('beforeCreate');
$record = static::getModel()::create($this->record);
$logoPath = $this->getLogoRealPath();
if ($logoPath !== '')
$record->attachLogo($logoPath);
$this->callHook('afterCreate');
if ($another)
{
$this->fillRecord();
$this->notify(__(static::$createdMessage));
return;
}
$this->redirect($this->getResource()::generateUrl(static::$showRoute, [
'record' => $record,
]));
}
}
//App\Filament\Resources\Admin\BrandResource\Pages\EditBrand.php
<?php
namespace App\Filament\Resources\Admin\BrandResource\Pages;
use App\Filament\Resources\Admin\BrandResource;
use Filament\Forms\Components\FileUpload;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Support\Facades\DB;
class EditBrand extends EditRecord
{
public static $resource = BrandResource::class;
public function getLogoRealPath()
{
$temporaryUploadedFile = $this->getTemporaryUploadedFile('record.logo');
if ($temporaryUploadedFile)
return $temporaryUploadedFile->getRealPath();
return '';
}
//In the edit feature, the method to override is the save of the EditRecord class. Basically the steps are the same as in create
//record.
//Only thing to note, if i tried to use $record->save or $record->update, i didn't find why, it tried to save the 'logo' field in
//the brands table, which obvioulsy don't exist and throwed me an exception. That's why the model's save i handled it with the
//DB facade.
public function save()
{
$this->callHook('beforeValidate');
$this->validateTemporaryUploadedFiles();
$data = $this->validate();
$this->callHook('afterValidate');
$this->callHook('beforeSave');
$affectedRows = DB::table('brands')
->where('id', $this->record->id)
->update([
'name' => $data['record']['name'],
'abbrv' => $data['record']['abbrv'],
]);
$logoPath = $this->getLogoRealPath();
if ($logoPath !== '')
$this->record->attachLogo($logoPath);
$this->callHook('afterSave');
$this->notify(__(static::$savedMessage));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment