Traits are PHP's mechanism for sharing a set of methods across multiple, otherwise-unrelated classes — a form of horizontal code reuse that exists specifically because PHP only supports single inheritance, and some shared behavior doesn't fit neatly into one class hierarchy.
The problem traits solve
If both a User model and a Post model need the same "soft delete" behavior, but they don't (and shouldn't) share a common parent class beyond Model itself, inheritance alone can't cleanly share that behavior between them — a trait can be mixed into both independently.
Defining a trait
trait Loggable
{
public function logActivity(string $message): void
{
Log::info(get_class($this) . ": {$message}", ['id' => $this->id]);
}
}
Using it in a class
class Order
{
use Loggable;
public function markAsShipped()
{
$this->status = 'shipped';
$this->save();
$this->logActivity('Order marked as shipped');
}
}
class User
{
use Loggable;
public function suspend()
{
$this->suspended = true;
$this->save();
$this->logActivity('User account suspended');
}
}
Order and User share no inheritance relationship, but both now have a working logActivity() method through the same trait.
Using multiple traits in one class
class Post
{
use Loggable, HasSlug, Searchable;
}
A class can use as many traits as needed — this is exactly how Eloquent models often compose several independent behaviors (soft deletes, factories, notifications) without a deep, rigid inheritance chain.
Trait constructors: bootHasSlug and similar conventions
PHP traits don't have their own real constructor, but Laravel's Eloquent uses a naming convention (bootTraitName, initializeTraitName) that it calls automatically for any trait following that pattern — this is a framework-level convention built on top of plain PHP traits, not a native PHP feature.
Resolving a method name conflict between two traits
class Report
{
use Exportable, Printable {
Exportable::export insteadof Printable;
Printable::export as printExport;
}
}
If two traits used in the same class define a method with the same name, PHP requires resolving the conflict explicitly with insteadof (choosing one) and optionally as (aliasing the other under a different name) — without this, using both traits together is a fatal error.
When to reach for a trait vs. a service class or composition
A trait suits small, focused, genuinely reusable behavior shared across otherwise-unrelated classes — for anything with real internal state, more complex dependencies, or behavior that would benefit from being independently testable and swappable, a proper service class injected via composition is generally the better-designed choice.