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