Download the PHP package gsebastiao/laravel-auditable without Composer
On this page you can find all versions of the php package gsebastiao/laravel-auditable. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download gsebastiao/laravel-auditable
More information about gsebastiao/laravel-auditable
Files in gsebastiao/laravel-auditable
Package laravel-auditable
Short Description Auditoria Eloquent com nomes legíveis (resolveMap), operações multi-tabela agrupadas por batch, auditoria de falhas, restauro de registros apagados e multitenancy opcional.
License MIT
Informations about the package laravel-auditable
Laravel Auditable
Guarde automaticamente quem mudou o quê e quando nos seus models
Eloquent — com nomes legíveis (Status: Ativo → Bloqueado) em vez de ids
soltos (status_id: 1 → 3).
Você adiciona uma linha ao model e, a partir daí, cada create(), update()
e delete() fica registrado. Com o histórico gravado, dá para montar telas
como esta:
| Quando | Quem | O quê | Alterações |
|---|---|---|---|
| 11/07/2026 09:00 | João Santos | updated | Preço: 20 → 25 · Status: Ativo → Bloqueado |
| 10/07/2026 14:30 | Maria Pereira | created | Nome: Café · Preço: 20 · Status: Ativo |
O que o pacote faz por você
- Registra criação, alteração e exclusão sozinho (e restauração, com SoftDeletes).
- Traduz chaves estrangeiras em nomes legíveis (
resolveMap). - Agrupa várias gravações de uma mesma ação do usuário numa só operação — inclusive o que os jobs em fila gravarem depois.
- Audita updates e deletes em massa, um registro de cada vez no histórico.
- Registra falhas com detalhes técnicos que só o desenvolvedor vê.
- Recria registros apagados a partir da auditoria.
- Mostra "criado por / alterado por" numa listagem com uma única consulta.
- Traz um modal de histórico pronto em JavaScript (opcional).
- Funciona com multitenancy (opcional).
Sumário
- Requisitos
- Instalação
- Primeiros passos
- O que é (e o que não é) auditado automaticamente
- Nomes legíveis com
resolveMap - Escolher o que auditar
- Quem fez a alteração
- Agrupar uma operação (batch)
- Registrar ações próprias:
auditAction() - Personalizar a entrada automática:
audit() - Auditar sem model:
Audit::for() - Update e delete em massa
- Consultar o histórico
- Mostrar o histórico numa tela Blade
- Página de pesquisa de auditoria (receita)
- Colunas "criado por / alterado por" numa listagem
- Modal de histórico pronto (JavaScript, opcional)
- Restaurar um registro apagado
- Registrar falhas
- Multitenancy (opcional)
- Desligar a auditoria
- Configuração completa
- Personalização avançada
- Problemas comuns
- Referência rápida
Requisitos
- PHP 8.2 ou mais recente
- Laravel 11.4+, 12 ou 13
- Um banco suportado pelo Laravel (MySQL, MariaDB, PostgreSQL, SQLite ou SQL Server)
Instalação
Só dois comandos são necessários: composer require e php artisan migrate.
Os dois vendor:publish no meio são opcionais. Rode na pasta do seu projeto:
Este comando é opcional — pode pular. Ele só copia o arquivo de configuração do pacote para o seu projeto (
config/auditable.php), para você poder editá-lo. O pacote funciona igual sem ele, com os valores padrão. E se um dia quiser mudar alguma opção (nome da tabela, banco, multitenancy...), também não precisa dele: basta acrescentar variáveisAUDITABLE_*ao.env. Veja todas as opções e variáveis em Configuração completa.Este comando também é opcional — a maioria dos projetos pode pular. O pacote já traz a migration da tabela de auditoria e o
php artisan migrate(o próximo comando) a executa sozinho, sem encher a pastadatabase/migrationsdo seu projeto. Só publique se quiser editar a migration antes de rodá-la — o caso típico é quando os ids dos seus models (ou dos usuários) são UUID ou ULID. Veja Meus ids são UUID.
O que cada um faz:
composer requireinstala o pacote. O Laravel o encontra sozinho; não precisa registrar nada.--tag=auditable-config(opcional) criaconfig/auditable.php, com todas as opções comentadas. Veja a explicação acima e a Configuração completa.--tag=auditable-migrations(opcional) copia a migration paradatabase/migrations/, para você poder editá-la. Veja a explicação acima.migratecria a tabela (chamadaaudit_table, por padrão).
Vai mudar o nome da tabela (
AUDITABLE_TABLE) ou ligar o multitenancy? Faça-o antes domigrate.
Primeiros passos
1. Adicione o trait Auditable ao model
Pronto. Não há mais nada a configurar.
2. Use o model como sempre usou
Cada uma dessas três linhas gravou uma linha na tabela de auditoria.
3. Veja o histórico
Para experimentar rapidamente sem criar tela nenhuma, use o Tinker:
O que fica gravado
Depois do exemplo acima, a tabela de auditoria tem estas três linhas (simplificando):
| event | changes |
|---|---|
created |
{"nome": "Café", "preco": 20, "status_id": 1, "id": 1} |
updated |
{"preco": {"old": 20, "new": 25}} |
deleted |
{"nome": "Café", "preco": 25, "status_id": 1, "id": 1} |
Repare:
- No
createde nodeletedfica um retrato do registro (não há "antes" e "depois"). - No
updatedfica só o que mudou, no formato{"old": ..., "new": ...}. password,remember_token,created_ateupdated_atdo model não entram emchanges(a própria auditoria já tem a sua data).
E estas são todas as colunas da tabela:
| Coluna | O que guarda |
|---|---|
id |
Número da auditoria |
batch |
Id da operação — as auditorias de uma mesma ação do usuário têm o mesmo batch (saiba mais) |
subject_type |
Qual model foi alterado (ex.: App\Models\Produto) |
subject_id |
O id do registro alterado |
event |
created, updated, deleted, restored ou um nome seu (ex.: aprovado) |
changes |
O que mudou, pronto para ler |
debug_info |
Detalhes técnicos: dados de falhas e o retrato usado para restaurar um registro apagado |
created_by |
Id do usuário que estava logado (vazio = ação do sistema) |
created_at / updated_at |
Quando aconteceu |
O que é (e o que não é) auditado automaticamente
A auditoria automática funciona pelos eventos do Eloquent. Por isso:
| Isto é auditado | Isto não é auditado |
|---|---|
Produto::create([...]) |
Produto::where(...)->update([...]) (update em massa) |
$produto->update([...]) |
Produto::where(...)->delete() (delete em massa) |
$produto->save() |
DB::table('produtos')->insert/update/delete(...) |
$produto->delete() |
Produto::insert([...]) e Produto::upsert(...) |
$produto->increment('estoque') |
$produto->saveQuietly() e código dentro de Model::withoutEvents() |
$produto->restore() (SoftDeletes) |
Para updates e deletes em massa, use as versões auditadas — a sintaxe é a mesma, e cada registro afetado ganha a sua auditoria (veja Update e delete em massa):
Para o resto da coluna da direita (DB::table(), tabelas sem model),
registre você mesmo com Audit::for().
Nomes legíveis com resolveMap
Sem ajuda, uma chave estrangeira aparece assim no histórico:
Ninguém sabe o que é o status 1 ou o 3. Com o resolveMap, você diz ao pacote
onde buscar o nome:
Leia a linha do resolveMap assim: "o campo status_id aparece com o nome
Status; o texto está na tabela status, coluna nome". O resultado:
O id original continua guardado (id) ao lado do texto (label).
As três formas
ResolveMap::direct() — o caso mais comum: o nome está na tabela para
onde a chave aponta.
ResolveMap::join() — o nome está noutra tabela, a mais de uma ligação
de distância. Exemplo: o campo guarda estado_id, mas você quer mostrar o
país do estado:
ResolveMap::alias() — não consulta nada, só troca o nome do campo:
Bom saber
- As consultas são feitas no mesmo banco do model, e cada valor é consultado no máximo uma vez por requisição.
- Se a linha procurada não existir (ex.: o status foi apagado), o
labelficanulle oidcontinua lá.
Escolher o que auditar
Tudo é configurado no método getAuditOptions() do model. Os métodos podem ser
encadeados:
| Método | Padrão | Para que serve |
|---|---|---|
events([...]) |
created, updated, deleted |
Quais eventos automáticos registrar. Acrescente restored se o model usa SoftDeletes. |
only([...]) |
todos os campos | Registrar só estes campos. |
except([...]) |
password, remember_token |
Não mostrar estes campos em changes. O que você passar soma-se ao padrão. |
neverSnapshot([...]) |
password, remember_token |
Nunca guardar estes campos em lugar nenhum (nem em changes, nem no retrato de restauro). Use para segredos. |
onlyDirty(false) |
ligado | No updated, mostrar também os campos que não mudaram. |
logEmpty() |
desligado | Gravar a auditoria mesmo quando nada mudou. |
logTimestamps() |
desligado | Mostrar created_at e updated_at do model em changes. |
fullSnapshotOnDelete(false) |
ligado | Não guardar o retrato que permite restaurar um registro apagado. |
resolveMap([...]) |
vazio | Nomes legíveis. |
except() ou neverSnapshot()?
except()é para campos que só poluem o histórico. Eles continuam no retrato de restauro, para que o registro volte inteiro se for apagado.neverSnapshot()é para segredos (tokens, chaves de API): não ficam guardados em lugar nenhum da auditoria.
Campos criptografados (cast encrypted) aparecem como ******** em
changes: você vê que o campo mudou, mas o valor nunca vai para a auditoria
em texto claro.
Quem fez a alteração
O id do usuário logado vai sozinho para a coluna created_by. Para ler:
Quando não há ninguém logado (comandos artisan, filas, agendamentos,
webhooks), created_by fica vazio. Se preferir gravar sempre um usuário (por
exemplo, um usuário chamado "Sistema" com id 1), coloque no .env:
Usa outro guard de login (ex.: admin)? Coloque no .env:
Num job da fila ninguém está logado, mas o pacote resolve isso sozinho: a
job leva consigo o usuário que estava logado quando ela foi despachada, e as
auditorias feitas por ela ficam em nome dele. Vale também para listeners,
notificações e e-mails em fila. (Para desligar: 'propagate_user' => false
na seção queue de config/auditable.php.)
Para gravar em nome de outra pessoa, use audit()
logo depois da gravação:
Agrupar uma operação (batch)
Uma única ação do usuário costuma mexer em várias tabelas. Por exemplo, "finalizar pedido" cria o pedido, cria os itens e baixa o estoque. Cada gravação tem a sua auditoria, mas é útil saber que todas fazem parte da mesma operação.
É para isso que serve a coluna batch. Tudo o que for gravado dentro de
Audit::transaction() recebe o mesmo batch:
Audit::transaction() faz duas coisas ao mesmo tempo:
- Abre uma transação de banco: se qualquer linha lançar um erro, nada é gravado — nem os dados, nem as auditorias.
- Dá o mesmo batch a todas as auditorias lá de dentro.
Se você só quer agrupar, sem transação, use Audit::batch(fn () => ...).
E se o seu código já usa DB::transaction(), pode combinar os dois, em
qualquer ordem:
Ver a operação inteira
Jobs em fila continuam a operação
Não é preciso fazer nada: um job despachado durante uma operação continua
nessa operação. Tudo o que ela gravar recebe o mesmo batch — e, como visto em
Quem fez a alteração, o mesmo autor.
Um job despachado fora de uma operação começa a sua própria, como uma
requisição normal. Funciona com qualquer driver de fila (database, redis,
sqs, sync...) e também com listeners, notificações e e-mails em fila. Para
desligar: 'propagate_batch' => false na seção queue da config.
Controle manual, se precisar:
Audit::useBatch($batch, fn () => ...)grava no batch indicado tudo o que estiver dentro do callback.
Registrar ações próprias: auditAction()
Nem tudo o que importa é um create, update ou delete. Para registrar
qualquer outro acontecimento, dê-lhe um nome:
O segundo parâmetro (opcional) vai para changes. auditAction() sempre
cria uma linha nova.
Personalizar a entrada automática: audit()
Às vezes você quer que a auditoria automática saia diferente: com outro
autor, outro nome de evento ou informações extra. É para isso que existe
audit():
As regras são simples:
audit()substitui a entrada automática que este objeto acabou de gravar. Não cria uma segunda linha.- Se o objeto ainda não gravou nada (por exemplo, acabou de ser carregado com
find()),audit()cria uma entrada nova. - O que você não passar continua automático.
- É seguro com muitos usuários ao mesmo tempo:
audit()só mexe na entrada do objeto em que foi chamado, nunca na de outra pessoa ou de outro registro.
audit() ou auditAction()?
audit() |
auditAction() |
|
|---|---|---|
| Cria uma linha nova? | Não: ajusta a entrada automática que o objeto acabou de gravar | Sempre |
| Para quê | Mudar autor, data, dados ou nome do evento da entrada automática | Registrar um acontecimento a mais |
Todos os parâmetros de audit() (todos opcionais, use por nome):
| Parâmetro | O que muda |
|---|---|
event |
O nome do evento gravado |
changes |
O conteúdo de changes (array) |
debugInfo |
O conteúdo de debug_info (array) |
createdBy |
O autor |
createdAt / updatedAt |
As datas ('Y-m-d H:i:s') |
batch |
A operação |
subjectType / subjectId |
Grava a entrada em nome de outro registro |
tenantId |
O tenant (só com multitenancy ligado) |
Auditar sem model: Audit::for()
Use quando a gravação não passa pelo Eloquent: DB::table(), updates em
massa ou tabelas que não têm model.
- Os três parâmetros são: o tipo (um nome livre, como
'pessoa', ou a classe de um model), o id e o evento. - Nada é gravado antes do
->save(). changes()aceita o formato de alteração (['campo' => ['old' => ..., 'new' => ...]]) ou um retrato (['campo' => valor]). O pacote reconhece qual é.- Se o tipo for a classe de um model (
Produto::class), as opções dogetAuditOptions()dele (como oresolveMap) são aproveitadas. - Também pode encadear:
except(),only(),events(),onlyDirty(),logEmpty(),resolveMap(),debugInfo(),batch(),createdBy(),tenantId(),createdAt()eupdatedAt().
Para updates e deletes em massa num model, prefira
auditedUpdate()eauditedDelete()(veja Update e delete em massa): calculam o "antes" e o "depois" sozinhos.
Audit::for() ou DB::table()->audit()? Use Audit::for(). A forma
DB::table('x')->audit(...) existe por compatibilidade e faz o mesmo — a
tabela do DB::table() não é usada para nada. A única diferença é a conexão
onde o resolveMap procura os nomes: a do DB::connection(...) usado. Isso
só importa numa tabela sem model que esteja numa conexão que não é a padrão:
Update e delete em massa
Um update() ou delete() em massa não gera auditoria (o Laravel não
dispara os eventos dos models). Use as versões auditadas — a sintaxe é a
mesma:
Cada registro afetado ganha a sua auditoria, com as mesmas regras de
getAuditOptions() (resolveMap, except, campos mascarados...), e todas
ficam na mesma operação. Os dois métodos devolvem quantos registros foram
afetados, como o update() e o delete() normais.
Bom saber
- O model precisa do trait
Auditable. - Trabalha em lotes de 500 registros. Em cada lote: lê os valores antigos
(travando as linhas), faz um único update e grava as auditorias de uma vez.
Se um lote falhar, ele volta atrás inteiro — dados e auditorias. Para outro
tamanho:
->auditedUpdate([...], chunkSize: 1000). - Aceita valores calculados pelo banco:
->auditedUpdate(['preco' => DB::raw('preco * 1.1')]). - Registros que já tinham o valor novo não ganham auditoria.
- Em models com SoftDeletes,
auditedDelete()manda para a lixeira (como odelete()). Em models sem SoftDeletes, o retrato de restauro é gravado erestore()funciona como num delete normal. - Tal como o
update()em massa normal, não dispara os eventos do Eloquent: observers e listeners deupdated/deletednão correm. - É mais lento que o
update()cru (lê e audita cada registro), mas muito mais rápido do que carregar e salvar os models um a um. - Não aceita
limit()/offset(): filtre comwhere().
Consultar o histórico
Filtros disponíveis (todos encadeáveis, e ainda funcionam where(),
latest(), paginate() etc.):
| Filtro | O que traz |
|---|---|
action('updated') ou action([...]) |
Pelo nome do evento |
byUser($id) |
Feitas por um usuário |
inBatch($batch) |
De uma operação |
failures() |
Só as falhas |
forRecord(Produto::class, $id) |
De um registro |
filter([...]) |
Vários de uma vez — para telas de pesquisa (veja a receita) |
Em cada auditoria:
$audit->subjectsó funciona quandosubject_typeé um model. Numa auditoria gravada com um nome livre (Audit::for('pessoa', ...)), usesubject_typeesubject_iddiretamente.
Audit::(deGsebastiao\Auditable\Audit) aceita qualquer consulta do model de auditoria. Se preferir usar o model diretamente, ele éGsebastiao\Auditable\Models\Audit.
Evite consultas repetidas: ao listar auditorias com o nome do usuário,
carregue os usuários de uma vez com with('user').
Mostrar o histórico numa tela Blade
No controller:
Na view:
Quer um nome mais bonito para um campo (ex.: "Preço" em vez de "Preco")? Use
ResolveMap::alias('Preço') no resolveMap.
Página de pesquisa de auditoria (receita)
Uma página para consultar todas as auditorias do sistema, com filtros. O
pacote não traz esta tela pronta — cada sistema tem o seu visual e as suas
permissões —, mas com o filter() ela fica curta. Copie e adapte.
1. A permissão, no boot() do AppServiceProvider:
A auditoria guarda dados de todos os usuários e detalhes técnicos das falhas: esta página é só para administradores.
2. A rota, em routes/web.php:
3. A view, em resources/views/auditoria/index.blade.php:
As chaves que o filter() entende:
| Chave | Filtra por |
|---|---|
user |
Id de quem fez |
event |
Evento (um texto, ou uma lista: event[]=created&event[]=updated) |
model |
Classe do model, ou o nome livre usado no Audit::for() |
id |
Id do registro (use junto com model) |
batch |
Id da operação |
from / to |
Período (datas inclusive) |
failures |
1 = só as falhas |
Campos vazios e chaves desconhecidas (como page e _token) são ignorados, e
datas inválidas também — por isso dá para passar $request->all() direto.
Colunas "criado por / alterado por" numa listagem
Numa listagem com muitos registros, buscar a auditoria de cada linha seria
lento. O AuditColumnJoiner acrescenta essas colunas à mesma consulta da
listagem:
Cada produto ganha quatro colunas:
audit_created_*vem da primeira auditoriacreated;audit_updated_*vem da mais recenteupdated. Registros nunca alterados ficam comnull.- O nome do usuário é encurtado para "Primeiro Último" ("Maria da Silva
Pereira" → "Maria Pereira") e a data sai como
10/07/2026 14:30:00. - Funciona também com
DB::table('produtos').
Opções (todas por nome):
Requisito: a tabela de auditoria precisa estar no mesmo banco da listagem. Não funciona se
AUDITABLE_CONNECTIONapontar para outro banco.
Modal de histórico pronto (JavaScript, opcional)
Esta seção é opcional. Tudo o que foi explicado até aqui funciona sem ela.
O pacote traz um arquivo JavaScript que abre um modal (janela por cima da página) com o histórico de um registro. Não depende de jQuery, Bootstrap nem de nenhuma outra biblioteca, e não precisa de HTML nenhum na página.
Há dois modais:
| Modal | Para quem | O que mostra |
|---|---|---|
GaAudit.full |
Administradores/auditores | Histórico completo, agrupado por operação, com busca, filtro e paginação |
GaAudit.simple |
Qualquer usuário | Lista simples: o quê, quem e quando |
Passo 1 — Copie o arquivo para public/
O arquivo vai para public/assets/js/audit-table.init.js. Para outra pasta,
use --path=js/auditoria. Depois de atualizar o pacote, rode de novo com
--force para pegar a versão nova.
Passo 2 — Inclua no layout
No layout Blade (ex.: resources/views/layouts/app.blade.php):
A tag csrf-token é obrigatória: sem ela o Laravel recusa o pedido do
modal (erro 419).
Passo 3 — Crie as rotas
Em routes/web.php. O AuditWidget já devolve o JSON no formato que o modal
espera:
Quem pode ver o histórico completo? Proteja a rota com uma permissão. Por exemplo, acrescente
->can('ver-auditoria')depois doRoute::post(...)e defina a regra noboot()doAppServiceProvider:
Passo 4 — Abra o modal
Sem escrever JavaScript, com atributos data-*:
Troque full por simple (e a URL) para o modal simples. O título é
opcional. Se preferir chamar pelo seu próprio JavaScript:
Personalizar o visual
As cores usam variáveis CSS. Sobrescreva no CSS do seu site:
Outras variáveis: --ga-audit-bg, --ga-audit-text, --ga-audit-border,
--ga-audit-success, --ga-audit-danger e --ga-audit-font.
Para trocar os ícones, defina-os depois de incluir o script:
Os textos da interface estão em português. Para outro idioma, edite o arquivo
publicado em public/ (é JavaScript comum, sem compilação).
Restaurar um registro apagado
Quando um registro é apagado, a auditoria deleted guarda um retrato completo
dele. Com esse retrato, o registro pode ser recriado:
O registro volta exatamente como estava no banco: o mesmo id, as mesmas datas e os mesmos valores (inclusive JSON e campos criptografados).
Variações:
Bom saber
- Só auditorias
deletedpodem ser restauradas. - Se já existir um registro com o mesmo id,
restore()lança um erro que explica o que fazer. - Em models com SoftDeletes, o registro apagado continua na tabela (na
"lixeira"): use o
$produto->restore()do próprio Laravel. O restauro pela auditoria serve para exclusões definitivas (forceDelete()ou models sem SoftDeletes). - O registro recriado gera uma auditoria
creatednova, então o histórico mostra "apagado" e depois "criado".
Registrar falhas
Quando algo dá errado, você pode registrar a falha. O usuário vê uma mensagem simples; os detalhes técnicos ficam guardados só para o desenvolvedor.
Parâmetros: o nome da ação, a exceção, dados extra para investigar (opcional) e a mensagem para o usuário (opcional; o padrão é "A operação falhou.").
O que é gravado:
changes— só a mensagem:{"message": "Não foi possível concluir o pagamento."}. Pode ser mostrada ao usuário.debug_info— para o desenvolvedor: classe e mensagem da exceção, arquivo e linha, as primeiras linhas do stack trace, os dados extra, a rota e o IP da requisição, e o nome do banco. Numa falha de SQL, a query é guardada com?no lugar dos valores. Senhas e endereço do banco nunca são gravados.
Para consultar:
Cuidado com transações. Se você registrar a falha dentro de um
Audit::transaction()e relançar o erro, a própria auditoria da falha é desfeita junto com a transação. Registre-a fora:
Multitenancy (opcional)
Multitenancy é quando um único sistema atende vários clientes (empresas, escolas, lojas), e cada um só pode ver os seus dados. Se o seu sistema não é assim, pule esta seção.
Caso A — cada cliente tem o seu próprio banco
Não é preciso configurar nada no pacote. A auditoria de cada cliente vai para
o banco dele, porque usa a mesma conexão da aplicação. É o caso típico de
pacotes como stancl/tenancy e spatie/laravel-multitenancy no modo
multi-banco.
Só não se esqueça de que a tabela de auditoria precisa existir em cada
banco de cliente. Para isso, publique a migration
(php artisan vendor:publish --tag=auditable-migrations) e coloque a cópia
junto das migrations dos tenants (no stancl/tenancy, a pasta
database/migrations/tenant). O php artisan migrate normal continua a criar
também a tabela no banco central, com a migration do pacote.
Caso B — um banco só, com uma coluna tenant_id em cada tabela
1. Ligue o modo tenant antes de rodar a migration (é ele que cria a
coluna tenant_id na tabela de auditoria):
Se a tabela de auditoria já existe, crie uma migration para acrescentar a coluna:
2. Diga ao pacote quem é o tenant atual, no boot() do
app/Providers/AppServiceProvider.php:
O pacote só pergunta qual é o tenant; quem decide é a sua aplicação.
3. Nos seus models com tenant_id, acrescente BelongsToTenant:
Com isso, Produto::all() só traz os produtos do tenant atual e
Produto::create() preenche o tenant_id sozinho. As auditorias também
passam a ser gravadas e filtradas por tenant.
Ver todos os tenants (relatórios internos, tarefas de manutenção):
Bom saber
- Sem tenant identificado (ex.: um comando no terminal), as consultas
não são filtradas. Para que não mostrem nada nesse caso, ligue
'strict' => trueemconfig/auditable.php(seçãotenant). - A coluna tem outro nome? Mude
'column'emconfig/auditable.php. Se só um model for diferente, defina nelepublic function tenantColumn(): string { return 'empresa_id'; }. - Jobs em fila: no worker ninguém está logado, então um resolver baseado em
auth()não sabe o tenant — e, sem tenant, as consultas não são filtradas. Para a job ficar no tenant de quem a despachou, ligue'propagate_tenant' => truena seçãoqueuedeconfig/auditable.php. - Não coloque
BelongsToTenantno modelUserse o seu resolver usaauth()->user(): para descobrir o tenant, o Laravel precisaria carregar o usuário, que por sua vez precisaria do tenant — um ciclo sem fim. -
Prefere configurar tudo em
config/auditable.php? Crie uma classe com o método__invoke()e indique o nome dela em'resolver':Não coloque uma função anônima (
fn () => ...) direto na config: isso impede ophp artisan config:cache.
Desligar a auditoria
Para um model específico, deixe a lista de eventos vazia:
AuditOptions::defaults()->events([]).
Configuração completa
Todas as opções de config/auditable.php. Os padrões funcionam; mude só o
que precisar.
Configurar sem publicar o config
Não é preciso publicar config/auditable.php. O pacote carrega a sua
configuração sozinho, e cada opção pode ser mudada por uma variável no .env.
Só publique o config se quiser editar o arquivo diretamente.
Variável do .env |
Opção | Padrão | O que faz |
|---|---|---|---|
AUDITABLE_ENABLED |
enabled |
true |
Liga/desliga a auditoria no sistema inteiro |
AUDITABLE_TABLE |
table |
audit_table |
Nome da tabela de auditoria (mude antes da migration) |
AUDITABLE_CONNECTION |
connection |
vazio | Conexão de banco da auditoria; vazio = a da aplicação |
AUDITABLE_MODEL |
model |
Gsebastiao\Auditable\Models\Audit |
Model da tabela de auditoria (estender) |
AUDITABLE_AUTH_GUARD |
auth_guard |
vazio | Guard de onde vem o usuário logado; vazio = o padrão |
AUDITABLE_USER_MODEL |
user_model |
vazio | Model dos usuários para $audit->user; vazio = o do config/auth.php |
AUDITABLE_USER_TABLE |
users_table |
users |
Tabela de usuários usada pelo AuditColumnJoiner |
AUDITABLE_DEFAULT_CREATED_BY |
default_created_by |
vazio | Usuário gravado quando ninguém está logado |
AUDITABLE_COLUMN_PREFIX |
column_prefix |
audit_ |
Prefixo das colunas do AuditColumnJoiner |
AUDITABLE_TENANT_ENABLED |
tenant.enabled |
false |
Liga o multitenancy por coluna |
AUDITABLE_TENANT_COLUMN |
tenant.column |
tenant_id |
Nome da coluna de tenant |
AUDITABLE_TENANT_RESOLVER |
tenant.resolver |
vazio | Classe invocável que devolve o tenant atual |
AUDITABLE_TENANT_STRICT |
tenant.strict |
false |
Sem tenant identificado: false vê tudo, true não vê nada |
AUDITABLE_PROPAGATE_BATCH |
queue.propagate_batch |
true |
Jobs despachadas durante uma operação continuam-na |
AUDITABLE_PROPAGATE_USER |
queue.propagate_user |
true |
As auditorias de uma job ficam em nome de quem a despachou |
AUDITABLE_PROPAGATE_TENANT |
queue.propagate_tenant |
false |
A job usa o tenant de quem a despachou, se o resolver não identificar nenhum |
AUDITABLE_PUBLISH_PATH |
js.publish_path |
assets/js |
Pasta (dentro de public/) do widget JS |
AUDITABLE_DEBUG_DB |
debug.include_database |
true |
Grava conexão, driver e nome do banco nas falhas |
Exemplo de .env — coloque só as linhas que quiser mudar (aqui, os
valores padrão, mais um exemplo de conexão própria):
Não deixe uma variável com o valor em branco (
AUDITABLE_CONNECTION=): o Laravel a entende como texto vazio, não como "sem valor". Para voltar ao padrão, apague a linha. Comphp artisan config:cache, rode-o de novo depois de mudar o.env.
Guardar a auditoria noutro banco. Crie a conexão em config/database.php
e indique-a no .env antes de rodar a migration:
Nesse caso, Audit::transaction() desfaz os dois bancos juntos em caso de
erro, mas o AuditColumnJoiner deixa de funcionar (ele precisa do mesmo
banco).
Personalização avançada
Nada disto é necessário para o uso normal.
O seu próprio model de auditoria
Para acrescentar relações ou métodos às auditorias:
Dentro da sua classe, leia a coluna com
$this->getAttribute('changes'):$this->changesé uma propriedade interna do Eloquent com o mesmo nome.
Gravar em outro lugar (fila, serviço externo)
Implemente Gsebastiao\Auditable\Contracts\AuditRepository (métodos
persist, replace e forget — a documentação de cada um está na
interface) e registre no AppServiceProvider:
Se o destino não tiver ids, persist() pode devolver null; nesse caso,
audit() passa a criar sempre uma linha nova.
Para que auditedUpdate()/auditedDelete() gravem em bloco no seu destino,
implemente Gsebastiao\Auditable\Contracts\BulkAuditRepository (que acrescenta
o método persistMany). Sem ela, o pacote grava linha a linha com persist().
Outras peças substituíveis
| Interface | Responsável por | Padrão |
|---|---|---|
Contracts\ContextResolver |
Descobrir o usuário e o tenant atuais | Lê o auth() e o resolver de tenant |
Contracts\BatchIdGenerator |
Gerar o id das operações | ULID (26 caracteres) |
Troque do mesmo jeito: $this->app->bind(Interface::class, SuaClasse::class);.
Problemas comuns
Não foi gravada nenhuma auditoria
Confira, nesta ordem:
- O model tem
use Auditable;? - A gravação passa pelo Eloquent? Updates e deletes em massa (use
auditedUpdate()/auditedDelete()),DB::table(),insert(),upsert()esaveQuietly()não geram auditoria (veja o que é auditado). - Algum valor mudou de verdade? Um
update()com os mesmos valores não grava nada — o Laravel nem chega a salvar. E se só mudaram campos ignorados (updated_atou os deexcept()), não sobra nada para registrar: uselogEmpty()para gravar mesmo assim. - O evento está em
events()? E a auditoria não está desligada (AUDITABLE_ENABLED,Audit::withoutAuditing())? - A gravação aconteceu dentro de uma transação que foi desfeita?
- Usa
php artisan config:cache? Depois de mudar o.env, rode-o de novo.
created_by está vazio
Ninguém estava logado quando a gravação aconteceu — por exemplo, num comando
artisan ou numa tarefa agendada. (Jobs despachadas por um usuário logado
já herdam o autor sozinhas.) Defina AUDITABLE_DEFAULT_CREATED_BY ou use
audit(createdBy: ...). Veja Quem fez a alteração.
O modal mostra "Não foi possível carregar"
- Erro 419: falta a tag
<meta name="csrf-token">no layout. - Erro 404: a URL em
data-ga-audit-urlnão bate com a rota (confira comphp artisan route:list). - Erro 401/403: a rota exige login ou permissão.
- Erro 500: veja
storage/logs/laravel.log.
Na aba Rede (Network) das ferramentas do navegador (F12) aparece o código de erro do pedido.
Meus ids são UUID ou ULID
Antes de rodar php artisan migrate, publique a migration
(php artisan vendor:publish --tag=auditable-migrations), abra-a e ajuste as
três linhas do topo:
Valores aceitos: 'integer', 'uuid', 'ulid' e 'string'. Se a tabela já
existe, faça uma migration que altere as colunas subject_id, created_by
(e tenant_id).
Aparece ******** no lugar de um valor
O campo usa o cast encrypted. É de propósito: o valor nunca vai para a
auditoria em texto claro.
O label do resolveMap vem null
Não foi encontrada a linha procurada: confira o nome da tabela e da coluna no
ResolveMap::direct() e se o registro ainda existe.
Erro ao usar php artisan config:cache
Há uma função anônima (fn () => ...) em config/auditable.php. Troque por
uma classe ou use Audit::resolveTenantUsing() (veja Multitenancy).
Referência rápida
Um resumo de tudo, para consulta (não é um arquivo para copiar inteiro):
Testes do pacote
Atualizando de uma versão anterior
Veja o CHANGELOG: ele lista o que mudou e o que ajustar no seu código.
Licença
MIT. Veja o arquivo LICENSE.
All versions of laravel-auditable with dependencies
illuminate/console Version ^11.4|^12.0|^13.0
illuminate/contracts Version ^11.4|^12.0|^13.0
illuminate/database Version ^11.4|^12.0|^13.0
illuminate/filesystem Version ^11.4|^12.0|^13.0
illuminate/http Version ^11.4|^12.0|^13.0
illuminate/support Version ^11.4|^12.0|^13.0