DEV Community

Cláudio Oliveira
Cláudio Oliveira

Posted on

3 1 1 1 1

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)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay