Laravel Polymorphic Models By Type

Paulund - May 30 - - Dev Community

I needed a better way of handling different model types based on a single database table.

The example I had was that there was a settings table for the application that can return different types of settings based on the type column of the row.

Schema::create('settings', function (Blueprint $table) {
    $table->id();
    $table->string('type');
    $table->text('settings');
    $table->timestamps();
});
Enter fullscreen mode Exit fullscreen mode

The type column would be used to determine the type of setting that was being stored in the settings column. Which can be something like email, slack, sms etc. The settings column would be a JSON column that would store the settings for the notification type such as the email to send or the token and channel to send the slack notification.

Because the value of the settings column can consist of different types of settings I needed a way to handle this in the application. I wanted different classes to use for each type of setting, such as EmailSetting, SlackSetting, SmsSetting etc.

The EmailSetting will have a method getEmail to get the email in the settings json column.

<?php

namespace App\Models\Notifications;

use Illuminate\Database\Eloquent\Model;

class EmailSetting extends Model
{
    public function getEmail()
    {
        return $this->settings['email'] ?? '';
    }
}
Enter fullscreen mode Exit fullscreen mode

While the SlackSetting class will have a method getChannel to get the channel in the settings json column and a getToken to get the API token.

<?php

namespace App\Models\Notifications;

use Illuminate\Database\Eloquent\Model;

class SlackSetting extends Model
{
    public function getChannel()
    {
        return $this->settings['channel'] ?? '';
    }

    public function getToken()
    {
        return $this->settings['token'] ?? '';
    }
}
Enter fullscreen mode Exit fullscreen mode

Polymorphic Relationship

To handle this I used a polymorphic relationship in Laravel to handle the different types of settings. I created a Setting model that would be used to handle the different types of settings.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Setting extends Model
{
    public function notification()
    {
        return $this->morphTo('type', 'type', 'id');
    }
}
Enter fullscreen mode Exit fullscreen mode

The notification method is a polymorphic relationship that will return the correct model based on the type column of the settings table.

In order to tell Laravel which model to use for each type we need to add a morphMap to the AppServiceProvider class.

<?php

namespace App\Providers;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Relation::morphMap([
            'email' => EmailSetting::class,
            'slack' => SlackSetting::class,
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

The morphMap method will map the email and slack types to the EmailSetting and SlackSetting classes respectively.

Now when you retrieve a setting from the database you can access the correct model based on the type column.

$setting = Setting::find(1);

if($setting->type === 'email') {
    $email = $setting->notification->getEmail();
}

if($setting->type === 'slack') {
    $channel = $setting->notification->getChannel();
    $token = $setting->notification->getToken();
}
Enter fullscreen mode Exit fullscreen mode

But with this you will get an error when you try to use ->notification as it will try to access the the email_settings table that doesn't exist.

To fix this you need to tell the EmailSetting model what table to use by using the method getTable.

public function getTable()
{
    return 'settings';
}
Enter fullscreen mode Exit fullscreen mode

This will tell the EmailSetting model to use the settings table instead of the email_settings table and return the right model for you when you use the notification settings.

This is how you can use polymorphic models by type in Laravel to handle different types of settings in a single database table. This can be useful when you have different types of settings that you need to handle in your application.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .