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

325
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

Twilio: Create an A2P 10DLC Campaign

April 08, 2024
IntroductionIf your company requires to implement a messaging service to send, for example, SMS reminders for appointments, booking confirmations or any other message of any... Continue reading

The ultimate guide to sending SMS using PHP and Twilio

September 16, 2024
IntroductionGoing with PHP and Twilio to send SMS is a powerful combination. Whether you're looking to build a webapp, a webservice, or even a script... Continue reading

Laravel Tip: Schema::getColumnListing method

May 15, 2024
IntroductionIn a previous post, I talked about the usage of Eloquent queries or the DB Facade to perform a mass insert of records, which you... 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