Traits en PHP y Laravel: guía práctica con ejemplos

18 de abril de 2024 • 4 min de lectura

Inicio / Blog / Traits en PHP y Laravel: guía práctica con ejemplos

Sobre el autor

Author

Gonzalo Gomez

Especialista en IA y Automatización

Diseño sistemas de comunicación impulsados por IA. Mi trabajo se centra en agentes de voz, chatbots de WhatsApp, asistentes de IA y automatización de flujos, construidos principalmente sobre Twilio, n8n y LLM modernos como OpenAI y Claude. En los últimos 7 años he entregado más de 30 proyectos de automatización que manejan más de 250 000 interacciones mensuales.

Suscríbete a mi newsletter

Si te gusta el contenido que hago, puedes suscribirte y recibir información valiosa por correo. No se envía spam, solo novedades sobre publicaciones interesantes o contenido especializado del que hablo.

Traits en PHP y Laravel: guía práctica con ejemplos | Cómo encapsular lógica reutilizable con traits en Laravel, con el ejemplo real de un contador de visitas compartido entre los modelos Post y Project.

Introducción

Los traits son un mecanismo de reutilización de código para lenguajes de herencia simple como PHP. Están diseñados para que los desarrolladores guardemos lógica y la reusemos en distintos lugares de la aplicación. Una clase puede contener todos los traits que haga falta, pero las funciones no pueden colisionar. Si más de 1 trait tiene el mismo nombre de función, hay que especificar cuál usar en la clase que los usa.

 

¿Cuándo conviene usar traits? Básicamente cuando querés encapsular código que puede correr en distintas partes de tu lógica. Por ejemplo, podemos armar un contador de visitas para distintos tipos de recursos (Posts y Projects) en nuestra app Laravel. Son un buen ejemplo porque podemos encapsular la lógica de guardar la visita y reusarla en varios modelos.

 

Para empezar

Primero vamos a definir la tabla, el modelo y el 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();
    }
}

 

Esta es la estructura básica que necesitamos para usar el trait. Ahora vamos a sumarlo a los modelos Post y Project para ver cómo reusar la función storeVisitor

 

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'
    ];

	...
}

 

Con eso listo, ya podemos usar storeVisitor() en el modelo donde queramos. En particular, yo lo voy a usar cada vez que renderizo un Post o un Project en la aplicación.

 

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,
    ]);
}

 

Conclusión

Los traits son una herramienta potente para mantener el codebase limpio y evitar aplicar la misma modificación en N lugares distintos de la aplicación. Bien usados, también mejoran la legibilidad del código y te ayudan a aplicar el principio DRY.

 

¿Te sirvió el post? Si querés, compartilo :)

-Gonza

1765
Laravel,  PHP
Publicado el 18 de abril de 2024

Descubre cuánto te está costando tu sistema de comunicaciones.

Obtén la auditoría de comunicaciones

Posts relacionados

Cómo enviar SMS con Laravel y Twilio en producción

5 de abril de 2024
IntroducciónEn un capítulo anterior armé una guía con mis consejos para verificar tu número 10DLC en Twilio. Lo que nunca hice fue la guía de... Leer más

Cómo integrar Google Calendar con Laravel paso a paso

3 de septiembre de 2024
Cómo integrar Google Calendar en tu app LaravelIntegrar Google Calendar en tu aplicación Laravel te cambia el juego, sobre todo si necesitás manejar eventos, agendar... Leer más

Schema::getColumnListing en Laravel: inserts sin romper

15 de mayo de 2024
IntroducciónEn un post anterior hablé sobre usar queries de Eloquent o el DB Facade para hacer un insert masivo de registros, que podés leer acá.... Leer más

Automatizar respuestas de WhatsApp con Twilio y Laravel

12 de mayo de 2025
IntroducciónTu equipo responde exactamente las mismas preguntas todos los días, por WhatsApp, mail y DMs: "¿Cuál es la dirección?""¿Abren los feriados?""¿Cuál es la política de devoluciones?""¿Dan... Leer más