Блог на Laravel 7, часть 1. Создание таблиц БД, наполнение тестовыми данными

02.12.2020

Теги: LaravelMySQLPHPWeb-разработкаБазаДанныхБлогКлассПрактикаФреймворк

Функционал будет не слишком богатый — просмотр списка всех постов, просмотр постов выбанной категории, просмотр постов выбранного тега, просмотр отдельного поста, регистрация и аутентификация пользователей. В панели управления можно создать, отредактировать и удалить категорию, пост или тег.

Создание проекта

Создаем новый проект Laravel:

> composer create-project --prefer-dist laravel/laravel blog

Пакет для подсказок PhpStorm:

> composer require barryvdh/laravel-ide-helper --dev
> php artisan clear-compiled
> php artisan ide-helper:generate

Таблицы БД

Модель Category и миграция:

> php artisan make:model Category -m
Model created successfully.
Created Migration: 2020_12_02_074609_create_categories_table
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCategoriesTable extends Migration {
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up() {
        Schema::create('categories', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('parent_id')->nullable(false)->default(0);
            $table->string('name', 100);
            $table->string('slug', 100)->unique();
            $table->string('image', 50)->nullable();
            $table->string('content', 500)->nullable();
            $table->timestamps();
        });
    }

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

Модель Post и миграция:

> php artisan make:model Post -m
Model created successfully.
Created Migration: 2020_12_02_074631_create_posts_table
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->unsignedBigInteger('user_id')->nullable();
            $table->unsignedBigInteger('published_by')->nullable();
            $table->unsignedBigInteger('category_id')->nullable();
            $table->string('name', 100);
            $table->string('slug', 100)->unique();
            $table->string('image', 50)->nullable();
            $table->string('excerpt', 500)->nullable(false);
            $table->text('content')->nullable(false);
            $table->timestamps();

            // внешний ключ, ссылается на поле id таблицы users
            $table->foreign('user_id')
                ->references('id')
                ->on('brands')
                ->nullOnDelete();
            // внешний ключ, ссылается на поле id таблицы users
            $table->foreign('published_by')
                ->references('id')
                ->on('users')
                ->nullOnDelete();
            // внешний ключ, ссылается на поле id таблицы categories
            $table->foreign('category_id')
                ->references('id')
                ->on('categories')
                ->nullOnDelete();
        });
    }

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

Модель Comment и миграция:

> php artisan make:model Comment -m
Model created successfully.
Created Migration: 2020_12_02_074651_create_comments_table
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCommentsTable extends Migration {
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up() {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('post_id')->nullable(false);
            $table->unsignedBigInteger('user_id')->nullable();
            $table->unsignedBigInteger('published_by')->nullable();
            $table->string('content', 500)->nullable(false);
            $table->timestamps();

            // внешний ключ, ссылается на поле id таблицы posts
            $table->foreign('post_id')
                ->references('id')
                ->on('posts')
                ->onDelete('cascade');
            // внешний ключ, ссылается на поле id таблицы users
            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->nullOnDelete();
            // внешний ключ, ссылается на поле id таблицы users
            $table->foreign('published_by')
                ->references('id')
                ->on('users')
                ->nullOnDelete();
        });
    }

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

Модель Tag и миграция:

> php artisan make:model Tag -m
Model created successfully.
Created Migration: 2020_12_02_074715_create_tags_table
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('name', 50)->nullable(false);
            $table->string('slug', 50)->nullable(false)->unique();
            $table->timestamps();
        });
    }

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

Миграция для создания таблицы связи пост-тег:

> php artisan make:migration create_post_tag_table
Created Migration: 2020_12_02_074853_create_post_tag_table
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostTagTable extends Migration {
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up() {
        Schema::create('post_tag', function (Blueprint $table) {
            $table->unsignedBigInteger('post_id');
            $table->unsignedBigInteger('tag_id');
            $table->timestamps();
            // внешний ключ, ссылается на поле id таблицы posts
            $table->foreign('post_id')
                ->references('id')
                ->on('posts')
                ->onDelete('cascade');
            // внешний ключ, ссылается на поле id таблицы tags
            $table->foreign('tag_id')
                ->references('id')
                ->on('tags')
                ->onDelete('cascade');
            // составной первичный ключ
            $table->primary(['post_id', 'tag_id']);
        });
    }

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

Seeders

Класс для заполнения таблицы categories:

> php artisan make:seeder CategoryTableSeeder
Seeder created successfully.
use Illuminate\Database\Seeder;

class CategoryTableSeeder extends Seeder {
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run() {
        // создать 12 категорий
        factory(App\Category::class, 12)->create();
    }
}

Класс для заполнения таблицы posts:

> php artisan make:seeder PostTableSeeder
Seeder created successfully.
use Illuminate\Database\Seeder;

class PostTableSeeder extends Seeder {
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run() {
        // создать 50 постов блога
        factory(App\Post::class, 50)->create();
    }
}

Класс для заполнения таблицы tags:

> php artisan make:seeder TagTableSeeder
Seeder created successfully.
use Illuminate\Database\Seeder;

class TagTableSeeder extends Seeder {
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run() {
        // создать 100 тегов блога
        factory(App\Tag::class, 100)->create();
    }
}

Класс для заполнения таблицы comments:

> php artisan make:seeder CommentTableSeeder
Seeder created successfully.
use Illuminate\Database\Seeder;

class CommentTableSeeder extends Seeder {
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run() {
        // создать 200 комментариев
        factory(App\Comment::class, 200)->create();
    }
}

Класс для заполнения таблицы post_tag:

> php artisan make:seeder PostTagTableSeeder
Seeder created successfully.
use Illuminate\Database\Seeder;

class PostTagTableSeeder extends Seeder {
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run() {
        // создать связи между постами и тегами
        foreach(App\Post::all() as $post) {
            foreach(App\Tag::all() as $tag) {
                if (rand(1, 20) == 10) {
                    $post->tags()->attach($tag->id);
                }
            }
        }
    }
}

Поскольку в классе PostTagTableSeeder мы используем связи, надо их сразу установить:

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model {
    public function tags() {
        return $this->belongsToMany(Tag::class)->withTimestamps();
    }
}
namespace App;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model {
    public function posts() {
        return $this->belongsToMany(Post::class)->withTimestamps();;
    }
}

Класс для заполнения таблицы users:

> php artisan make:seeder UserTableSeeder
Seeder created successfully.
use Illuminate\Database\Seeder;

class UserTableSeeder extends Seeder {
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run() {
        // создать 10 пользователей
        factory(App\User::class, 10)->create();
    }
}

Фабрики

Фабрика для модели Category:

> php artisan make:factory CategoryFactory --model=Category
Factory created successfully.
/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Category;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(Category::class, function (Faker $faker) {
    $name = $faker->realText(rand(40, 50));
    return [
        'name' => $name,
        'content' => $faker->realText(rand(200, 500)),
        'slug' => Str::slug($name),
    ];
});

Фабрика для модели Post:

> php artisan make:factory PostFactory --model=Post
Factory created successfully.
/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Post;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

$factory->define(Post::class, function (Faker $faker) {
    $name = $faker->realText(rand(70, 100));
    return [
        'user_id' => rand(1, 10),
        'category_id' => rand(1, 12),
        'name' => $name,
        'excerpt' => $faker->realText(rand(300, 400)),
        'content' => $faker->realText(rand(400, 500)),
        'slug' => Str::slug($name),
        'published_by' => rand(1, 10),
    ];
});

Фабрика для модели Tag:

> php artisan make:factory TagFactory --model=Tag
Factory created successfully.
/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Tag;
use Faker\Generator as Faker;

$factory->define(Tag::class, function (Faker $faker) {
    $name = $faker->realText(rand(20, 30));
    return [
        'name' => $name,
        'slug' => Str::slug($name),
    ];
});

Фабрика для модели Comment:

> php artisan make:factory CommentFactory --model=Comment
Factory created successfully.
/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Comment;
use Faker\Generator as Faker;

$factory->define(Comment::class, function (Faker $faker) {
    return [
        'post_id' => rand(1, 50),
        'user_id' => rand(1, 10),
        'content' => $faker->realText(rand(200, 500)),
        'created_at' => $faker->dateTimeBetween('-200 days', '-50 days'),
        'updated_at' => $faker->dateTimeBetween('-40 days', '-1 days'),
        'published_by' => rand(1, 3), // супер-админ или просто админ
    ];
});

Фабрика для модели User:

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/

$factory->define(User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'password' => Hash::make('qwerty'),
        'email_verified_at' => now(),
        'remember_token' => Str::random(10),
    ];
});

Наполнение БД

Редактируем класс DatabaseSeeder:

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder {
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run() {
        $this->call(UserTableSeeder::class);
        $this->command->info('Таблица пользователей загружена данными!');

        $this->call(CategoryTableSeeder::class);
        $this->command->info('Таблица категорий загружена данными!');

        $this->call(TagTableSeeder::class);
        $this->command->info('Таблица тегов загружена данными!');

        $this->call(PostTableSeeder::class);
        $this->command->info('Таблица постов загружена данными!');

        $this->call(PostTagTableSeeder::class);
        $this->command->info('Таблица пост-тег загружена данными!');

        $this->call(CommentTableSeeder::class);
        $this->command->info('Таблица комментариев загружена данными!');
    }
}

Все готово, запускаем миграцию:

> php artisan migrate:fresh --seed
Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table (0.03 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table (0.03 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated:  2019_08_19_000000_create_failed_jobs_table (0.02 seconds)
Migrating: 2020_12_02_074609_create_categories_table
Migrated:  2020_12_02_074609_create_categories_table (0.03 seconds)
Migrating: 2020_12_02_074631_create_posts_table
Migrated:  2020_12_02_074631_create_posts_table (0.03 seconds)
Migrating: 2020_12_02_074651_create_comments_table
Migrated:  2020_12_02_074651_create_comments_table (0.02 seconds)
Migrating: 2020_12_02_074715_create_tags_table
Migrated:  2020_12_02_074715_create_tags_table (0.02 seconds)
Migrating: 2020_12_02_074853_create_post_tag_table
Migrated:  2020_12_02_074853_create_post_tag_table (0.02 seconds)
Seeding: UserTableSeeder
Seeded:  UserTableSeeder (0.91 seconds)
Таблица пользователей загружена данными!
Seeding: CategoryTableSeeder
Seeded:  CategoryTableSeeder (0.13 seconds)
Таблица категорий загружена данными!
Seeding: TagTableSeeder
Seeded:  TagTableSeeder (0.61 seconds)
Таблица тегов загружена данными!
Seeding: PostTableSeeder
Seeded:  PostTableSeeder (0.56 seconds)
Таблица постов загружена данными!
Seeding: PostTagTableSeeder
Seeded:  PostTagTableSeeder (7.32 seconds)
Таблица пост-тег загружена данными!
Seeding: CommentTableSeeder
Seeded:  CommentTableSeeder (1.67 seconds)
Таблица комментариев загружена данными!
Database seeding completed successfully.

Поиск: Laravel • MySQL • PHP • Web-разработка • База данных • Блог • Класс • Практика • Фреймворк

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.