
O Eloquent escreve created_at e updated_at a cada inserção: o erro significa que a tabela não as tem. Três correções, consoante o caso: acrescenta-as por migração com $table->timestamps(), desativa-as com public $timestamps = false; no modelo, ou declara os seus nomes reais com as constantes CREATED_AT e UPDATED_AT.
A mensagem aparece logo no primeiro save() ou create() sobre um modelo:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'A causa é sempre a mesma: o Eloquent mantém duas colunas de datas, created_at e updated_at, e escreve nelas a cada inserção ou atualização. Se a tabela não as tiver, a consulta falha. Há três formas de resolver isto, e a escolha certa depende da tabela, não das tuas preferências.
Artigo do percurso Desenvolvimento web. Erro reproduzido e correções verificadas no Laravel 13.30.1 com MariaDB 11.8.9.
Reconhecer o erro
O texto varia consoante o motor e a operação, mas aponta sempre para updated_at ou created_at:
-- à l'insertion (MySQL / MariaDB)
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'
-- à la mise à jour
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'reports.updated_at' in 'SET'
-- sur SQLite
SQLSTATE[HY000]: General error: 1 table reports has no column named updated_atComeça por verificar o que a tabela contém de facto:
php artisan tinker --execute='echo implode(", ", Schema::getColumnListing("reports"));'id, titleSe created_at e updated_at faltarem, continua a ler. Se existirem com outro nome, salta diretamente para a terceira solução.
Solução 1: acrescentar as colunas
O procedimento completo está descrito no guia das migrações Laravel.
É a resposta certa na maioria dos casos. Saber quando uma linha foi criada e alterada acaba sempre por servir, tanto no debugging como na ordenação. O helper timestamps() cria as duas colunas de uma só vez, nullable:
php artisan make:migration add_timestamps_to_reports_table --table=reportspublic function up(): void
{
Schema::table('reports', function (Blueprint $table) {
$table->timestamps();
});
}
public function down(): void
{
Schema::table('reports', function (Blueprint $table) {
$table->dropTimestamps();
});
}php artisan migrateDepois da migração, a inserção passa e as datas ficam preenchidas. Verificado: a tabela contém mesmo id, title, created_at, updated_at e $report->created_at devolve uma instância de Illuminate\Support\Carbon, que podes formatar e comparar.
Numa tabela que já tem linhas, as colunas são criadas a NULL no existente, o que não levanta problema. Se quiseres um valor, indica-o explicitamente:
$table->timestamp('created_at')->nullable()->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate();Solução 2: desativar os timestamps no modelo
Quando a tabela não tem vocação para guardar estas datas, tabela de ligação, referencial fixo ou esquema imposto por um sistema terceiro, , diz-lo ao modelo:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
public $timestamps = false;
}O Eloquent deixa de preencher as duas colunas e a inserção funciona. Podes confirmar o estado com $video->usesTimestamps(), que devolve false.
A mesma desativação pode ser feita pontualmente numa instância, por exemplo para corrigir um dado sem alterar updated_at:
$task->timestamps = false;
$task->title = 'Titre corrigé';
$task->save(); // updated_at fica inalteradoSe a tabela devia ter estas colunas, $timestamps = false não resolve nada: retira uma informação que vais procurar mais tarde. Guarda esta solução para as tabelas onde a data de modificação realmente não faz sentido.
Solução 3: as colunas existem com outro nome
Numa base herdada, as colunas chamam-se muitas vezes date_creation, creation_date ou modified_on. Não vale a pena renomear seja o que for: indica ao Eloquent os nomes reais através de duas constantes.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Clip extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}O Eloquent passa a escrever nessas colunas. Verificado no Laravel 13: depois de Clip::create(['title' => 'Extrait']), a coluna creation_date contém mesmo a data e hora, e getUpdatedAtColumn() devolve updated_date.
Se só existir uma das duas datas, põe a outra a null:
const CREATED_AT = 'creation_date';
const UPDATED_AT = null; // nenhuma coluna de atualizaçãoOs acessores $clip->created_at e $clip->updated_at, esses, não seguem estas constantes: devolvem null. Leia a coluna pelo seu nome real, $clip->creation_date, ou de forma genérica com $clip->{$clip->getCreatedAtColumn()}.
Que solução escolher
| Situação | Solução |
|---|---|
| Tabela aplicacional normal, colunas simplesmente esquecidas | Acrescentar as colunas por migração |
| Tabela pivô, referencial fixo, esquema imposto | public $timestamps = false; |
| Base herdada com colunas de datas com outro nome | Constantes CREATED_AT e UPDATED_AT |
Uma única escrita não deve tocar em updated_at | $model->timestamps = false; antes do save() |
Erros frequentes
$timestamps = false apaga uma informação útil em vez de corrigir o esquema. Acrescenta antes as colunas.timestamps() cria-as nullable, o que passa. Uma definição NOT NULL sem valor por omissão faria falhar a migração.

