I have a model for Product
and a related ProductAsset
model for images, videos or documents related to the product. What I want to achieve is to let the user upload the main image as a product asset (flag is_primary=true
) in the same form as the rest of the information.
Here is my Product model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $fillable = [
'name',
'description',
'is_active',
'category_id',
];
public function menus()
{
return $this->belongsToMany(Catalog::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
public function assets()
{
return $this->hasMany(ProductAsset::class);
}
public function allergens()
{
return $this->belongsToMany(Allergen::class);
}
public function mainImage()
{
return $this->assets()->where('is_primary', true)->first();
}
}
And my ProductAsset model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ProductAsset extends Model
{
protected $fillable = [
'name',
'type',
'path',
'is_primary',
'product_id',
];
public function product()
{
return $this->belongsTo(Product::class);
}
}
In my Filament/Resources/ProductResource.php, I have the following in the form()
method:
public static function form(Form $form): Form
{
return $form
->columns(12)
->schema([
Group::make()->schema([
FileUpload::make('image')
->label('Imagen')
->avatar()
->placeholder(function ($record) {
if ($record && $record->mainImage()) {
$main_image = $record->mainImage();
$imagePath = asset("/storage/" . $main_image->path);
return <<
HTML;
}
})
->required(),
])
->columnSpan(2),
Group::make()->schema([
TextInput::make('name')
->label('Nombre')
->required(),
Textarea::make('description')
->label('Descripción')
->required(),
Select::make('category_id')
->label('Categoría')
->relationship(name: 'category', titleAttribute: 'name')
->required(),
Toggle::make('is_active')
->default(true)
->label('Activo'),
])
->columnSpan(10),
]);
}
So, in the Filament/Resources/CreateProduct.php I handle the file using the mutateFormDataBeforeCreate()
and afterCreate()
method:
protected function mutateFormDataBeforeCreate(array $data): array
{
$main_image = $data['image'];
unset($data['image']);
$this->creatingProductAsset = $main_image;
return $data;
}
protected function afterCreate()
{
if ($this->creatingProductAsset) {
ProductAsset::create([
'name' => 'Imagen principal',
'type' => 'image',
'path' => $this->creatingProductAsset,
'product_id' => $this->record->id,
'is_primary' => true, //
]);
}
}
So far so good! My image is uploading, and the related record is created successfully.
Here is the problem: when I enter the view/edit page, it’ll show the image as a placeholder but it won’t attatch the value to the field. Of course, I won’t be able to save changes until I upload a new one. That’s logic since I’m showing the actual image as a placeholder instead of loading it into the field’s state.
Now, my question is, how do I bind the already uploaded image as the field value so the record can be updated without uploading a new one? How should I use the ->afterStateHydrated
method in the field to link the actual image instead of showing it as a placeholder?
Than you all in advance!