Laravel: błąd „Unknown column 'updated_at’” i jak go naprawić

ORM Eloquent zapisuje kolumny created_at i updated_at przy każdym wstawieniu i każdej aktualizacji wiersza. Jeśli tabela ich nie ma, zapytanie kończy się błędem SQLSTATE 1054. Do wyboru są trzy poprawki: dodać kolumny migracją, wyłączyć timestampy w modelu albo wskazać Eloquentowi ich prawdziwe nazwy.

Laravel: błąd „Unknown column ‘updated_at’” i jak go naprawić
Szybka odpowiedź

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:

code
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:

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

Najpierw sprawdź, co tabela naprawdę zawiera:

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

Jeś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:

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

Po 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:

php
$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:

app/Models/Video.php
<?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:

php
$task->timestamps = false;
$task->title = 'Titre corrigé';
$task->save();   // updated_at zostaje bez zmian
Nie wyłączaj ich po to, żeby uciszyć błąd

Jeś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.

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';
}

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:

php
const CREATED_AT = 'creation_date';
const UPDATED_AT = null;   // brak kolumny aktualizacji

Akcesory $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

Wyłączenie timestampów, żeby uciszyć błąd W zwykłej tabeli aplikacyjnej $timestamps = false kasuje przydatną informację, zamiast poprawić schemat. Lepiej dodaj kolumny.
Kolumny NOT NULL w wypełnionej już tabeli timestamps() tworzy je jako nullowalne i migracja przechodzi. Definicja NOT NULL bez wartości domyślnej wywaliłaby migrację.
Zmiana nazw kolumn w odziedziczonej bazie Niepotrzebna i ryzykowna: stałe CREATED_AT i UPDATED_AT wystarczą, żeby wskazać istniejące nazwy.
Pominięcie UPDATED_AT = null Jeśli tabela ma tylko kolumnę utworzenia, zostawienie UPDATED_AT z wartością domyślną przywraca ten sam błąd przy aktualizacji.

EloquentLaravelMigrationsPHP

Damien Flandrin Web developer od 2010 roku, twórca Gekkode i Email Impact. Każdy artykuł jest sprawdzany na prawdziwym projekcie przed publikacją. Kontakt
Newsletter

Nowe testy, poradniki i projekty, e-mailem.

Powtarzalne testy, wersjonowany kod, datowane wyniki. Nigdy spamu.