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();
});
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'] ?? '';
}
}
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'] ?? '';
}
}
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');
}
}
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,
]);
}
}
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();
}
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';
}
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.
I hope this article has helped you to understand how to use polymorphic models by type in Laravel.