Skip to content

How to Set Default Timestamp Values in Laravel Migrations

how to set default timestamp values in laravel migrations. best practice

In this guide, I will show you how to set the default value of a timestamp column to the current timestamp in a Laravel migration.

🎥 Not in the mood for reading? Why not check out a video tutorial instead?

The Problem

If you choose to create a migration using the php artisan migrate command, you will notice that Laravel adds the $table->timestamps() line, which will generate two nullable timestamp columns, created_at and updated_at.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name', 50);
            $table->decimal('price');
            $table->timestamps(); 👈 // this
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

I’ve also seen developers who, even if they don’t generate a migration using the artisan command, create the created_at and updated_at columns using the timestamps() method.

There’s nothing wrong with either of these approaches, although there is a catch! ⚠

If we create the record through code, we’ll see that the timestamps for created_at and updated_at are generated automatically.

$ php artisan tinker

> App\Models\Product::create(['name' => 'Product 1', 'price' => 11]);

= App\Models\Product {#5293
    name: "Product 1",
    price: 11,
    updated_at: "2024-10-17 19:36:37",
    created_at: "2024-10-17 19:36:37",
    id: 1,
  }

Notice that the default timestamp values have been generated automatically for created_at and updated_at columns.

That’s great!

But if we create a record manually or by executing a raw SQL outside the application, the created_at and updated_at columns won’t be filled with the default current timestamp. They will be null.

INSERT INTO db.products (id, name, price)
	VALUES (2, 'Product 2', 22.0);

Notice that the created_at and updated_at columns are both null. The default timestamp values have not been generated automatically.

Let’s see how we can fix this problem so that created_at and updated_at are filled with the current timestamp in both cases: when the record is created using the Model and when it’s created outside the application code.

Set Default Timestamp Values in Laravel Migrations

The solution is to create the created_at and updated_at columns using timestamp() instead of timestamps().

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration
{
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name', 50);
            $table->decimal('price');
            $table->timestamp('created_at')->useCurrent(); 👈
            $table->timestamp('updated_at')->useCurrent()->useCurrentOnUpdate(); 👈
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

The useCurrent() method tells Laravel to set the column’s default value to the current timestamp (the moment when the record is created).

So, whenever a new row is added, the created_at column will automatically be filled with the current date and time.

The useCurrentOnUpdate() method updates the updated_at column with the current timestamp every time the record changes.

Now, every time a new record is created, whether through the application code or by running a query directly in the database, created_at and updated_at will be set to the current timestamp. ✅

Before and After

This is what the table structure looks like when we use the timestamps() as generated by the artisan command:

CREATE TABLE `products` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `price` decimal(8,2) NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

This is what the table structure looks like when we use the timestamp(), useCurrent() and useCurrentOnUpdate() methods:

CREATE TABLE `products` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `price` decimal(8,2) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Conclusion

I think using useCurrent() for created_at and useCurrent()->useCurrentOnUpdate() for updated_at is a best practice that simplifies timestamp management and enhances the reliability of the data.

By relying on these methods, you ensure consistent behavior across your application, regardless of where the record is created or modified (e.g., through the application’s code, command line, or directly in the database).

This approach can also simplify your code. You can avoid writing extra code to handle timestamps, making your application cleaner and easier to maintain.

That’s all! 💜

I’d love to hear your thoughts in the comments section.


Let me know what you think about this article in the comments section below.

If you find this article helpful, please share it with others and subscribe to the blog to support me, and receive a bi-monthly-ish e-mail notification on my latest articles.   
  

Comments