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.