Let's define the current scenario, we have a photos table, posts and comments. We want to store photos from posts and comments which requires polymorphic relation.
What is polymorphic relationship?
According to official Laravel documentation, polymorphic relationship is:
"A polymorphic relationship allows the child model to belong to more than one type of model using a single association. For example, imagine you are building an application that allows users to share blog posts and videos. In such an application, a Comment model might belong to both the Post and Video models."
OK, got it.... a complicated name for such a common feature... back to our example.
So, we want to create Post, and Photos table and connect them with polymorphic relationship. And let's make it even more real-life example, we will add a connection to post category, and later on we can query for similar posts based on post category
Example of polymorphic relationship in Laravel
First, let's create the posts table with a foreign key to the categories table:
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->unsignedBigInteger('category_id');
$table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
$table->timestamps();
});
Next, let's create the photos table with the polymorphic relation to Post (and potentially other models):
Schema::create('photos', function (Blueprint $table) {
$table->id();
$table->string('url');
$table->unsignedBigInteger('photoable_id');
$table->string('photoable_type');
$table->timestamps();
});
Note the and photoable_id columns, which are used to store the ID and type of the model that the photo belongs to. This allows the photoable_type table to be used for multiple models with a polymorphic relation.photos
Now, let's create the Post and Photo models with their respective relationships:
class Post extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function photos()
{
return $this->morphMany(Photo::class, 'photoable');
}
}
class Photo extends Model
{
public function photoable()
{
return $this->morphTo();
}
}
In the Post model, we define a photos relationship using the morphMany method, which allows the Post model to have many Photo models associated with it. We also define a category relationship using the belongsTo method, which indicates that the Post model belongs to a Category model.
In the Photo model, we define a photoable relationship using the morphTo method, which allows the Photo model to be associated with any model that implements the MorphOne or MorphMany relationships.
Now, let's see an example of how to query posts with photos:
$posts = Post::with('photos')->get();
In the above code, we use the with method to eagerly load the photos relationship for all posts.
Finally, let's see an example of how to get similar posts (with photos) based on a selected photo category:
$photoCategory = 'landscape';
$similarPosts = Post::whereHas('photos', function ($query) use ($photoCategory) {
$query->where('category', $photoCategory);
})
->with(['photos' => function ($query) use ($photoCategory) {
$query->where('category', $photoCategory);
}])
->get();
In the above code, we use the whereHas method to filter posts that have at least one photo with the selected category. We also use the with method to eagerly load the photos relationship for the matching posts, and we use a nested closure to filter the photos by category as well.
Note that the category column in the photos table is assumed to store the category of the photo.