Laravel‘s Polymorphic Relationships: A Practical Use Case

Laravel Polymorphic Relationships

Laravel‘s polymorphic relationships are a powerful feature that allow a single model to belong to multiple other models. While the official Laravel documentation provides a basic example using blog posts and comments, polymorphic relationships can enable elegant solutions to much more complex real-world problems.

In this article, we‘ll explore a practical use case for polymorphic relationships in an e-commerce application. We‘ll see how they can streamline the database schema and simplify queries for features like:

  • Product reviews and ratings
  • Questions and answers on product pages
  • Favorites or wishlist system
  • Order history and shipment tracking

Let‘s dive in and see how polymorphic relationships work their magic!

The E-Commerce Example

Consider an e-commerce site with the following core features:

  • Users can browse products, view details and images, and add items to their cart
  • Users can leave text reviews and 1-5 star ratings on products they‘ve purchased
  • Users can post questions on product pages, which are answered by the site admin or other users
  • Users can save products to a favorites list to purchase later
  • Users can view their past orders and the real-time status of pending shipments

Traditionally, each of these features would require separate database tables linking back to the products and users tables, quickly leading to a convoluted schema. However, with Laravel‘s polymorphic relationships, we can use a single table to handle multiple linkages.

Designing the Database Schema

Here‘s how we can structure the database using polymorphic relationships:

Polymorphic e-commerce database schema

The key tables enabling the polymorphic magic are:

  • reviews – stores all reviews, with the reviewable_id and reviewable_type columns linking to either a product or a user (for seller/store reviews in a marketplace)

  • questions – stores all product questions, with questionable_id and questionable_type columns (only linking to products in this case, but flexible for future expansion)

  • favorites – a polymorphic many-to-many link of products, categories, sellers, etc. that users can save to their wishlist

The orders and order_items tables use a standard one-to-many relationship, but the shipments table also links polymorphically to allow tracking multiple shipments per order, each delivering a subset of the order items.

Defining the Eloquent Models

With the schema in place, we can define the polymorphic relationships in the Laravel Eloquent models:

class Product extends Model
{
    public function reviews()
    {
        return $this->morphMany(Review::class, ‘reviewable‘);
    }
public function questions()
{
    return $this->morphMany(Question::class, ‘questionable‘);  
}

public function favorites()
{
    return $this->morphToMany(User::class, ‘favoriteable‘);
}

}

class User extends Model
{
public function reviews()
{
return $this->morphMany(Review::class, ‘reviewable‘);
}

public function favorites()
{
    return $this->morphedByMany(Product::class, ‘favoriteable‘);  
}

public function orders()
{
    return $this->hasMany(Order::class);
}

}

class Review extends Model
{
public function reviewable()
{
return $this->morphTo();
}
}

class Question extends Model
{
public function questionable()
{
return $this->morphTo();
}

public function answers()
{
    return $this->hasMany(Answer::class);
}

}

class Answer extends Model
{
public function question()
{
return $this->belongsTo(Question::class);
}
}

class Order extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}

public function items()
{
    return $this->belongsToMany(Product::class, ‘order_items‘) 
        ->withPivot(‘quantity‘, ‘price‘)
        ->withTimestamps();
}

public function shipments()
{
    return $this->hasMany(Shipment::class);
}

}

class Shipment extends Model
{
public function order()
{
return $this->belongsTo(Order::class);
}

public function items()
{
    return $this->belongsToMany(OrderItem::class);
}

}

The polymorphic relationship methods like morphMany(), morphTo(), and morphToMany() succinctly define the connections between models.

Querying Polymorphic Relationships

With the models and relationships defined, retrieving data is quite intuitive using Laravel‘s Eloquent ORM:

// Get all reviews for a product
$product = Product::find(1);
$reviews = $product->reviews;

// Get a user‘s favorite products $user = User::find(1); $favorites = $user->favorites;

// Get all questions and answers for a product
$product = Product::find(1); $questions = $product->questions;

foreach ($questions as $question) { $answers = $question->answers; }

// Get a user‘s order history $user = User::find(1); $orders = $user->orders;

foreach ($orders as $order) { $items = $order->items; $shipments = $order->shipments;
}

The power of polymorphic relationships shines through in how simple it is to access related items without having to perform complex joins. The single reviews and questions tables handle the content for multiple other models.

Adding new types of reviewable or questionable items is trivial – simply add the morphMany() relation to the model and you‘re set. No database schema changes needed.

Querying a user‘s favorites from potentially different models is also seamless using the morphedByMany() relation. We can easily eager load a user‘s favorite products, categories, blogs, etc. in a single line of Eloquent.

Conclusion

Laravel‘s polymorphic relationships are a hidden gem that can make complex real-world application schemas much more manageable. By reducing repetition and allowing a single source of data to connect to multiple models, they keep the codebase DRY and flexible.

The e-commerce domain is a prime use case, with many content types like reviews, questions, and favorites that span products, users, categories and more. Polymorphic relations let us model this elegantly with minimal database tables. Yet they still provide convenient and performant ways to query the data.

Next time you‘re wrestling with a complicated database schema, consider if polymorphic relationships can help. They may be just the magic bullet you need to slay that many-headed hydra!

What other use cases for polymorphic relations have you found? Share your thoughts and questions in the comments below. And if you found this article helpful, please share it so others can also learn this Laravel superpower. Happy coding!

Similar Posts