250

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.

Karl Hill
  • 12,937
  • 5
  • 58
  • 95
JoeyD473
  • 2,890
  • 2
  • 21
  • 25

14 Answers14

451

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 the useCurrent() 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 timestamps nullable() 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 the CURRENT_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.

Paulo Freitas
  • 13,194
  • 14
  • 74
  • 96
  • Can this also be used for PARTITION BY statements in your tests? – Glenn Plas Jan 02 '14 at 17:15
  • 2
    Not flawlessly. For PostgreSQL 'CURRENT_TIMESTAMP' returns something in the format of: 2014-08-11 15:06:29.692439. This causes the Carbon::createFromFormat('Y-m-d H:i:s', $timestamp) method to fail (it can't parse the trailing milliseconds). This is used by Laravel when accessing timestamps. To fix for PostgreSQL, use: DB::raw('now()::timestamp(0)') (reference: http://www.postgresql.org/docs/8.1/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT) – andrewhl Aug 11 '14 at 20:37
65

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

Brian Adams
  • 1,080
  • 8
  • 9
  • 3
    Why not just use `$table->timestamps()->default(DB::raw('CURRENT_TIMESTAMP'));` ? – dave Jun 30 '15 at 08:23
  • 1
    @dave Because then `updated_at` wouldn't change when the record was modified after its initial creation – Erik Berkun-Drevnig Oct 07 '15 at 19:10
  • Yeah, add to that timestamps() doesn't allow for defaults anyways, so that wouldn't work at all. I had committed code to allow it, but the Laravel managers don't really want people using default the way we are using it (assuming because versions of MySQL prior to 5.6.5 don't allow multiple columns that have timestamps as default). – dave Oct 09 '15 at 03:32
  • Actually the updated_at is managed by Eloquent so there is no need for the "on update" bit at all since it will be set when a model is updated automatically. – dmyers Apr 28 '16 at 17:00
  • @dmyers If you're using eloquent then you can just do `$t->timestamps();` but that doesn't answer the question. – Brian Adams Apr 28 '16 at 17:14
57

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.

Gras Double
  • 15,901
  • 8
  • 56
  • 54
41

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+8 with mysql 8).


Edit: Since Laravel 8 you can also use:

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

to achieve the same result.

Thanks to @bilogic for pointing this out.

Gras Double
  • 15,901
  • 8
  • 56
  • 54
ndberg
  • 3,391
  • 1
  • 21
  • 36
  • 1
    I really like this idea. In some cases it would be extremely useful to have `updates_at = null` in non modified records – atorscho Sep 15 '20 at 09:15
  • nice, does that equate to `->nullable()->useCurrentOnUpdate()`? – bilogic Feb 07 '22 at 03:52
  • Oh wow yes, thanks, didn't know that this is implemented since Laravel 8 – ndberg Feb 08 '22 at 06:51
  • Also for future googlers, this is how I used to do it, but I have found it made other queries less convenient, such as finding out the last time any row in the table was updated (which becomes much worse as the number of joins increases). You don't need a null updated_at to know if the query has not been modified because you can check equality between created_at and updated_at. – Jared Becksfort Jan 12 '23 at 16:06
13

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.

Community
  • 1
  • 1
Glenn Plas
  • 1,608
  • 15
  • 17
  • little remark, before yelling: this doesn't work for me in laravel, take a look a the date this answer was written: 2013. It was valid back then. I would appreciate it before you tap the down arrow. – Glenn Plas May 03 '18 at 15:06
12

In laravel 7, 8 to set current time use the following:

$table->timestamp('column_name')->useCurrent();
user311086
  • 920
  • 2
  • 16
  • 29
9

Use Paulo Freitas suggestion instead.


Until Laravel fixes this, you can run a standard database query after the Schema::create have been run.

    Schema::create("users", function($table){
        $table->increments('id');
        $table->string('email', 255);
        $table->string('given_name', 100);
        $table->string('family_name', 100);
        $table->timestamp('joined');
        $table->enum('gender', ['male', 'female', 'unisex'])->default('unisex');
        $table->string('timezone', 30)->default('UTC');
        $table->text('about');
    });
    DB::statement("ALTER TABLE ".DB::getTablePrefix()."users CHANGE joined joined TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL");

It worked wonders for me.

Marwelln
  • 28,492
  • 21
  • 93
  • 117
  • That's a nice trick. I wish the schema builder supported partitioned tables as I use those all over the place. I tried digging into the code but it's not that obvious to me where to mod this. – Glenn Plas Sep 15 '13 at 09:44
8

If your're looking to set the current datetime for a dateTime column (like i was when I googled), use this way

$table->dateTime('signed_when')->useCurrent();
1

I use the following customization in Laravel to:

  1. Default created_at to the current timestamp
  2. Update the timestamp when the record is updated

First, I'll create a file, helpers.php, in the root of Laravel and insert the following:

<?php

if (!function_exists('database_driver')) {
    function database_driver(): string
    {
        $connection = config('database.default');
        return config('database.connections.' . $connection . '.driver');
    }
}

if (!function_exists('is_database_driver')) {
    function is_database_driver(string $driver): bool
    {
        return $driver === database_driver();
    }
}

In composer.json, I'll insert the following into autoload. This allows composer to auto-discover helpers.php.

    "autoload": {
        "files": [
            "app/Services/Uploads/Processors/processor_functions.php",
            "app/helpers.php"
        ]
    },

I use the following in my Laravel models.

        if (is_database_driver('sqlite')) {
            $table->timestamps();
        } else {
            $table->timestamp('created_at')->default(\DB::raw('CURRENT_TIMESTAMP'));
            $table->timestamp('updated_at')
                ->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
        }

This allows my team to continue using sqlite for unit tests. ON UPDATE CURRENT_TIMESTAMP is a MySQL shortcut and is not available in sqlite.

Dream Ideation
  • 153
  • 1
  • 6
1

It will work 100%. By this way you can set default datetime value after change datetype of the column.

Schema::table('table_name', function (Blueprint $table) {
            $table->dateTime('ans_time')->default(now())->change();
        });
Kaleemullah
  • 446
  • 3
  • 8
0

You can pass any default value you want, using

$table->dateTime('ans_time')->default(date("Y-m-d H:i:s",strtotime('1-1-2023')));

Imad Ullah
  • 929
  • 9
  • 17
-1

This is how you do it, I have checked it and it works on my Laravel 4.2.

$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));

Hope this helps.

Nic
  • 12,220
  • 20
  • 77
  • 105
Jawad
  • 95
  • 1
  • 1
-1

The only thing that worked for me was

DB::statement("ALTER TABLE orders CHANGE updated_at updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP");
helvete
  • 2,455
  • 13
  • 33
  • 37
-7

In Laravel 5 simply:

$table->timestamps(); //Adds created_at and updated_at columns.

Documentation: http://laravel.com/docs/5.1/migrations#creating-columns

JoenMarz
  • 143
  • 3
  • 13
  • 14
    But this does not set the default as CURRENT_TIMESTAMP as asked in the question. – Josh Feb 17 '16 at 22:48
  • i try this in, but given null in 5.4 idk why, but when i try ->useCurrent(); its work fine – Anthony Kal Jun 03 '17 at 07:33
  • doesn't answers the question of `CURRENT_TIMESTAMP` on `created_at` column and `on UPDATE CURRENT_TIMESTAMP` on `updated_at` column. Till folks at Laravel fix it, use this: `$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));` `$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP'));` – Hamza Rashid Jan 30 '19 at 00:53
  • This does not answer the question. This only adds the timestamps not the default value. – Patrick Jul 27 '22 at 11:06