DEV Community

Denis Augusto
Denis Augusto

Posted on

Casts do Eloquent que você não sabia que existiam

Você ainda faz json_decode na mão, né?

Confessa. Tem um lugar no seu projeto onde você lê uma coluna do banco, dá um json_decode, mexe no array, dá json_encode de volta e salva. E toda vez você reza pra não esquecer o true no json_decode.

Ou pior: aquela coluna que guarda uma lista separada por vírgula e vive num vai-e-vem de explode e implode.

O Eloquent resolve isso há anos com casts. Só que a maioria dos devs conhece uns quatro (array, boolean, datetime, integer) e para por aí. O problema é que a lista é bem maior — e tem uns casts que são pura mão na roda esperando você descobrir.

Relembrando rapidinho: o que é um cast

Cast é uma tradução automática entre o formato que fica no banco e o formato que você quer usar no PHP. Você declara no model e o Eloquent faz a ponte sozinho, nos dois sentidos:

class Produto extends Model
{
    protected function casts(): array
    {
        return [
            'ativo'      => 'boolean',
            'preco'      => 'decimal:2',
            'publicado_em' => 'datetime',
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Até aqui, nada de novo. A graça começa nos casts que ninguém mostra.

AsArrayObject: array de verdade, mexível

O cast array clássico tem uma pegadinha chata: você não consegue mutar um item direto. Isso aqui não funciona como você espera:

$produto->options['cor'] = 'azul'; // ignorado silenciosamente 😑
Enter fullscreen mode Exit fullscreen mode

Com AsArrayObject, funciona:

use Illuminate\Database\Eloquent\Casts\AsArrayObject;

protected function casts(): array
{
    return [
        'options' => AsArrayObject::class,
    ];
}
Enter fullscreen mode Exit fullscreen mode
$produto->options['cor'] = 'azul';
$produto->options['tamanho'] = 'M';
$produto->save(); // salva certinho
Enter fullscreen mode Exit fullscreen mode

Coluna JSON que você mexe direto, sem cerimônia.

AsCollection: sua coluna JSON vira Collection

Se você ama as Collections do Laravel (e você ama), por que trabalhar com aquele JSON como array cru? O AsCollection te devolve uma Collection completa, com map, filter, where, o pacote inteiro:

use Illuminate\Database\Eloquent\Casts\AsCollection;

protected function casts(): array
{
    return [
        'tags' => AsCollection::class,
    ];
}
Enter fullscreen mode Exit fullscreen mode
$produto->tags->filter(fn ($tag) => $tag['ativo'])->pluck('nome');
Enter fullscreen mode Exit fullscreen mode

E tem um detalhe pouco conhecido: dá pra apontar uma Collection customizada sua, com métodos próprios de negócio:

'options' => AsCollection::using(OptionCollection::class),
Enter fullscreen mode Exit fullscreen mode

AsEnumCollection: lembra dos Enums? Agora um array deles

Já falei aqui sobre castar uma coluna pra Enum. Mas e quando a coluna guarda vários enums? Tipo as permissões de um usuário, os status pelos quais um pedido passou?

use App\Enums\Permissao;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;

protected function casts(): array
{
    return [
        'permissoes' => AsEnumCollection::of(Permissao::class),
    ];
}
Enter fullscreen mode Exit fullscreen mode

Salva como JSON no banco, volta como uma Collection de enums tipados. Type-safety num array inteiro, de graça.

AsStringable: string com superpoderes

Toda vez que você faz Str::of($model->titulo)->slug(), tá tratando na mão o que o cast já faz. Com AsStringable, a coluna já sai como um objeto Stringable, pronto pra encadear:

use Illuminate\Database\Eloquent\Casts\AsStringable;

protected function casts(): array
{
    return [
        'titulo' => AsStringable::class,
    ];
}
Enter fullscreen mode Exit fullscreen mode
$post->titulo->slug();      // já é fluente
$post->titulo->limit(50)->upper();
Enter fullscreen mode Exit fullscreen mode

encrypted e hashed: segurança sem boilerplate

Precisa guardar um dado sensível criptografado no banco? Tem cast pra isso, e ele criptografa na gravação e descriptografa na leitura sozinho:

protected function casts(): array
{
    return [
        'token_api' => 'encrypted',
        'dados_bancarios' => 'encrypted:array', // criptografa E serializa
    ];
}
Enter fullscreen mode Exit fullscreen mode

E pra senha, o hashed aplica o hash automático quando você atribui — chega de Hash::make() espalhado:

'password' => 'hashed',
Enter fullscreen mode Exit fullscreen mode
$user->password = 'senha123'; // já vai hasheada pro banco
Enter fullscreen mode Exit fullscreen mode

Como usar na prática

Configurações de usuário. Uma coluna preferencias JSON com AsArrayObject deixa você fazer $user->preferencias['tema'] = 'dark' e salvar, sem decode nenhum.

Roles/permissões. AsEnumCollection::of(Permissao::class) guarda a lista tipada e você compara com contains() sem medo de string errada.

Metadados de qualquer coisa. Pedido, produto, post — aquela coluna meta JSON com AsCollection te dá map, filter e amigos direto no model.

Pegadinha: quer o seu? Cria um custom cast

Nenhum dos prontos serve? Você cria o seu implementando CastsAttributes (leitura e gravação) ou CastsInboundAttributes (só na gravação, tipo um hash). Exemplo real de um cast que hasheia antes de salvar:

<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Database\Eloquent\Model;

class AsHash implements CastsInboundAttributes
{
    public function set(Model $model, string $key, mixed $value, array $attributes): string
    {
        return bcrypt($value);
    }
}
Enter fullscreen mode Exit fullscreen mode
'codigo_secreto' => AsHash::class,
Enter fullscreen mode Exit fullscreen mode

A regra de bolso: se você percebe que fica transformando o mesmo atributo do mesmo jeito toda vez, isso é um cast pedindo pra nascer.

Bônus: e agora?

Abre o model mais gordo do seu projeto e procura por json_decode, explode, Str::of($this-> ou Hash::make dentro dele. Cada um desses é forte candidato a virar cast e sumir do controller.

A doc oficial lista uns quantos que nem citei aqui — AsUri, AsFluent, immutable_datetime, decimal. Vale um passeio de 10 minutos.

Antes de você fechar a aba

Qual desses casts você não conhecia? Aposto que pelo menos um vai entrar no seu próximo commit. 😄

Me conta aí embaixo qual coluna JSON do seu projeto tá implorando por um AsArrayObject. E se o post te economizou uns json_decode, salva ele e manda pro colega que ainda trata dado na unha.

Top comments (0)