Explorando o Padrão de Projeto Template Method com Laravel e Livewire
Published: 2024-01-05 00:06:39

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.](https://refactoring.guru/pt-br/design-patterns) 

## 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);
    }
}
```


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);
    }
}
```

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)](https://dev.to/claudio1994oliveira/organizando-os-livewire-components-4bj2)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cjyd1j7xeouby3o8hsdu.png)

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
}
```

```
<?php
namespace App\Livewire\Post;

class Edit extends PostBase
{
 // Edit component
}
```
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;
}

```

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');
    }
}
```

```
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');
    }
}
```
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](https://github.com/claudio1994-oliveira/blog-laravel), 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!