DEV Community

Cláudio Oliveira
Cláudio Oliveira

Posted on

Explorando o Padrão de Projeto Template Method com Laravel e Livewire

Olá, nesse post eu quero tentar simplificar a codificação usando o padrão Template Method em uma aplicação Laravel com Livewire.

Template Method?

O Template Method é um padrão de projeto que define a estrutura básica em uma classe base (superclasse), mas permite que as subclasses alterem partes específicas dessa estrutura sem mudar sua comportamento geral. Isso proporciona flexibilidade e reuso de código já que definimos comportamentos gerias na superclasse que são executados nas subclasses.

Saiba mais sobre padrões de projeto aqui.

Aplicando o Template Method

Para exemplificar, iremos usar o famoso CRUD de posts em um blog.

Dentro de um projeto Laravel criei um modelo Post e fiz a associação com o User.

Post.php

<?php

namespace App\Models;

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

class Post extends Model
{
    use HasFactory;

    protected $fillable = ['user_id', 'title', 'body'];

    public  function  user(): BelongsTo
    {
        $this->belongsTo(User::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

User.php

<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
    ];

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}
Enter fullscreen mode Exit fullscreen mode

Iremos criar os componentes Livewire responsáveis pelo CRUD e nesta postagem iremos nos atentar a criação e a edição: Post\Create, Post\Edit (eu escrevi outro post de como costumo organizar os meus componentes livewire)

Image description

O componente Create é responsável por criar um post e o Edit por atualizar um post existente. Os campos são simples, um título e um "corpo" da postagem.

Agora iremos aplicar o Template Method. Criaremos uma classe base (superclasse) para que as subclasses (Create e Edit) possam herdar diretamente dela. Nesse caso, elas deixariam de herdar da classe Component do Livewire.

<?php
namespace App\Livewire\Post;

class Create extends PostBase
{
 // Create component
}
Enter fullscreen mode Exit fullscreen mode
<?php
namespace App\Livewire\Post;

class Edit extends PostBase
{
 // Edit component
}
Enter fullscreen mode Exit fullscreen mode

Criaremos a superclasse como abstrata para podermos "forçar" as subclasses a implementarem alguns métodos em comum, mas isso não é uma regra. Poderíamos tranquilamente criar uma classe normal com seus devidos métodos para serem herdados pelas subclasses.

Para que o comportamento do Livewire funcione normalmente, a superclasse irá herdar de Component do Livewire, assim a reatividade/funcionalidades providas pelo pacote continuarão funcionando normalmente.

<?php

namespace App\Livewire\Post;

use Illuminate\View\View;
use Livewire\Component;

abstract class PostBase extends Component
{
    public array $fields;

    protected array $rules = [
        'fields.title' => 'required',
        'fields.body' => 'required'
    ];

    protected array $messages = [
        'fields.*' => 'The field is required.'
    ];

    public function mount(): void
    {
        $this->fields = [];
    }

    abstract function render(): View;
}

Enter fullscreen mode Exit fullscreen mode

Dentro da superclasse podemos colocar tudo que é comum como validações, declaração de atributos e etc.

Aproveitando que estamos usando uma classe abstrata - abstract class PostBase {} - iremos criar um método abstrato - abstract function render(): View - com sua respectivas assinatura que deverá ser implementada nas subclasses.

Post.php
<?php

namespace App\Livewire\Post;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;

class Create extends PostBase
{

    public function render(): View
    {
        return view('livewire.post.create');
    }

    public function store(): RedirectResponse|Redirector
    {
        $this->validate();

        Post::create([
        'user_id' => auth()->user()->id,
        ... $this->fields
        ]);

        session()->flash('success', 'Post successfully created.');

        return to_route('dashboard');
    }
}
Enter fullscreen mode Exit fullscreen mode
Edit.php
<?php

namespace App\Livewire\Post;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;

class Edit extends PostBase
{
    public Post $post;

    public function mount(): void
    {
        $this->fields = $this->post->toArray();
    }

    public function render(): View
    {
        return view('livewire.post.edit');
    }

    public function update(): RedirectResponse|Redirector
    {
        $this->validate();

        $this->post->update($this->fields);

        session()->flash('success', 'Post successfully updated.');

        return to_route('dashboard');
    }
}
Enter fullscreen mode Exit fullscreen mode

A sobrescrita do método é livre por parte do desenvolvedor, caso necessite. Por isso o Template Method é tão produtivo, pois evita repetição de código e fluidez no fluxo do desenvolvimento. Neste caso, sobrescrevemos o método mount() na classe Edit para inicializarmos os campos com os dados que recebemos na criação do componente.

O projeto completo deste post está no github, se puder da uma ⭐ eu ficaria grato.

Ha, fique a vontade para criticar e sugerir melhorias na aplicação do padrão. Essa é uma solução que utilizo no meu dia a dia e gostaria de aprender com você caso queira compartilhar algo 😎✌️.

Abraços!

Top comments (0)