Laravel 11 for Beginners: Laravel Migrations

Arlind Musliu Portrait
Arlind Musliu

January 5, 2024 · 5 min read · 43 views

Laravel Blogpost Image

2024 UPDATE - LARAVEL 11

We're excited to announce that we have updated all of our blog post examples to reflect the new Laravel 11 version! Our previous examples were based on Laravel 10, but with the release of Laravel 11, we wanted to ensure that our readers have access to the most up-to-date information and examples.

Ensuring Data Consistency with Migrations

Setting up your database tables correctly is like laying the foundation for a building. For our Laravel app project, migrations are the blueprint that defines the structure of our data. Let's walk through the process of creating a robust database schema for a blog with users, profiles, posts, tags, and images using Laravel's migration system. Coding time!

Getting Started with Laravel Migrations

Migrations in Laravel are PHP files that describe database changes, such as creating or altering tables. They give you the power to evolve your database schema over time in a version-controlled manner. Whether you're adding a new feature or modifying an existing one, migrations ensure that these changes are applied consistently across all environments.

The Blueprint of Our Blog

Let's define the main features of our blog:

  • Users: The authors who write blog posts.

  • Profiles: The profiles of the users.

  • Posts: The articles or blog entries published by users.

  • Tags: Keywords or topics that categorize posts.

  • Images: Photos or graphics associated with posts or users.

Before we create the migrations, let's use the flag -mfsc for creating everything that is needed: model, migration, controller, seeder and factory.

Users

A new Laravel installation already has the User model and seeder. That's why in this case we only need to create the seeder and the controller. Let's start with the seeder:

php artisan make:seeder UserSeeder

Now we create the controller with the command:

php artisan make:controller UserController

Profiles

For the Profile model, you might handle profile creation and updates through the UserController. Thus, you wouldn't necessarily need a dedicated controller. We will need the model, migration, factory and seeder. We can create all of them through the command:

php artisan make:model Profile -mfs

Posts

For the Post model, you typically want a controller to handle CRUD operations for blog posts. In this case, we need everything, that's why we added the -mfsc flag. Let's create a model, a migration, a factory, a seeder and a controller through the command:

php artisan make:model Post -mfsc

Tags

For the Tag model, you may not need a controller if tag management is done within another controller, like PostController. That's why besides the model, we will create the migration, factory and seeder:

php artisan make:model Tag -mfs

Images

Similarly, for the Image model, if image uploads are managed through the PostController, you may not need a separate controller:

php artisan make:model Image -mfs

Each command will create the following:

  • A model class in the app/Models directory.

  • A migration file in the database/migrations directory.

  • A factory file in the database/factories directory.

  • A seeder file in the database/seeders directory.

And for User and Post, where the -c flag is included, it will also create:

  • A controller file in the app/Http/Controllers directory.

Modifying Our Migrations

Users Migration

Every blog needs authors, and in Laravel, we'll start by setting up a users table. With the previous command, we already have a default migration file, and we will modify it to fit our blog application needs:

<?php

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

class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->string('email_verified_at')->nullable();
            $table->string('password');
            $table->timestamps();
        });
    }

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

Profiles Migration

We also have a profiles table that provides additional information about our users:

<?php

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

class CreateProfilesTable extends Migration
{
    public function up()
    {
        Schema::create('profiles', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->text('bio')->nullable();
            $table->string('website')->nullable();
            $table->timestamps();
        });
    }

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

Posts Migration

The posts table will hold all the blog entries written by our users:

<?php

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

class CreatePostsTable extends Migration
{
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->string('title');
            $table->text('content');
            $table->timestamps();
        });
    }

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

Tags Migration

To help categorize posts, we'll set up a tags table:

<?php

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

class CreateTagsTable extends Migration
{
    public function up()
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->timestamps();
        });
    }

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

Post_Tag Pivot Table Migration

Since a post can have multiple tags and vice versa, we'll need a pivot table to establish a many-to-many relationship:
php artisan make:migration create_post_tag_table --create=post_tag

<?php

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

class CreatePostTagTable extends Migration
{
    public function up()
    {
        Schema::create('post_tag', function (Blueprint $table) {
            $table->foreignId('post_id')->constrained()->onDelete('cascade');
            $table->foreignId('tag_id')->constrained()->onDelete('cascade');
            $table->primary(['post_id', 'tag_id']); // Composite primary key
        });
    }

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

Images Migration

Lastly, we'll need a table to store images associated with our posts:

<?php

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

class CreateImagesTable extends Migration
{
    public function up()
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->morphs('imageable');
            $table->string('path'); // This will store the file path of the image
            $table->timestamps();
        });
    }

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

Running the Migrations

Once these migration files are created, run them with Laravel's artisan command:

php artisan migrate

This command will execute each migration in order, creating your database schema as defined.

Conclusion

With these migrations, we've laid the groundwork for our blogging platform. Each table is designed to hold specific pieces of data, with relationships that reflect the real-world connections between users, their profiles, blog posts, tags, and images. Migrations provide a clear, version-controlled path to evolve this schema as our blog grows and changes, ensuring a solid foundation for our Laravel application.

Upcoming Articles in the Series

  1. Laravel for Beginners: Laravel Seeders and Factories

  2. Laravel for Beginners: Routing Your Application

  3. Laravel for Beginners: Middleware


Bring Your Ideas to Life 🚀

If you need help with a Laravel project let's get in touch.

Lucky Media is proud to be recognized as a Top Laravel Development Agency

Arlind Musliu Portrait
Arlind Musliu

Cofounder and CFO of Lucky Media

Technologies:

Laravel
Heading Pattern

Related Posts

Stay up to date

Be updated with all news, products and tips we share!