How Can I Set the Default Value of a Timestamp Column to the Current Timestamp with Laravel Migrations?
I would like to make a timestamp column with a default value of CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
using the Laravel Schema Builder/Migrations. I have gone through the Laravel documentation several times, and I don't see how I can make that the default for a timestamp column.
The timestamps()
function makes the defaults 0000-00-00 00:00
for both columns that it makes.
Given it's a raw expression, you should use DB::raw()
to set CURRENT_TIMESTAMP
as a default value for a column:
$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
This works flawlessly on every database driver.
As of Laravel 5.1.25 (see PR 10962 and commit 15c487fe) you can now use the new useCurrent()
column modifier method to achieve the same default value for a column:
$table->timestamp('created_at')->useCurrent();
Back to the question, on MySQL you could also use the ON UPDATE
clause through DB::raw()
:
$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
Again, as of Laravel 8.36.0 (see PR 36817) you can now use the new useCurrentOnUpdate()
column modifier method together with the useCurrent()
modifier to achieve the same default value for a column:
$table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate();
Gotchas
-
MySQL
Starting with MySQL 5.7,0000-00-00 00:00:00
is no longer considered a valid date. As documented at the Laravel 5.2 upgrade guide, all timestamp columns should receive a valid default value when you insert records into your database. You may use theuseCurrent()
column modifier (from Laravel 5.1.25 and above) in your migrations to default the timestamp columns to the current timestamps, or you may make the timestampsnullable()
to allow null values. -
PostgreSQL & Laravel 4.x
In Laravel 4.x versions, the PostgreSQL driver was using the default database precision to store timestamp values. When using theCURRENT_TIMESTAMP
function on a column with a default precision, PostgreSQL generates a timestamp with the higher precision available, thus generating a timestamp with a fractional second part - see this SQL fiddle.This will led Carbon to fail parsing a timestamp since it won't be expecting microseconds being stored. To avoid this unexpected behavior breaking your application you have to explicitly give a zero precision to the
CURRENT_TIMESTAMP
function as below:$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP(0)'));
Since Laravel 5.0,
timestamp()
columns has been changed to use a default precision of zero which avoids this.
Thanks to @andrewhl for pointing out the Laravel 4.x issue in the comments.
Thanks to @ChanakaKarunarathne for bringing out the new useCurrentOnUpdate()
shortcut in the comments.
To create both of the created_at
and updated_at
columns:
$t->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
$t->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));
You will need MySQL version >= 5.6.5 to have multiple columns with CURRENT_TIMESTAMP
Starting from Laravel 5.1.26, tagged on 2015-12-02, a useCurrent()
modifier has been added:
Schema::table('users', function ($table) {
$table->timestamp('created')->useCurrent();
});
PR 10962 (followed by commit 15c487fe) leaded to this addition.
You may also want to read issues 3602 and 11518 which are of interest.
Basically, MySQL 5.7 (with default config) requires you to define either a default value or nullable for time fields.
As additional possibility for future googlers
I find it more useful to have null in the updated_at column when the record is been created but has never been modified. It reduces the db size (ok, just a little) and its possible to see it at the first sight that the data has never been modified.
As of this I use:
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->default(DB::raw('NULL ON UPDATE CURRENT_TIMESTAMP'))->nullable();
(In Laravel 7 with mysql 8).
This doesn't work for a fact:
$table->timestamp('created_at')->default('CURRENT_TIMESTAMP');
It doesn't remove the 'default 0' that seems to come with selecting timestamp and it just appends the custom default. But we kind of need it without the quotes. Not everything that manipulates a DB is coming from Laravel4. That's his point. He wants custom defaults on certain columns like:
$table->timestamps()->default('CURRENT_TIMESTAMP');
I don't think it's possible with Laravel. I've been searching for an hour now to see whether it's possible.
Update: Paulos Freita's answer shows that it is possible, but the syntax isn't straightforward.