Create a Blog with Laravel Migrations, Model Factories and Seeding
Tags: laravel, php
Published: 2021-01-09 21:33:53

When I was first introduced to Laravel, less then 2 years ago, I was immediately taken by it. The rich echo system, the fast-paced updates, a huge community of developers and the relative ease with which you can get a project up and running. However, there were some DB features like  `Migrations`  and  `Seeding`  I never fully explored. When I started incorporating migrations and seeding into my project, my development process become more productive and efficient.

In this tutorial, we will create a simple blog with migrations, model factories and DB seeding. There is a lot to cover here and I cannot dive deeply into every subject. Laravel is a feature rich framework – and very well documented! So, for each subject I’ll be writing about, there is a lot more to cover (seriously, a lot!). I’ve included links to relevant sections of Laravel’s official docs in every part of this tutorial. This tutorial uses Laravel 8.x.

We will:

- Setup a Laravel project

- Connect to a DB

- Create models

- Create migrations

- Create factories

- Seed DB

Here is the GitHub repository for this tutorial:

👉 [https://github.com/yossi-abramov/laravel-blog-migration-factory-seed](https://github.com/yossi-abramov/laravel-blog-migration-factory-seed)

### Setup a Laravel project

First, let’s create a fresh Laravel project. Make sure you have  `composer`  installed, then you can start a new Laravel project with the Laravel Installer or with  `composer create-project`. If you want to use the Laravel installer follow the Laravel official docs here:

👉  [https://laravel.com/docs/8.x/installation](https://laravel.com/docs/8.x/installation)

For this tutorial, I will be installing Laravel with  `composer create-project`.

First, check if you have composer installed:

```plaintext
composer -v
# or 
composer --version
```

If you don’t have composer installed, you should head over to [https://getcomposer.org](https://getcomposer.org/)  and get composer.

Now, let’s create a Laravel project:

```plaintext
composer create-project laravel/laravel laravel-blog-migration-factory-seed
```

### Connect to a DB

For this tutorial I’ll be using MySQL via Shell. First, login to your DB of choice (via Shell, GUI – pick your tool), Then, create a database by running:

```plaintext
CREATE DATABASE blog_example;
```

Now, you need to tell Laravel about your DB connection, host, port, name, username and password. Go to  `.env`  in your project’s root directory and change the following block:

```plaintext
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=blog_example
DB_USERNAME=your_db_user_name
DB_PASSWORD=your_db_password
```

Now, before continuing, test your DB connection. There are a few ways to do this. My favorite is by using  [Laravel Tinker](https://laravel.com/docs/8.x/artisan#tinker)  which is an awesome REPL (read–eval–print loop) tool that's included by default in Laravel applications.

Open the terminal, then `cd`  to your project's root folder, or run in your IDE’s terminal:

```plaintext
php artisan tinker // starts the REPL
DB::connection()->getPdo() // get a PDO connection object
```

If the REPL outputs a PDO object, you’re all set, and your DB is connected to Laravel!

Exit the REPL by running  `exit`.

### Models

For this project we will have 4 Models: User, Post, Tag and PostTag. Laravel comes with a solid User model, migration, factory and seeder so we can skip it for now. Let’s create a Post  `model`, `migration`  and  `factory`  with one  `php artisan`  command:

```plaintext
php artisan make:model Post -mf
```

By running the command we’ve created 3 files:

```plaintext
/app/Models/Post.php
/database/factories/PostFactory.php
/database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php
```

I want to focus on migrations and DB seeding with model factories, so we will not go over models in this tutorial.

Let’s run these  `artisan`  commands to generate the necessary files for Tag and PostTags:

```plaintext
php artisan make:model Tag -mf
php artisan make:model PostTag -m
```

Read more about Laravel models in the official Docs:

👉 [https://laravel.com/docs/8.x/eloquent](https://laravel.com/docs/8.x/eloquent)

### Migrations

DB migrations allows you to create and manage DB schemas. With migrations, you do not have to use a  `CREATE TABLE` statement and can dive into creating tables, defining relationships, modifying columns and much more.

Now, let’s setup our Post model’s first migration.

Go to  `/database/migrations/xxxx_xx_xx_xxxxxx_create_posts_table.php`. This is the migration  `artisan`  generated for us:

```php
<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
```

As you can see, we have two methods.The  `up()`  method will run when we execute migrations with the  `php artisan migrate`  command. The  `down()`  method will run when we rollback our migrations with the  `php artisan migrate:rollback`  command. Let’s go over the  `up()`  method – this is where you define your scheme and preform most of the migration logic.

To create, modify and remove tables and/or columns, Laravel offers us the  `Schema`  façade. Then,  `$table`  is an instance of the  `Blueprint`  class (dependency injection).  `$table`  is packed with methods to help you mange tables! Here is our Post migration logic:

```php
<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id(); 
            
            $table->bigInteger('user_id') 
            ->foreign('user_id')
            ->references('id')
            ->on('users');
            
            // VARCHAR equivalent column with a length.
            $table->string('slug', 255)
            ->unique(); // Index
            
            $table->string('title', 255);  
            
            // TEXT equivalent column.
            $table->text('description'); 
            $table->text('post');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
```

For a list of available methods see:

👉 [https://laravel.com/docs/8.x/migrations#columns](https://laravel.com/docs/8.x/migrations#columns)

Here is our Tag migration:

```php
<?php

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

class CreateTagsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->id();
            $table->string('tag', 255);
            $table->timestamps();
        });
    }
    
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tags');
    }
}
```

And the PostTag migration:

```php
<?php

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

class CreatePostTagsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('post_tags', function (Blueprint $table) {
            $table->id();

            $table->bigInteger('post_id') 
            ->foreign('post_id')
            ->references('id')
            ->on('posts');
            
            $table->bigInteger('tag_id') 
            ->foreign('tag_id')
            ->references('id')
            ->on('tags');
            
            $table->timestamps();
        });
    }
    
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('post_tags');
    }
}
```

Now, let’s run our migrations with:

```plaintext
php artisan migrate
```

### Model Factories

Model factories are an excellent way to create test data for your applications. If you need fake data to mockup an application or fake data for database testing, model factories are an excellent solution.

Here is the Post model factory generated by the  `artisan`  command we ran:

```php
<?php

namespace Database\Factories;

use App\Models\Post;
use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Post::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        return [
            //
        ];
    }
}
```

In the  `definition()`  method we will define a “state” for the factory. Laravel’s  `Factory`  class comes with the  `Faker` library baked in. Php Faker is a powerful tool for generating fake data. Here is the Faker GitHub repo for all available methods:

👉 [https://github.com/fzaninotto/Faker](https://github.com/fzaninotto/Faker)

There are many ways of creating a factory for our posts. Here is my quick and dirty suggestion:

```php
<?php

namespace Database\Factories;

use App\Models\Post;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;

class PostFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Post::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition()
    {
        $title = $this->faker->sentence;
        
        $post = collect($this->faker->paragraphs(rand(5, 15)))
        ->map(function($item){
            return "<p>$item</p>";
        })->toArray();

        $post = implode($post);

        return [
            'user_id' => 1,
            'title' => $title,
            'description' => $this->faker->paragraph,
            'slug' => Str::slug($title),
            'post' => $post,
        ];
    }
}
```

We can check our factory with  `tinker`. Start  `tinker`  with:

```plaintext
php artisan tinker
```

Run factory with:

```plaintext
Post::factory()->count(1)->create()
```

`tinker`  will output the generated post/s in the console and will insert the data to your db. You can check your DB with  `tinker`  by running:

```plaintext
Post::all()
```

Here is our Tag factory:

```php
<?php

namespace Database\Factories;

use App\Models\Tag;
use Illuminate\Database\Eloquent\Factories\Factory;

class TagFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = Tag::class;

    /**
     * Define the model's default state.
     *
     * @return array
     */

    public function definition()
    {
        return [
            'tag' => $this->faker->word
        ];
    }
}
```

Test it with  `tinker`:

```plaintext
php artisan tinker
Tag::factory()->count(1)->create()
```

We will not be creating a PostTag Factory.

Read more about model factories in the official docs:

👉 [https://laravel.com/docs/8.x/database-testing](https://laravel.com/docs/8.x/database-testing)

### Seeders

DB seeders allow us to insert data to our DB with ease. For DB seeding we can call Laravel’s DB façade or use model factories. The default DB seeder is located at:  `/database/seeds/DatabaseSeeder.php`:

```php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        // \App\Models\User::factory(10)->create();
    }

}
```

In the  `run()`  method, we can call other seeders that call model factories, or just call our factories directly. We will call model factories and use the DB façade:

```php
<?php

namespace Database\Seeders;

use App\Models\Tag;
use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        User::factory(1)->create(); // Create a single user
        Post::factory(50)->create(); // Create 50 posts
        Tag::factory(8)->create(); // Create 8 tags

        foreach(Post::all() as $post){ // loop through all posts 
            $random_tags = Tag::all()->random(rand(2, 5))->pluck('id')->toArray();
            // Insert random post tag
            foreach ($random_tags as $tag) {
                DB::table('post_tags')->insert([
                    'post_id' => $post->id,
                    'tag_id' => Tag::all()->random(1)[0]->id
                ]);
            }
        }
    }
}
```

To seed your DB run:

```plaintext
php artisan db:seed
```

Now your DB is full of posts 😎!

Check posts with  `tinker`:

```plaintext
php artisan tinker
Post::all()->count()
Tag::all()->count()
PostTag::all()->count()
```

Read more about DB seeding in the official docs:

👉 [https://laravel.com/docs/8.x/seeding](https://laravel.com/docs/8.x/seeding)

I hope this small project helped you get started with Laravel migrations, factories and seeding ✌

_Think about this:_  a blog post usually contains more than just paragraphs. You can take the post model factory a few steps further and create rich random posts with images, quotes and more content elements ✍

The GitHub repository for this tutorial includes 2 Views for checking out your fake data. For now, these views are not styled at all and serve **only** as a quick reference for our data.

In an upcoming post, I will style this small project with  [Laravel Blade UI Kit](https://blade-ui-kit.com/)  and add more features – stay tuned!

✍ For more posts:
[https://yossiabramov.com/](https://yossiabramov.com/)