
Eloquent écrit created_at et updated_at à chaque insertion : l’erreur signifie que la table ne les possède pas. Trois correctifs selon le cas : ajoutez-les par migration avec $table->timestamps(), désactivez-les avec public $timestamps = false; sur le modèle, ou déclarez leurs vrais noms avec les constantes CREATED_AT et UPDATED_AT.
Le message tombe au premier save() ou create() sur un modèle :
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'La cause est toujours la même : Eloquent tient à jour deux colonnes de dates, created_at et updated_at, et les écrit à chaque insertion ou mise à jour. Si la table ne les possède pas, la requête échoue. Il y a trois façons de régler ça, et le bon choix dépend de la table, pas de vos préférences.
Article du parcours Développement web. Erreur reproduite et correctifs vérifiés sur Laravel 13.30.1 avec MariaDB 11.8.9.
Reconnaître l’erreur
Le libellé varie selon le moteur et l’opération, mais il désigne toujours 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_atVérifiez d’abord ce que contient réellement la table :
php artisan tinker --execute='echo implode(", ", Schema::getColumnListing("reports"));'id, titleSi created_at et updated_at manquent, passez à la suite. Si elles sont présentes sous un autre nom, allez directement à la troisième solution.
Solution 1 : ajouter les colonnes
La marche à suivre complète est décrite dans le guide des migrations Laravel.
C’est la bonne réponse dans la plupart des cas. Savoir quand une ligne a été créée et modifiée sert tôt ou tard, au débogage comme au tri. Le helper timestamps() crée les deux colonnes d’un coup, nullables :
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 migrateAprès la migration, l’insertion passe et les dates sont renseignées. Vérifié : la table contient bien id, title, created_at, updated_at et $report->created_at renvoie une instance de Illuminate\Support\Carbon, que vous pouvez formater et comparer.
Pour une table qui contient déjà des lignes, les colonnes sont créées à NULL sur l’existant, ce qui ne pose pas de problème. Si vous voulez une valeur, ajoutez-la explicitement :
$table->timestamp('created_at')->nullable()->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate();Solution 2 : désactiver les timestamps sur le modèle
Quand la table n’a pas vocation à porter ces dates, table de liaison, référentiel figé ou schéma imposé par un système tiers, dites-le au modèle :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
public $timestamps = false;
}Eloquent cesse alors de renseigner les deux colonnes, et l’insertion fonctionne. Vous pouvez confirmer l’état avec $video->usesTimestamps(), qui renvoie false.
La même désactivation peut se faire ponctuellement sur une instance, par exemple pour corriger une donnée sans modifier updated_at :
$task->timestamps = false;
$task->title = 'Titre corrigé';
$task->save(); // updated_at reste inchangéSi la table devrait avoir ces colonnes, $timestamps = false ne règle rien : il enlève une information que vous chercherez plus tard. Réservez cette solution aux tables où la date de modification n’a réellement pas de sens.
Solution 3 : les colonnes existent sous un autre nom
Sur une base héritée, les colonnes s’appellent souvent date_creation, creation_date ou modified_on. Inutile de renommer quoi que ce soit : indiquez à Eloquent les noms réels avec deux constantes.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Clip extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}Eloquent écrit désormais dans ces colonnes. Vérifié sur Laravel 13 : après Clip::create(['title' => 'Extrait']), la colonne creation_date contient bien l’horodatage, et getUpdatedAtColumn() renvoie updated_date.
Si une seule des deux dates existe, mettez l’autre à null :
const CREATED_AT = 'creation_date';
const UPDATED_AT = null; // aucune colonne de mise à jourLes accesseurs $clip->created_at et $clip->updated_at, eux, ne suivent pas ces constantes : ils renvoient null. Lisez la colonne par son vrai nom, $clip->creation_date, ou de façon générique avec $clip->{$clip->getCreatedAtColumn()}.
Quelle solution retenir
| Situation | Solution |
|---|---|
| Table applicative normale, colonnes simplement oubliées | Ajouter les colonnes par migration |
| Table pivot, référentiel figé, schéma imposé | public $timestamps = false; |
| Base héritée avec des colonnes de dates nommées autrement | Constantes CREATED_AT et UPDATED_AT |
Une seule écriture ne doit pas toucher updated_at | $model->timestamps = false; avant save() |
Erreurs fréquentes
$timestamps = false supprime une information utile au lieu de corriger le schéma. Ajoutez plutôt les colonnes.timestamps() les crée nullables, ce qui passe. Une définition NOT NULL sans valeur par défaut ferait échouer la migration.

