
Eloquent zapisuje created_at i updated_at przy każdym wstawieniu: błąd oznacza, że tabela ich nie ma. Trzy poprawki zależnie od sytuacji: dodaj kolumny migracją przez $table->timestamps(), wyłącz je w modelu przez public $timestamps = false; albo zadeklaruj ich prawdziwe nazwy stałymi CREATED_AT i UPDATED_AT.
Komunikat pojawia się przy pierwszym save() albo create() na modelu:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'Przyczyna jest zawsze ta sama: Eloquent prowadzi dwie kolumny z datami, created_at i updated_at, i zapisuje je przy każdym wstawieniu oraz każdej aktualizacji. Jeśli tabela ich nie ma, zapytanie kończy się błędem. Sposoby na to są trzy, a właściwy zależy od tabeli, nie od twoich upodobań.
Artykuł ze ścieżki Programowanie webowe. Błąd odtworzony, a poprawki sprawdzone na Laravelu 13.30.1 z MariaDB 11.8.9.
Jak rozpoznać błąd
Treść komunikatu zależy od silnika i od operacji, ale zawsze wskazuje na updated_at albo 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_atNajpierw sprawdź, co tabela naprawdę zawiera:
php artisan tinker --execute='echo implode(", ", Schema::getColumnListing("reports"));'id, titleJeśli brakuje created_at i updated_at, czytaj dalej. Jeśli są, ale pod inną nazwą, przejdź od razu do trzeciego rozwiązania.
Rozwiązanie 1: dodanie kolumn
Całą procedurę opisuje przewodnik po migracjach w Laravelu.
W większości przypadków to właśnie jest właściwa odpowiedź. Wiedza o tym, kiedy wiersz powstał i kiedy go zmieniono, prędzej czy później się przydaje, przy debugowaniu tak samo jak przy sortowaniu. Helper timestamps() tworzy obie kolumny naraz, jako nullowalne:
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 migratePo migracji wstawienie przechodzi, a daty są uzupełniane. Sprawdzone: tabela zawiera id, title, created_at, updated_at, a $report->created_at zwraca instancję Illuminate\Support\Carbon, którą możesz formatować i porównywać.
W tabeli, w której są już wiersze, kolumny powstaną na istniejących danych z wartością NULL, i nic złego się przez to nie dzieje. Jeśli chcesz mieć wartość, dopisz ją wprost:
$table->timestamp('created_at')->nullable()->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate();Rozwiązanie 2: wyłączenie timestampów w modelu
Kiedy tabela z założenia nie ma nosić tych dat, tabela łącząca, zamrożony słownik, schemat narzucony przez zewnętrzny system, powiedz o tym modelowi:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
public $timestamps = false;
}Eloquent przestaje wtedy uzupełniać obie kolumny i wstawienie działa. Stan potwierdzisz wywołaniem $video->usesTimestamps(), które zwraca false.
To samo da się wyłączyć doraźnie, na pojedynczej instancji, na przykład żeby poprawić dane bez ruszania updated_at:
$task->timestamps = false;
$task->title = 'Titre corrigé';
$task->save(); // updated_at zostaje bez zmianJeśli tabela powinna mieć te kolumny, $timestamps = false niczego nie naprawia: usuwa informację, której później będziesz szukać. Zostaw to rozwiązanie dla tabel, w których data modyfikacji naprawdę nie ma sensu.
Rozwiązanie 3: kolumny istnieją pod inną nazwą
W odziedziczonej bazie kolumny nazywają się często date_creation, creation_date albo modified_on. Nie ma po co niczego zmieniać: wskaż Eloquentowi prawdziwe nazwy dwiema stałymi.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Clip extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}Eloquent zapisuje odtąd do tych kolumn. Sprawdzone na Laravelu 13: po Clip::create(['title' => 'Extrait']) kolumna creation_date zawiera znacznik czasu, a getUpdatedAtColumn() zwraca updated_date.
Jeśli istnieje tylko jedna z dwóch dat, drugą ustaw na null:
const CREATED_AT = 'creation_date';
const UPDATED_AT = null; // brak kolumny aktualizacjiAkcesory $clip->created_at i $clip->updated_at nie podążają natomiast za tymi stałymi: zwracają null. Czytaj kolumnę pod jej prawdziwą nazwą, $clip->creation_date, albo ogólnie przez $clip->{$clip->getCreatedAtColumn()}.
Które rozwiązanie wybrać
| Sytuacja | Rozwiązanie |
|---|---|
| Zwykła tabela aplikacyjna, kolumny po prostu pominięte | Dodać kolumny migracją |
| Tabela pivot, zamrożony słownik, narzucony schemat | public $timestamps = false; |
| Odziedziczona baza z inaczej nazwanymi kolumnami dat | Stałe CREATED_AT i UPDATED_AT |
Jeden zapis nie ma ruszać updated_at | $model->timestamps = false; przed save() |
Częste błędy
$timestamps = false kasuje przydatną informację, zamiast poprawić schemat. Lepiej dodaj kolumny.timestamps() tworzy je jako nullowalne i migracja przechodzi. Definicja NOT NULL bez wartości domyślnej wywaliłaby migrację.

