Traits and Laravel: the practical guide

April 18, 2024 • 4 min read

Home / Blog / Traits and Laravel: the practical guide
Traits and Laravel: the practical guide | Learn how to implement code and logic reusability by using native PHP traits in Laravel

Introduction

Traits are a mechanism for code re-use in single inheritance languages such as PHP. They were designed to allow us developers to save logic and re-use it across different places in our applications. A class can contain as many traits as required, but functions must not collide. If more than 1 trait contain the same function name, we need to specify which one to use in the class that's using the traits.

 

When can we use traits? Basically when we want to encapsulate code that could run in different parts of our logic. For example,we can make a visitors count for different kind of resources (Posts and Projects) in our Laravel app. They are a good example because we could encapsulate the store visitor logic and re-use it across different models.

 

Getting started

Let's first define the table, model and trait.

 

create_visitors_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('visitors', function (Blueprint $table) {
            $table->id();
            $table->ipAddress('ip');
            $table->string('user_agent');
            $table->string('url', 500);
            $table->string('model_type');
            $table->unsignedBigInteger('model_id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('visitors');
    }
};

 

App\Models\Visitor.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Visitor extends Model
{
    use HasFactory;

    protected $fillable = [
        'ip',
        'user_agent',
        'url',
        'model_type',
        'model_id',
    ];
}

 

App\Traits\HasVisitorCounter.php

<?php

namespace App\Traits;

use App\Models\Visitor;
use Illuminate\Database\Eloquent\Relations\MorphMany;

trait HasVisitorCounter
{
    public function visitors(): MorphMany
    {
        return $this->morphMany(Visitor::class, 'model');
    }

    public function storeVisitor()
    {
        return $this->visitors()->create([
            'ip' => request()->ip(),
            'user_agent' => request()->userAgent(),
            'url' => request()->fullUrl(),
        ]);
    }

    public function visitorsCount()
    {
        return $this->visitors()->count();
    }
}

 

This is the basic structure that we need to use the trait. Now let's add it to the Post and Project models and see how we can re-use the storeVisitor function

 

App\Models\Post.php

<?php

namespace App\Models;

use App\Traits\HasVisitorCounter;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory, HasVisitorCounter;

    protected $fillable = [
        'title',
        'subtitle',
        'content',
        'slug',
        'post_category_id',
        'image',
        'is_published',
        'published_at'
    ];

	...
}

 

App\Models\Project.php

<?php

namespace App\Models;

use App\Traits\HasVisitorCounter;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Project extends Model
{
    use HasFactory, HasVisitorCounter;

    protected $fillable = [
        'title',
        'slug',
        'subtitle',
        'content',
        'image'
    ];

	...
}

 

With that set in place, we can use the storeVisitor() in our model at any place that we want. Particularly, I'm going to use it whenever I render a Post or Project in the application.

 

App\Http\Controllers\Web\PostController.php

/**
 * Display the specified resource.
 */
public function show(Post $post)
{

    // Trait method
    $post->storeVisitor();
    

    $relatedPosts = Post::published()
                        ->where(function($query) use ($post) {
                            $query->where('post_category_id', $post->post_category_id)
                                  ->orWhereHas('tags', function ($query) use ($post) {
                                        $query->where(function($q) use ($query, $post) {
                                            $query->where('model_has_tags.taggable_type', Post::class)
                                                ->whereIn('model_has_tags.tag_id', $post->tags->pluck('id'));
                                        });
                                  });
                        })
                        ->where('posts.id', '!=', $post->id)
                        ->limit(4)
                        ->inRandomOrder()
                        ->get();

    return view('post.show', [
        'breadcrumbs' => $this->breadcrumbsService->getBlogPostBreadcrumbs($post),
        'post' => $post,
        'relatedPosts' => $relatedPosts
    ]);
}

 

App\Http\Controllers\Web\ProjectController.php

/**
 * Display the specified resource.
 */
public function show(Project $project)
{
    // Trait method
    $project->storeVisitor();

    return view('project.show', [
        'breadcrumbs' => $this->breadcrumbsService->getProjectPublicBreadcrumbs($project),
        'project' => $project,
    ]);
}

 

Conclusion

Traits are a powerful tool that can help us maintain our codebase clean and avoid having to apply the same modification on N different places in our application. With their proper usage, it also helps us to enhance code readability and to apply the DRY principle.

 

Liked the post? Feel free to share :)

-Gonza

1210
Laravel,  PHP
Published on April 18, 2024

Ready to automate your customer conversations?

Contact me

About the author

Author

Gonzalo Gomez

AI & Automation Specialist

I design AI-powered communication systems. My work focuses on voice agents, WhatsApp chatbots, AI assistants, and workflow automation built primarily on Twilio, n8n, and modern LLMs like OpenAI and Claude. Over the past 7 years, I've shipped 30+ automation projects handling 250k+ monthly interactions.

Subscribe to my newsletter

If you enjoy the content that I make, you can subscribe and receive insightful information through email. No spam is going to be sent, just updates about interesting posts or specialized content that I talk about.

Related posts

How to Stop Answering the Same Customer Questions 5x a Day (Without Hiring More Agents)

May 12, 2025
IntroductionYour team is answering the same exact questions every single day: “What’s your address?”“Are you open on holidays?”“What’s the return policy?”“Do you offer WhatsApp support?” Whether it’s... Continue reading

Introducing Laravel Cloud: Revolutionizing Laravel Deployment

February 04, 2025
IntroductionIn an exciting announcement at Laracon EU, Taylor Otwell, the creator of Laravel, unveiled Laravel Cloud, a groundbreaking platform designed to streamline the shipping and... Continue reading

Laravel and Twilio: Cloud communications in a nutshell

April 05, 2024
IntroductionIn a previous chapter, I added a small guide with my tips on how to get your 10DLC phone number verified in Twilio, but I... Continue reading

Laravel Timestamps: UTC Storage & Timezone Display (2025)

August 23, 2024
IntroductionLaravel makes timestamp handling easy, but there's a right way and a wrong way. In this guide, you'll learn how Laravel's built-in timestamp handling works... Continue reading