Laravelでmigratinonを実行、Seederでデータを挿入する

:::note
今回はLaravel Sailを使用します。SailはアプリケーションのDockerコンテナ内でArtisanコマンドを実行します。

コマンドを書く際に
./vendor bin sailをsailに省略しています。
:::

今回のハンズオンのリポジトリは以下です。

https://github.com/yamatai12/laravel-sql

# 1. 背景
業務でLaravelを使っているので勉強のために書きます。
今回はmigrationでテーブルを作成、Seederで作成後のテーブルにサンプルデータの挿入をします。

# 2. migrationとは

SQLを使わずにDBを管理できる仕組みです。
以下の流れでTableを作成、削除、編集できます。
- migrationファイルを作成
- migrationを実行

# 3. migrationで作成するテーブルのER図

```mermaid
erDiagram
    employees {
        INT id PK "従業員ID（主キー）"
        VARCHAR(50) name "名前"
        VARCHAR(50) position "役職"
        INT department_id FK "部門ID（外部キー）"
    }

    departments {
        INT id PK "部門ID（主キー）"
        VARCHAR(50) department_name "部門名"
    }

    projects {
        INT id PK "プロジェクトID（主キー）"
        VARCHAR(50) project_name "プロジェクト名"
        INT department_id FK "部門ID（外部キー）"
    }

    employee_project {
        INT employee_id FK "従業員ID（外部キー）"
        INT project_id FK "プロジェクトID（外部キー）"
    }

    employees ||--o{ departments : "belongs to"
    departments ||--o{ projects : "has"
    employees ||--o{ employee_project : "participates in"
    projects ||--o{ employee_project : "includes"

```

## 3.1. employees（従業員テーブル）
従業員の情報を保持します。

| Column        | Type          | Description                    |
|---------------|---------------|--------------------------------|
| employee_id   | INT           | 従業員ID（主キー）             |
| name          | VARCHAR(50)   | 名前                           |
| position      | VARCHAR(50)   | 役職                           |
| department_id | INT           | 部門ID（外部キー）             |

---

## 3.2. departments（部門テーブル）
各部門の情報を保持します。

| Column          | Type          | Description                    |
|-----------------|---------------|--------------------------------|
| department_id    | INT           | 部門ID（主キー）               |
| department_name  | VARCHAR(50)   | 部門名                         |

---

## 3.3. projects（プロジェクトテーブル）
プロジェクトの情報を保持します。

| Column          | Type          | Description                    |
|-----------------|---------------|--------------------------------|
| project_id      | INT           | プロジェクトID（主キー）       |
| project_name    | VARCHAR(50)   | プロジェクト名                 |
| department_id    | INT           | 部門ID（外部キー）             |

---

## 3.4. employee_project（従業員とプロジェクトの関連テーブル）
従業員が参加しているプロジェクトの情報を保持します。

| Column        | Type          | Description                    |
|---------------|---------------|--------------------------------|
| employee_id   | INT           | 従業員ID（外部キー）           |
| project_id    | INT           | プロジェクトID（外部キー）     |

# 4. migrationファイルを作成する

:::note
sail artisan make:migration [アクション名]_[テーブル名]_table
マイグレーションファイル名は記法に決まりはないですが、上のように書くと良いそうです。
:::

```terminal
sail artisan make:migration create_departments_table
```

以下のようなファイルが作成されます。

:::note
[タイムスタンプ]_create_departments_table.php
タイムスタンプが小さいものから先にmigrationが実行されます。
:::


```sample-app/database/migrations/2024_10_26_053505_create_departments_table.php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('departments', function (Blueprint $table) {
            $table->id();
            $table->string('name', 50);
            $table->timestamps();
        });
    }

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

```

- `$table->id()`
bigIncrementsのエイリアスです。
作成されるカラムはUNSIGNED BIGINT型の主キーです。

- `$table->string('name', 50)`
VARCHAR型のカラムで、カラム名はnameです。

```terminal
sail artisan make:migration create_employees_table
```

```sample-app/database/migrations/2024_10_26_053506_create_employees_table.php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('employees', function (Blueprint $table) {
            $table->id();
            $table->string('name', 50);
            $table->string('position', 50);
            $table->unsignedBigInteger('department_id');
            $table->foreign('department_id')->references('id')->on('departments')->onDelete('cascade');
            $table->timestamps();
        });
    }

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

```sample-app/database/migrations/2024_10_26_053506_create_employees_table.php
~~~~~~~~~~~~~
$table->unsignedBigInteger('department_id');
$table->foreign('department_id')->references('id')->on('departments')->onDelete('cascade');
~~~~~~~~~~~~~
```

department_idは外部参照キーで、departmentsテーブルのidです。
departmentsテーブルの該当のidのレコードが削除されたときに、projectsテーブルでdepartmentsテーブルの削除されたidを参照していたレコードも自動的に削除されます。

docにもありましたが以下のように簡潔に書き直せるのでこちらの方が良いですね。

:::note
$table->foreignId('department_id')->constrained('departments')->onDelete('cascade');
:::

```terminal
sail artisan make:migration create_projectss_table
```

```sample-app/database/migrations/2024_10_26_054010_create_projects_table.php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('projects', function (Blueprint $table) {
            $table->id();
            $table->string('name', 50);
            $table->unsignedBigInteger('department_id');
            $table->foreign('department_id')->references('id')->on('departments')->onDelete('cascade');
            $table->timestamps();
        });
    }

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

```terminal
sail artisan make:migration create_employee_project_table
```

```sample-app/database/migrations/2024_10_26_054215_create_employee_project_table.php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('employee_project', function (Blueprint $table) {
            $table->unsignedBigInteger('employee_id');
            $table->foreign('employee_id')->references('id')->on('employees')->onDelete('cascade');
            $table->unsignedBigInteger('project_id');
            $table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
            $table->timestamps();
        });
    }

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

# 5. migratinonを実行する
```terminal
sail artisan migrate
```

新しくdepartments,employee_project,employees,projectsが追加されています。

```terminal
mysql> show tables;
+-----------------------+
| Tables_in_laravel     |
+-----------------------+
| cache                 |
| cache_locks           |
| departments           |
| employee_project      |
| employees             |
| failed_jobs           |
| job_batches           |
| jobs                  |
| migrations            |
| password_reset_tokens |
| projects              |
| sessions              |
| users                 |
+-----------------------+
13 rows in set (0.00 sec)
```

次にテーブルにサンプルデータを挿入します。

# 6. サンプルデータ

## 6.1. employees テーブル

| id | name    | position   | department_id |
|-------------|---------|------------|---------------|
| 1           | Alice   | Manager    | 1             |
| 2           | Bob     | Developer  | 1             |
| 3           | Charlie | Developer  | 2             |
| 4           | David   | Analyst    | 2             |

---

## 6.2. departments テーブル

| id | department_name |
|---------------|------------------|
| 1             | Engineering      |
| 2             | Research         |

---

## 6.3. projects テーブル

| id | project_name | department_id |
|------------|--------------|---------------|
| 1          | Project A    | 1             |
| 2          | Project B    | 1             |
| 3          | Project C    | 2             |

---

## 6.4. employee_project テーブル

| employee_id | project_id |
|--------------|------------|
| 1            | 1          |
| 2            | 1          |
| 2            | 2          |
| 3            | 3          |
| 4            | 3          |

# 7. SeederでTableにサンプルデータを挿入する

```terminal
sail artisan make:seeder EmployeeDepartmentProjectSeeder
```

```sample-app/database/seeders/EmployeeDepartmentProjectSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class EmployeeDepartmentProjectSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
         // departmentsテーブルにデータを挿入
         DB::table('departments')->insert([
            ['name' => 'Engineering'],
            ['name' => 'Research'],
        ]);

        // employeesテーブルにデータを挿入
        DB::table('employees')->insert([
            ['name' => 'Alice', 'position' => 'Manager', 'department_id' => 1],
            ['name' => 'Bob', 'position' => 'Developer', 'department_id' => 1],
            ['name' => 'Charlie', 'position' => 'Developer', 'department_id' => 2],
            ['name' => 'David', 'position' => 'Analyst', 'department_id' => 2],
        ]);

        // projectsテーブルにデータを挿入
        DB::table('projects')->insert([
            ['name' => 'Project A', 'department_id' => 1],
            ['name' => 'Project B', 'department_id' => 1],
            ['name' => 'Project C', 'department_id' => 2],
        ]);

        // employee_projectテーブルにデータを挿入
        DB::table('employee_project')->insert([
            ['employee_id' => 1, 'project_id' => 1],
            ['employee_id' => 2, 'project_id' => 1],
            ['employee_id' => 2, 'project_id' => 2],
            ['employee_id' => 3, 'project_id' => 3],
            ['employee_id' => 4, 'project_id' => 3],
        ]);
    }
}
```

```sample-app/database/seeders/DatabaseSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     */
    public function run(): void
    {

        $this->call(EmployeeDepartmentProjectSeeder::class);
    }
}
```

サンプルデータの挿入を実行します

```terminal
sail artisan db:seed
```

データが挿入されています。

```terminal
mysql> select * from projects;
+----+-----------+---------------+------------+------------+
| id | name      | department_id | created_at | updated_at |
+----+-----------+---------------+------------+------------+
|  1 | Project A |             1 | NULL       | NULL       |
|  2 | Project B |             1 | NULL       | NULL       |
|  3 | Project C |             2 | NULL       | NULL       |
+----+-----------+---------------+------------+------------+
3 rows in set (0.00 sec)
```

# 8. 参考

https://books.mdn.co.jp/books/3221303041/

https://laravel.com/docs/11.x/migrations

https://www.youtube.com/watch?v=plDBsStXuO4&list=PLCyDm9NTxdhLnA4tH5ToQR1K1LcWIAdOa&index=3

https://qiita.com/Takahiro_Nago/items/71d30873313862ab6818


# 9. 最後に
- documentを読みたいと思います
- 次回はModel、Controller、Viewを作成し、クエリの結果を表示して業務アプリに近いものを作ります