Polymorphic Relations

Polymorphic One to One and One to Many

Products have photos and customers an avatar. One images table serves both by recording which kind of parent each row belongs to. In its migration, $table->morphs('imageable') creates imageable_type (VARCHAR(255)), imageable_id (BIGINT UNSIGNED) and one index on the pair. Product::images() returns $this->morphMany(Image::class, 'imageable'), Customer::avatar() returns $this->morphOne(Image::class, 'imageable'), and the inverse Image::imageable() is a bare $this->morphTo(), which takes its column names from the method name.

Creating an image through a product and resolving each image's ownerSQL
use App\Models\{Customer, Image, Product};
$mug = Product::firstWhere('sku', 'AC-MUG-01');
$image = $mug->images()->create(['path' => 'products/mug.jpg']);
echo "{$image->imageable_type} #{$image->imageable_id}\n";
echo Product::find(1)->images->pluck('path')->join(', '), "\n";
echo Customer::find(1)->avatar->path, "\n";
echo Image::all()->map(fn ($i) => class_basename($i->imageable).' '.$i->imageable->id), "\n";
Output
App\Models\Product #7
products/php-front.jpg, products/php-back.jpg
avatars/ana.jpg
["Product 1","Product 1","Customer 1","Product 7"]

Images 1 and 3 share imageable_id 1; only the type tells them apart. MySQL 524 cannot enforce a foreign key that points at several tables, so deleting a product leaves orphan images unless a deleting event removes them, and stored class names tie data to namespaces (Polymorphic Many to Many).