Polymorphic One To Many

What is Polymorphic One To Many?

  • A Polymorphic One To Many relationship allows a model to belong to more than one type of model using a single association.
  • Polymorphic One To Many allows a single model (like Review) to belong to multiple other models (like Product, Hotel, Movie, Book) using the same relation.

That means:

  • A product can have many reviews.
  • A hotel can have many reviews.
  • A movie can have many reviews.
  • A book can have many reviews.
  • But all reviews are stored in one table (reviews), instead of creating separate tables like product_reviews, hotel_reviews, etc.

Flow diagram


Migration:

Use this command to generate a migration for creating a tables:

php artisan make:migration create_polymorphic_one_to_many_tables


Migration File

public function up(): void
{
    Schema::create('products', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->text('description')->nullable();
        $table->timestamps();
    });

    Schema::create('hotels', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('location')->nullable();
        $table->timestamps();
    });

    Schema::create('movies', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->year('release_year')->nullable();
        $table->timestamps();
    });

    Schema::create('reviews', function (Blueprint $table) {
        $table->id();
        $table->text('review_text');
        $table->unsignedBigInteger('reviewable_id');   // Polymorphic ID
        $table->string('reviewable_type');             // Polymorphic Type
        $table->timestamps();
    });
}

public function down(): void
{
    Schema::dropIfExists('reviews');
    Schema::dropIfExists('movies');
    Schema::dropIfExists('hotels');
    Schema::dropIfExists('products');
}


Run Migration

php artisan migrate

Tables

products table
id name description created_at updated_at
hotels table
id name location created_at updated_at
movies table
id title release_year created_at updated_at
reviews table
id review_text reviewable_id reviewable_type created_at updated_at

Model:

Generate a model with Artisan:

php artisan make:model Review
php artisan make:model Product
php artisan make:model Hotel
php artisan make:model Movie

This generates 4 files Review.php , Product.php ,Hotel.php and Movie.php


Review Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Review extends Model
{
    protected $fillable = ['review_text'];

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


Product Model

namespace App\Models;

use App\Models\Review;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $fillable = ['name', 'description'];

    public function reviews()
    {
        return $this->morphMany(Review::class, 'reviewable');
    }
}


Hotel Model

namespace App\Models;

use App\Models\Review;
use Illuminate\Database\Eloquent\Model;

class Hotel extends Model
{
    protected $fillable = ['name', 'location'];

    public function reviews()
    {
        return $this->morphMany(Review::class, 'reviewable');
    }
}


Movie Model

namespace App\Models;

use App\Models\Review;
use Illuminate\Database\Eloquent\Model;

class Movie extends Model
{
    protected $fillable = ['title', 'release_year'];

    public function reviews()
    {
        return $this->morphMany(Review::class, 'reviewable');
    }
}



CRUD Examples

Create

use App\Models\Hotel;
use App\Models\Movie;
use App\Models\Product;

// Add review for a Product
$product = Product::create(['name' => 'Laptop', 'description' => 'A high-end laptop']);
$product->reviews()->create(['review_text' => 'Great Product!']);

// Add review for a Hotel
$hotel = Hotel::create(['name' => 'Grand Hotel', 'location' => 'New York']);
$hotel = Hotel::find(1);
$hotel->reviews()->create(['review_text' => 'Excellent stay!']);

// Add review for a Movie
$movie = Movie::create(['title' => 'Inception', 'release_year' => 2010]);
$movie->reviews()->create(['review_text' => 'Mind-blowing movie!']);
Output
products table
id name description created_at updated_at
1 Laptop A high-end laptop 2025-08-26 13:18:32 2025-08-26 13:18:32
hotels table
id name location created_at updated_at
1 Grand Hotel New York 2025-08-26 13:18:32 2025-08-26 13:18:32
movies table
id title release_year created_at updated_at
1 Inception 2010 2025-08-26 13:18:32 2025-08-26 13:18:32
reviews table
id review_text reviewable_id reviewable_type created_at updated_at
1 Great Product! 1 App\Models\Product 2025-08-26 13:18:32 2025-08-26 13:18:32
2 Excellent stay! 1 App\Models\Hotel 2025-08-26 13:18:32 2025-08-26 13:18:32
3 Mind-blowing movie! 1 App\Models\Movie 2025-08-26 13:18:32 2025-08-26 13:18:32

Read

use App\Models\Hotel;
use App\Models\Movie;
use App\Models\Product;

$product = Product::find(1);
foreach ($product->reviews as $review) {
  echo $review->review_text;
}

// Get hotel reviews
$hotel = Hotel::find(1);
foreach ($hotel->reviews as $review) {
  echo $review->review_text;
}

// Get movie reviews
$movie = Movie::find(1);
foreach ($movie->reviews as $review) {
  echo $review->review_text;
}

// Get Review with Parent
$review = Review::find(1);
echo $review->reviewable->name;

Update Review

use App\Models\Hotel;
use App\Models\Movie;
use App\Models\Product;

// Find review and update
$review = Review::find(1);
$review->update(['content' => 'Updated review content']);

// Or Update via Product
$product = Product::find(1);
$product->reviews()->where('id', 1)->update(['review_text' => 'Updated review content']);

// Or Update via Hotel
$hostel = Hotel::find(1);
$hostel->reviews()->where('id', 2)->update(['review_text' => 'Updated review content']);

// Or Update via Movie
$movie = Movie::find(1);
$movie->reviews()->where('id', 3)->update(['review_text' => 'Updated review content']);

Delete Review

use App\Models\Hotel;
use App\Models\Movie;
use App\Models\Product;

// Delete a review
$review = Review::find(1);
$review->delete();

// Delete all reviews for a Product
$product = Product::find(1);
$product->reviews()->where('id', 1)->delete();  // where condition delete
$product->reviews()->delete();                  // Delete all reviews

// Delete all reviews for a Hotel
$hotel = Hotel::find(1);
$hotel->reviews()->where('id', 2)->delete();  // where condition delete
$hotel->reviews()->delete();                  // Delete all reviews

// Delete all reviews for a Movie
$movie = Movie::find(1);
$movie->reviews()->where('id', 3)->delete();  // where condition delete
$movie->reviews()->delete();

Eager loading (optimizes queries)

use App\Models\Hotel;
use App\Models\Movie;
use App\Models\Product;

$product = Product::with('reviews')->find(1);
foreach ($product->reviews as $review) {
  echo $review->review_text;
}

// Get hotel reviews
$hotel = Hotel::with('reviews')->find(1);
foreach ($hotel->reviews as $review) {
  echo $review->review_text;
}

// Get movie reviews
$movie = Movie::with('reviews')->find(1);
foreach ($movie->reviews as $review) {
  echo $review->review_text;
}

// Get Review with Parent
$review = Review::with('reviewable')->find(1);
echo $review->reviewable->name;

Always use with() when fetching related data in bulk.


Whereisstuff is simple learing platform for beginer to advance level to improve there skills in technologies.we will provide all material free of cost.you can write a code in runkit workspace and we provide some extrac features also, you agree to have read and accepted our terms of use, cookie and privacy policy.
© Copyright 2024 www.whereisstuff.com. All rights reserved. Developed by whereisstuff Tech.