Laravel one to one Polymorphic Eloquent relationship tutorial
While designing database structure, you may get to know that some tables are related to one another. For example in a blog application, a user have many posts and post have many comments. In a MySQL database tables, we add one table's id to another table or create pivot table to create relationship between tables.
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 one polymorphic eloquent relationship. This is similar to one-to-one relationship. However, in this relationship, one model may have associated with more than one model. For example, a Profile
model may be associated with User
model as well as Admin
model.
Example:
Now, in this example, we will build relationship between Profile
model with User
and Admin
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: users
, admins
and profiles
. A profiles table will be related with users and admins table.
Migration
we need three migration table. We already have users migration. Run the following commands into Terminal to create remaining migration classes at database/migrations
directory.
php artisan make:migration create_admins_table
php artisan make:migration create_profiles_table
Below are the migration table fields for these table:
users migration
In the users migration, we have defined the following fields:
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
admins migration
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
profiles migration
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->string('image_url');
$table->string('github');
$table->integer('profileable_id');
$table->string('profileable_type');
$table->timestamps();
});
}
Now, let's look at the migration fields. We have profileable_id
and profileable_type
fields in the profiles migration. The profileable_id
will store id value of user or admin and profileable_type
will store the User or Admin class name like App\Models\User
or App\Models\Admin
.
Now run the migrate command to create tables into database.
php artisan migrate
Model
Laravel models are located at app/Models
directory. We already have User
model. Create the rest of model classes for these tables using following Artisan commands one by one into Terminal.
php artisan make:model Admin
php artisan make:model Profile
Now let's build relationship. First create profileable()
method into Profile
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 Profile extends Model
{
use HasFactory;
/**
* Get the parent profileable model.
*/
public function profileable()
{
return $this->morphTo();
}
}
Create profile()
method into User
model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Get the user's profile.
*/
public function profile()
{
return $this->morphOne(Profile::class, 'profileable');
}
}
Also create profile()
method into Admin
model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Admin extends Model
{
use HasFactory;
/**
* Get the admin's profile.
*/
public function profile()
{
return $this->morphOne(Profile::class, 'profileable');
}
}
You may want to use custom field name for "id
" and "type
" columns of polymorphic model. 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 profile.
*/
public function profile()
{
return $this->morphTo(__FUNCTION__, 'profileable_type', 'profileable_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\ProfileController;
Route::get('/user-profile', [ProfileController::class, 'user']);
Route::get('/admin-profile', [ProfileController::class, 'admin']);
Controller
As we have added route, also create ProfileController
using following command.
php artisan make:controller ProfileController
Open the controller at app/Http/Controllers/ProfileController
and create user and admin method. To retrieve the profile of user or admin, you can access profile
property of the model object.
<?php
namespace App\Http\Controllers;
use App\Models\Admin;
use App\Models\Profile;
use App\Models\User;
use Illuminate\Http\Request;
class ProfileController extends Controller
{
/**
* Access user profile.
*
* @return \Illuminate\Http\Response
*/
public function user()
{
$user_profile = User::find(1)->profile;
dd($user_profile);
}
/**
* Access admin profile.
*
* @return \Illuminate\Http\Response
*/
public function admin()
{
$admin_profile = Admin::find(1)->profile;
dd($admin_profile);
}
}
This will return Profile
model 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 profile model record and you may want to retrieve the parent model, you may call morphTo()
method of Profile model. We have defined profileable()
method into Profile
model, so we need to access profileable
property of Profile model.
/**
* Access the parent model of profile.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$parent_model = Profile::find(2)->profileable;
dd($parent_model);
}
This will return parent model record which is associated with Profile model.
I hope this will help you to understand one to one polymorphic relationship.
Copyright 2023 ErrorGram