What are traits?
Traits are a mechanism for code reuse. Traits allow you to create methods that can be used in multiple classes to avoid code duplication. They are intended to enhance traditional inheritance, enabling you to reuse sets of methods freely in several independent classes living in different class hierarchies.
Creating a Trait
Traits are classes nothing more, typically you would create them in a traits
folder or concerns
which has become quite popular in Laravel. I use a traits folder.
When the traits for written to be used in models I place the traits folder inside a app/Models
folder.
The skeleton for a trait is as follows:
<?php
namespace App\Models\Traits;
trait HasUuid
{
public static function booted()
{
...
}
}
Like any other class, a trait needs a namespace so it can be loaded.
Next name the trait in this example the trait is called HasUuid.
Inside the traits are either public methods that can be called or like in this case a named function that would be loaded automatically by Laravel using a special booted
name.
Usage
To use the above trait in a model. The trait will be imported by calling its namespace use App\Models\Traits\HasUuid;
Then inside the class use
followed by the trait name.
<?php
namespace App\Models;
use App\Models\Traits\HasUuid;
class Setting extends Model
{
use HasUuid;
...
}
A complete UUID trait
In the above examples I’ve mentioned using a trait called HasUuid
The complete class:
<?php
namespace App\Models\Traits;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
trait HasUuid
{
public function getIncrementing(): bool
{
return false;
}
public function getKeyType(): string
{
return 'string';
}
public static function booted()
{
static::creating(function (Model $model) {
// Set attribute for new model's primary key (ID) to an uuid.
$model->setAttribute($model->getKeyName(), Str::uuid()->toString());
});
}
}
This trait contains 3 methods. In this case, they are all used internally by Laravel.
getIncrementing
is used to turn off auto-incrementing IDs in a database for a primary key column.
getKeyType
is used to override the getKeyType
method, which is defined in the parent classes/interfaces. In Laravel, the getKeyType
method is used to specify the data type of the primary key in the model associated with the trait.
In this specific example, the getKeyType method is hard-coded to always return the string 'string'. This means that any model using this trait will have its primary key treated as a string type. For instance, if you have a model that uses this trait, and you retrieve its primary key type using $model->getKeyType()
. it will always return string
regardless of the actual data type of the primary key column in the database.
booted
will be called automatically by Laravel, since the method will be called statically it must be defined as a static method.
Creating a custom trait
In Laravel you can’t boot multiple traits so instead it's better to name a trait boot
method in the format of bootHasName
so a trait called HasRoles
would be called bootHasRoles
This allows booting multiple traits and will still be called automatically when they are used inside other classes.
A common use case is building a multi-tenancy system. Where when a user is logged in then grab a column from the table such as tenant_id
and then populate any model with the value of the current tenant.
This avoids having to define a tenend_id any time a model has a create event, this trait will automatically see the create event, and populate a field called tenant_id
and then apply a global function so only records matching this tenant will be used.
<?php
namespace App\Models\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
trait HasTenant
{
public static function bootHasTenant(): void
{
if (auth()->check()) {
$tenantId = auth()->user()->tenant_id;
static::creating(function (Model $model) use ($tenantId) {
$model->tenant_id = $tenantId;
});
static::addGlobalScope('tenant', function (Builder $builder) use ($tenantId) {
$builder->where('tenant_id', $tenantId);
});
}
}
}
To use both these examples in a single model:
<?php
namespace App\Models;
use App\Models\Traits\HasTenant;
use App\Models\Traits\HasUuid;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
use HasTenant;
use HasUuid;
//rest of the model code
}
Conclusion
Traits in Laravel offer a powerful way to reuse code across different classes, thereby reducing code duplication. By utilizing concepts such as the HasUuid
and HasRoles
traits, Laravel applications can be made more efficient and maintainable.
Furthermore, Laravel's unique booting mechanism allows for the automatic execution of trait methods, simplifying the implementation of complex functionalities like multi-tenancy. Therefore, understanding and effectively using traits is an essential skill for any Laravel developer.