Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

AlbertLens's avatar

Please help: Attachments( uploads) for multiple models - MorphToMany Filamentphp

I am developing an app under filementphp 2.x. I will have many models which will need attachments (images, pdfs, xls, docx, etc.) so I need to make a polymorphic relationship such as fileable in order not to repeat tables and tables with files fields.

I have created the morph files table, as this migration:

Schema::create('files', function (Blueprint $table) {
            $table->id();
            $table->morphs('fileable');
            $table->string('file_name')->nullable();
            $table->string('original_file_name')->nullable();
            $table->timestamps();
        });

I have put the fileable function in my FILE Model file:

<?php

namespace App\Models;

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

class File extends Model
{
    use HasFactory;

    public function fileable()
    {
        return $this->morphTo();
    }
}

I have put the files function in my model file (Model is called TiposFinca):

class TiposFinca extends Model
{
    use HasFactory, SoftDeletes;
    
    protected static ?string $name = 'Tipos de finca';

    public $fillable = [
        'nombre', 'coste', 'files'
    ];

    public function files()
    {
        return $this->morphMany(File::class, 'fileable');
    }
}

And now, for my create form, I have added to my TiposFincaResource.php file a new FileUpload field:

 public static function form(Form $form): Form
    {
        return $form
            ->schema([
                Forms\Components\TextInput::make('nombre')
                ->autofocus()
                ->required()
                ->unique(ignoreRecord: true)
                ->placeholder(__('Nombre')),
                FileUpload::make('files')->multiple()->preserveFilenames(),
...

But when I try to save the new record, it throws an error saying:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'files' in 'field list'

I know it really is NOT a field in that table. What is must do is save a new record on the table "Files" but it does not do that.

Any idea, please?

Thanks in advance.

0 likes
6 replies
xsven's avatar

@albertlens , did you figure out a way to do this? I'm having the same morphTo relationship and would be interested to know how you've implemented it.

2 likes
M_Elsherbeny's avatar

I think it's late, but i find a way i'm not sure if it's the better way but it do the purpose.

I make this with product morph many images.

Form inputs:

		    Hidden::make('product_images'),

             FileUpload::make('images')
                   ->required()
                    ->image()
                   ->multiple()
                   ->enableReordering()
                   ->imagePreviewHeight('250')
                   ->removeUploadedFileButtonPosition('right')
                   ->saveUploadedFileUsing(function (array $state, callable $set): void {
                   						$set('product_images', $state);
                   }),

create page:

class CreateProduct extends CreateRecord {

 protected static string $resource = ProductResource::class;

 protected array $images = [];

protected function mutateFormDataBeforeCreate(array $data): array
{
    $this->images = $data['product_images'];

    return $data;
}

protected function afterCreate(): void
{
    $imageTypeId = ImageType::where('slug', 'product')->first()->id;
    foreach ($this->images as $image) {
        // add your code to store images in storage here.

        $this->record->images()->create([
            'path' => $savingImage['image_path'],
            'thumbnail' => $savingImage['thumbnail_path'],
            'image_type_id' => $imageTypeId,
            'alt' => 'product image',
        ]);
    }
}

}

i faced problems in edit but i try this:

class EditProduct extends EditRecord {

protected static string $resource = ProductResource::class;

protected array $images = [];

protected function mutateFormDataBeforeFill(array $data): array
{
    $record = static::getRecord();

    foreach ($record->images as $image) {
        $data['images'][] = $image->path;
    }

    $data['product_images'] = $data['images'] ?? null;

    return $data;
}

protected function mutateFormDataBeforeSave(array $data): array
{
    //get product images as file object from path or add as new file image
    foreach ($data['product_images'] as $image) {
        if (is_string($image) && Storage::exists($image)) {
            $getImage = storage_path('app/public/'.$image);
            $this->images[] = new UploadedFile(path: $getImage, originalName: filesize($getImage));
        } else {
            $this->images[] = $image;
        }
    }

    return $data;
}

protected function afterSave(): void
{
    $filamentService = app()->make(FilamentService::class);
    $imagesService = app()->make(ImagesService::class);

    $imageTypeId = ImageType::where('slug', 'product')->first()->id;

    $record = static::getRecord();
    $record->images()->forceDelete();

    //approach depends on store images in temp directory then delete old directory and rename temp directory
    foreach ($this->images as $image) {
        $savingImage = $filamentService->storeRecordImageInStorage(
            image: $image,
            path: 'products/'.$record->id.'-temp',
            imageSize: 600,
        );

        $storeImagePath = str_replace('-temp', '', $savingImage['image_path']); // remove "-temp" from drectory name
        $storeThumbnailPath = str_replace('-temp', '', $savingImage['thumbnail_path']);

        $this->record->images()->create([
            'path' => $storeImagePath,
            'thumbnail' => $storeThumbnailPath,
            'image_type_id' => $imageTypeId,
            'alt' => 'product image',
        ]);
    }

    $imagesService->deleteImagesDirectory('products/'.$record->id);
    $imagesService->moveImagesDirectory('products/'.$record->id.'-temp', 'products/'.$record->id);
}

}

2 likes

Please or to participate in this conversation.