Laravel one to many polymorphic Eloquent relationship tutorial
In a database structure, There are many tables are associated with another tables. For example in a commerce application, a user have many orders and orders have many items.
Laravel provides eloquent relationship which provides powerful query builders. In Laravel, eloquent relationships are defined in model classes. Laravel eloquent provides easy ways to create common relationships:
- one to one
- one to many
- many to many
- has one of many
- has one through
- has many through
- one to one polymorphic
- one to many polymorphic
- many to many polymorphic
In this article, we will discuss on Laravel's one to many polymorphic eloquent relationship. This is similar to one-to-many relationship. However, in this relationship, a child model may be associated with more than one type of model. For example, in a media application, a user can comment on Video and Audio both. So, a Comment
model may be associated with Video
model as well as Audio
model.
Example:
Now, in this example, we will build relationship between Comment
with Video
and Audio
model. We assume that you have created fresh Laravel application. We also assume that you have confiured database connection.
Database tables
In this relationship, we have three database tables: comments
, videos
and audios
. A comments table will be associated with videos and audios table.
Migration
We need to create three migration table. Run the following three commands into Terminal to create migration classes at database/migrations
directory.
php artisan make:migration create_comments_table
php artisan make:migration create_videos_table
php artisan make:migration create_audios_table
Below are the migration table fields for these table:
videos migration
In the videos migration, we have defined the following fields:
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('videos', function (Blueprint $table) {
$table->id();
$table->string('url');
$table->timestamps();
});
}
audios migration
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('audios', function (Blueprint $table) {
$table->id();
$table->string('url');
$table->timestamps();
});
}
comments migration
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->text('body');
$table->integer('commentable_id');
$table->string('commentable_type');
$table->timestamps();
});
}
Now, let's look at the migration fields. We have commentable_id
and commentable_type
fields in the comments migration. The commentable_id
will store id value of video or audio and commentable_type
will store the Video or Audio class name like App\Models\Video
or App\Models\Audio
.
Now run the migrate command to create tables into database.
php artisan migrate
Model
Laravel models are located at app/Models
directory. Create the model classes for these tables using following Artisan commands one by one into Terminal.
php artisan make:model Video
php artisan make:model Audio
php artisan make:model Comment
Now let's build relationship. First create commentable
method into Comment
model which will return morphTo()
method. This will retrieve the parent model record.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
/**
* Get the parent commentable model.
*/
public function commentable()
{
return $this->morphTo();
}
}
Next, create comments
method into Video
model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
use HasFactory;
protected $table = 'videos';
/**
* Get the videos's comments.
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
Also create comments
method into Audio
model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Audio extends Model
{
use HasFactory;
protected $table = 'audios';
/**
* Get the audio's comments.
*/
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
You can also use custom field name for "id
" and "type
" columns of polymorphic model. But you need to ensure that you pass the name of the relationship as the first argument to the morphTo()
method. For that, you may use PHP's __FUNCTION__
magic constant. The second and third arguments are name of type and id fields like below:
/**
* Get the model's comments.
*/
public function comments()
{
return $this->morphTo(__FUNCTION__, 'commentable_type', 'commentable_id');
}
Route
In routes/web.php
file, we have added two new routes for relationship testing.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\CommentController;
Route::get('/video-comments', [CommentController::class, 'video']);
Route::get('/audio-comments', [CommentController::class, 'audio']);
Controller
As we have added route, also create CommentController
using following command.
php artisan make:controller CommentController
Open the controller at app/Http/Controllers/CommentController
and create video and audio method. To retrieve the comments of video or audio, you can access comments
property of the model object.
<?php
namespace App\Http\Controllers;
use App\Models\Audio;
use App\Models\Comment;
use App\Models\Video;
use Illuminate\Http\Request;
class CommentController extends Controller
{
/**
* Access video comments.
*
* @return \Illuminate\Http\Response
*/
public function video()
{
$video_comments = Video::find(1)->comments;
dd($video_comments);
}
/**
* Access audio comments.
*
* @return \Illuminate\Http\Response
*/
public function audio()
{
$audio_comments = Audio::find(1)->comments;
dd($audio_comments);
}
}
This will return all comments record with related model's id of 1. If the relationship not found, the eloquent will return null record.
In the reverse, you may already have the Comment model and you may want to retrieve the parent model, you may call morphTo()
method of Comment
model. We have defined commentable()
method into Comment
model, so we need to access commentable property of Comment model.
/**
* Access the parent model of comment.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$parent_model = Comment::find(2)->commentable;
dd($parent_model);
}
This will return parent model record which is associated with Comment model.
I hope this will help you to understand one to many polymorphic relationship.
Copyright 2023 HackTheStuff