Yossi Abramov
Posted on January 9, 2021
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
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
For this tutorial, I will be installing Laravel with composer create-project
.
First, check if you have composer installed:
composer -v
# or
composer --version
If you don’t have composer installed, you should head over to https://getcomposer.org and get composer.
Now, let’s create a Laravel project:
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:
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:
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 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:
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:
php artisan make:model Post -mf
By running the command we’ve created 3 files:
/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:
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
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
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
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
Here is our Tag migration:
<?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
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:
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
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
There are many ways of creating a factory for our posts. Here is my quick and dirty suggestion:
<?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:
php artisan tinker
Run factory with:
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:
Post::all()
Here is our Tag factory:
<?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
:
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
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
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
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:
php artisan db:seed
Now your DB is full of posts 😎!
Check posts with tinker
:
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
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 and add more features – stay tuned!
✍ For more posts:
https://yossiabramov.com/
Posted on January 9, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.