“Some do banco” nem sempre é o que você quer
Imagina a cena: o cliente liga desesperado porque um funcionário apagou um pedido importante. Você abre o banco, e… não tem. Sumiu. DELETE é DELETE. Agora é backup ou choro.
Agora imagina a mesma cena, só que você digita $pedido->restore() e o registro volta como se nada tivesse acontecido. Herói do dia.
Essa é a proposta do Soft Delete: em vez de apagar a linha de verdade, o Laravel marca ela como apagada e finge que ela não existe mais nas consultas. Ela continua lá, só invisível. E dá pra trazer de volta quando quiser.
O problema: delete() é definitivo
Sem soft delete, isso aqui é irreversível:
$pedido->delete(); // tchau. pra sempre. 💀
Enter fullscreen mode Exit fullscreen mode
Some do banco, some dos relatórios, some da vida. Se alguém apagou por engano, ou se você precisa de histórico pra auditoria, azar. E é impressionante como “apagou por engano” acontece com frequência em produção.
A solução: três passos e pronto
Primeiro, adiciona a coluna deleted_at na migration. O Laravel tem um helper só pra isso:
Schema::table('pedidos', function (Blueprint $table) {
$table->softDeletes(); // cria a coluna deleted_at (nullable)
});
Enter fullscreen mode Exit fullscreen mode
Depois, usa a trait SoftDeletes na model:
use Illuminate\Database\Eloquent\SoftDeletes;
class Pedido extends Model
{
use SoftDeletes;
}
Enter fullscreen mode Exit fullscreen mode
Pronto. Agora o delete() mudou de comportamento sem você tocar em mais nada:
$pedido->delete();
// não apaga a linha — só preenche deleted_at com a data/hora de agora
Enter fullscreen mode Exit fullscreen mode
E o melhor: as consultas normais ignoram automaticamente os registros apagados. Pedido::all() não traz nada que foi soft-deletado. Você não precisa filtrar nada na mão.
Trazendo de volta e vendo os apagados
Restaurar é uma linha:
$pedido->restore(); // deleted_at volta pra null, registro "revive"
Enter fullscreen mode Exit fullscreen mode
Pra incluir os apagados numa consulta, withTrashed:
Pedido::withTrashed()->get(); // traz tudo, apagados junto
Enter fullscreen mode Exit fullscreen mode
Pra ver só os apagados (útil pra uma tela de “lixeira”):
Pedido::onlyTrashed()->get();
Enter fullscreen mode Exit fullscreen mode
E se precisar apagar de verdade, aí sim, sem volta:
$pedido->forceDelete(); // agora foi pro além de verdade
Enter fullscreen mode Exit fullscreen mode
Como usar na prática
Lixeira pro usuário. Uma tela onde ele apaga itens, mas tem 30 dias pra restaurar. onlyTrashed() lista, restore() recupera.
Auditoria e histórico. Setores como financeiro e jurídico costumam exigir que nada suma de verdade. Soft delete mantém o rastro sem poluir as telas do dia a dia.
Checar o estado no código. Dá pra saber se um registro tá apagado:
if ($pedido->trashed()) {
// esse aqui tá na lixeira
}
Enter fullscreen mode Exit fullscreen mode
Pegadinha 1: unique não sabe de soft delete
Essa pega muita gente. Você tem uma coluna email com índice unique. O usuário [email protected] é soft-deletado. Ele tenta se cadastrar de novo… e o banco recusa, porque a linha antiga ainda está lá, ocupando o email.
O unique do banco não liga pro deleted_at — pra ele, a linha existe. Na validação, você precisa dizer isso explicitamente. Na Rule unique, ignore os apagados:
use Illuminate\Validation\Rule;
Rule::unique('usuarios', 'email')->whereNull('deleted_at');
Enter fullscreen mode Exit fullscreen mode
Assim a validação só considera os registros ativos, e o email “liberado” pelo soft delete volta a funcionar.
Pegadinha 2: relacionamento com registro apagado
Outra que confunde. O pedido foi soft-deletado, mas você carrega ele por um relacionamento e o item some:
$cliente->pedidos; // não traz os pedidos apagados
Enter fullscreen mode Exit fullscreen mode
Isso normalmente é o que você quer. Mas quando não for — tipo montar um histórico completo do cliente — o relacionamento também aceita withTrashed:
public function pedidos()
{
return $this->hasMany(Pedido::class)->withTrashed();
}
Enter fullscreen mode Exit fullscreen mode
Ou pontualmente, sem mexer na model:
$cliente->pedidos()->withTrashed()->get();
Enter fullscreen mode Exit fullscreen mode
Bônus: e agora?
Olha as tabelas do seu projeto e pergunta pra cada uma: “se alguém apagar isso por engano, dói?” Pedido, fatura, contrato, usuário — provavelmente sim. Categoria de teste que você cria e joga fora — provavelmente não. Soft delete é ótimo, mas não precisa estar em tudo; coluna a mais e query um pouco mais complexa têm um custo.
E cuidado com a ideia de “guardar tudo pra sempre”. Dependendo do dado (LGPD, por exemplo), você pode ser obrigado a apagar de verdade depois de um tempo. Aí um job periódico com forceDelete() nos registros antigos resolve.
Antes de você fechar a aba
Já perdeu algum registro importante em produção por causa de um delete() sem volta? Ou já foi salvo por um soft delete que alguém teve a sabedoria de colocar antes?
Me conta aí embaixo. E se esse post pode evitar um choro futuro no seu projeto, salva ele e manda pro colega que ainda apaga tudo direto no banco. 😅
답글 남기기