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:

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.

Was this article helpful?

0 out of 0 person found this article helpful.

Leave a comment

Or

No Comment