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

257
Laravel,  PHP

About the author

Author

Gonzalo Gomez

Sr. Software Engineer

I have over 6 years of experience building highly scalable web applications using a wide variety of technologies. Currently focusing on Laravel, Livewire, Vue.js and AWS as my go-to stack.

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.

Ready to take your project to the next level?

Contact me

Related posts

Content rendering: Blade vs Vue and React

June 03, 2024
IntroductionThere are different approaches when it comes to showing data to the end user. Today, I'd like to talk about the 2 alternatives that I... Continue reading

How to integrate Google Calendar with Laravel

September 03, 2024
How to Seamlessly Integrate Google Calendar into Your Laravel AppIntegrating Google Calendar into your Laravel application can be a game-changer, especially if you need to... 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

Best practices for storing timestamps in Laravel

August 23, 2024
IntroductionWhen dealing with time-sensitive data in web applications, timestamps are crucial. They help track everything from user activity to system logs. However, the way we... Continue reading