Laravel: risolvere l’errore « Unknown column ‘updated_at’ »

Introduzione L'ORM Eloquent incluso in Laravel ti offre un modo semplice di interagire con il tuo database. Semplifica tutte le operazioni CRUD (Create, read, update, and delete) e qualsiasi altra query. Come spiegato nella documentazione ufficiale di Laravel, Eloquent si aspetta che created_at e updated_at esistano

Laravel: risolvere l’errore « Unknown column ‘updated_at’ »
Risposta rapida

Eloquent scrive created_at e updated_at a ogni inserimento: l'errore dice che la tabella non le possiede. Tre correttivi, secondo il caso: aggiungile con una migration usando $table->timestamps(), disattivale con public $timestamps = false; sul modello, oppure dichiara i loro nomi reali con le costanti CREATED_AT e UPDATED_AT.

Il messaggio arriva al primo save() o create() su un modello:

code
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'

La causa è sempre la stessa: Eloquent tiene aggiornate due colonne di date, created_at e updated_at, e le scrive a ogni inserimento o aggiornamento. Se la tabella non le ha, la query fallisce. Ci sono tre modi per rimettere le cose a posto, e la scelta giusta dipende dalla tabella, non dai tuoi gusti.

Articolo del percorso Sviluppo web. Errore riprodotto e correzioni verificate su Laravel 13.30.1 con MariaDB 11.8.9.

Riconoscere l’errore

Il testo cambia a seconda del motore e dell’operazione, ma punta sempre su updated_at o created_at:

code
-- à 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_at

Comincia col guardare che cosa contiene davvero la tabella:

bash
php artisan tinker --execute='echo implode(", ", Schema::getColumnListing("reports"));'
code
id, title

Se created_at e updated_at mancano, vai avanti. Se ci sono ma con un altro nome, salta direttamente alla terza soluzione.

Soluzione 1: aggiungere le colonne

La procedura completa è descritta nella guida alle migration di Laravel.

Nella maggior parte dei casi è questa la risposta giusta. Sapere quando una riga è stata creata e modificata serve prima o poi, sia per il debug sia per gli ordinamenti. L’helper timestamps() crea le due colonne in un colpo solo, nullable:

bash
php artisan make:migration add_timestamps_to_reports_table --table=reports
database/migrations/2026_09_02_000000_add_timestamps_to_reports_table.php
public function up(): void
{
    Schema::table('reports', function (Blueprint $table) {
        $table->timestamps();
    });
}

public function down(): void
{
    Schema::table('reports', function (Blueprint $table) {
        $table->dropTimestamps();
    });
}
bash
php artisan migrate

Dopo la migration l’inserimento passa e le date sono valorizzate. Verificato: la tabella contiene id, title, created_at, updated_at e $report->created_at restituisce un’istanza di Illuminate\Support\Carbon, che puoi formattare e confrontare.

Su una tabella che contiene già delle righe, le colonne vengono create a NULL sull’esistente, e non è un problema. Se invece vuoi un valore, dichiaralo esplicitamente:

php
$table->timestamp('created_at')->nullable()->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate();

Soluzione 2: disattivare i timestamp sul modello

Quando la tabella non ha motivo di portarsi dietro queste date, tabella di collegamento, tabella di riferimento congelata, schema imposto da un sistema esterno, dillo al modello:

app/Models/Video.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Video extends Model
{
    public $timestamps = false;
}

Eloquent smette di valorizzare le due colonne e l’inserimento funziona. Puoi controllare lo stato con $video->usesTimestamps(), che restituisce false.

La stessa disattivazione si può fare una volta sola su un’istanza, per esempio per correggere un dato senza toccare updated_at:

php
$task->timestamps = false;
$task->title = 'Titre corrigé';
$task->save();   // updated_at resta invariato
Non disattivare i timestamp per zittire l’errore

Se la tabella dovrebbe avere queste colonne, $timestamps = false non risolve niente: toglie un’informazione che cercherai più avanti. Tieni questa soluzione per le tabelle in cui la data di modifica non ha davvero senso.

Soluzione 3: le colonne esistono ma con un altro nome

Su un database ereditato le colonne si chiamano spesso date_creation, creation_date o modified_on. Non serve rinominare nulla: dichiara a Eloquent i nomi reali con due costanti.

app/Models/Clip.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Clip extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'updated_date';
}

Da qui in avanti Eloquent scrive in quelle colonne. Verificato su Laravel 13: dopo Clip::create(['title' => 'Extrait']) la colonna creation_date contiene l’orario, e getUpdatedAtColumn() restituisce updated_date.

Se esiste una sola delle due date, metti l’altra a null:

php
const CREATED_AT = 'creation_date';
const UPDATED_AT = null;   // nessuna colonna di aggiornamento

Gli accessor $clip->created_at e $clip->updated_at, invece, non seguono queste costanti: restituiscono null. Leggete la colonna con il suo vero nome, $clip->creation_date, o in modo generico con $clip->{$clip->getCreatedAtColumn()}.

Quale soluzione scegliere

Situazione Soluzione
Tabella applicativa normale, colonne semplicemente dimenticate Aggiungere le colonne con una migration
Tabella pivot, tabella di riferimento congelata, schema imposto public $timestamps = false;
Database ereditato con colonne di date chiamate diversamente Costanti CREATED_AT e UPDATED_AT
Una singola scrittura non deve toccare updated_at $model->timestamps = false; prima di save()

Errori frequenti

Disattivare i timestamp per zittire l'errore Su una normale tabella applicativa, $timestamps = false elimina un'informazione utile invece di correggere lo schema. Meglio aggiungere le colonne.
Colonne NOT NULL su una tabella già piena timestamps() le crea nullable, e passa. Una definizione NOT NULL senza valore predefinito farebbe fallire la migration.
Rinominare le colonne del database ereditato Inutile e rischioso: le costanti CREATED_AT e UPDATED_AT bastano a puntare ai nomi esistenti.
Dimenticare UPDATED_AT = null Se la tabella ha solo la colonna di creazione, lasciare UPDATED_AT al valore predefinito fa ricomparire lo stesso errore in aggiornamento.

EloquentLaravelMigrationsPHP

Damien Flandrin Sviluppatore web dal 2010, creatore di Gekkode e di Email Impact. Ogni articolo è testato su un progetto reale prima della pubblicazione. Contatti
Newsletter

I nuovi test, tutorial e progetti, via e-mail.

Test riproducibili, codice versionato, risultati datati. Mai spam.