Laravel プロジェクトの作成からシーダーまで

## Example プロジェクトの作成

Eloquent リレーションの動きを tinker で試すための Example プロジェクトの作成をまとめてみた。
このプロジェクトは次の記事で練習台として使う。

- [Laravel のコレクションとは](https://qiita.com/mikomey/items/f464dafe883b60a09247)
- [Eloquent のリレーションの使い方](https://qiita.com/mikomey/items/705834f47dcf4d3cbd1e)
- TBD

まずは、Laravel の新規プロジェクトの作成から。

*/var/www/sutdy/project をアプリケーションルートする場合：*

```bash
$ mkdir -p /var/www/study
$ cd /var/www/study
$ composer create-project laravel/laravel:^8.0 example
$ cd example
$ chmod -R a+w storage
$ chmod -R a+w bootstrap/cache
```
あらかじめ database を作成し、.env にデータベース接続に必要な記述を行う。

ブラウザでの動作確認を行うならドキュメントルートを定義する。
Laravel の public フォルダをドキュメントルートにするわけだが、httpd.conf でのドキュメントルート指定は固定しておき、シンボリックリンクで参照すると練習用のプロジェクトの切り替えが楽。

*/var/www/sutdy/html がドキュメントルートの場合：*
```bash
$ cd /var/www/study/
$ ln -s example/public html
$ ll
total 4
drwxrwxr-x 12 vagrant vagrant 4096 Jun 21 18:07 example
lrwxrwxrwx  1 vagrant vagrant   14 Jun 21 18:09 html -> example/public
```

#### マイグレーションの作成

Laravel の新規プロジェクトを作成したら、マイグレーションを編集する。練習なのでテーブルごとにマイグレーションを分けていない。
ユーザーに `company_id` を追加し、会社とプロジェクトのテーブルを追加した。
マイグレーションのルールは、[Eloquent 前提の MySQL データベース設計](https://qiita.com/mikomey/items/50cceaf1f5e22d8b2b72) を参照のこと。

*database\migrations\2014_10_12_000000_create_users_table.php*

```php
<?php

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

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name')->default('')->commen('ユーザー名');
            $table->string('email')->default('')->unique()->comment('メールアドレス');
            $table->integer('company_id')->default(0)->index()->comment('会社');
            $table->datetime('email_verified_at')->nullable()->comment('メール確認日時');
            $table->string('password')->default('')->comment('パスワード');
            $table->rememberToken()->comment('リメンバートークン');
            $table->datetime('created_at')->nullable();
            $table->datetime('updated_at')->nullable();
        });
        DB::statement("ALTER TABLE users COMMENT 'ユーザー';");

        Schema::create('companies', function (Blueprint $table) {
            $table->id();
            $table->string('name')->default('')->comment('会社名');
            $table->datetime('created_at')->nullable();
            $table->datetime('updated_at')->nullable();
        });
        DB::statement("ALTER TABLE companies COMMENT '会社';");

        Schema::create('projects', function (Blueprint $table) {
            $table->id();
            $table->string('name')->default('')->comment('プロジェクト名');
            $table->datetime('created_at')->nullable();
            $table->datetime('updated_at')->nullable();
        });
        DB::statement("ALTER TABLE projects COMMENT 'プロジェクト';");

        Schema::create('project_user', function (Blueprint $table) {
            $table->integer('project_id')->default(0)->comment('プロジェクト');
            $table->integer('user_id')->default(0)->comment('ユーザー');
            $table->primary(['project_id', 'user_id']);
            $table->index('user_id');
        });
        DB::statement("ALTER TABLE project_user COMMENT 'プロジェクト・ユーザー';");
    }

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

マイグレーションを実行してみる。データベース接続に失敗したなら .env を確認する。

```bash
php artisan migration
```

#### モデルの追加

```bash
php artisan make:model Project
php artisan make:model Company
```

artisan コマンド作成したモデルには、最低でも `protected $nullabl = ['id'];` を追加しておく。`$fillable` か `$nullable` のどちらかがないとデータが保存されない。

User と Project にリレーションを定義する。

*app/Models/User.php*
```php
public function company()
{
    return $this->belongsTo(Company::class);
}

public function projects()
{
    return $this->belongsToMany(Project::class, 'project_user');
}
```

*app/Models/Project.php*
```php
public function users()
{
    return $this->belongsToMany(User::class, 'project_user');
}
```

*app/Models/Company.php*
```php
public function users()
{
    return $this->hasMay(User::class);
}
```


#### ファクトリー

UserFactory はそのままでも問題ないので、ProjectFactory と CompanyFactory を追加する。

```bash
php artisan make:factory ProjectFactory
php artisan make:factory CompanyFactory
```
[マイグレーションに必ず DEFAULT を定義する](https://qiita.com/mikomey/items/50cceaf1f5e22d8b2b72) ならば、ファクトリーの中身は空でも問題なくレコード作成に成功する。
それでは味気ないので、それぞれ name の指定だけ書き加える。
Project は `faker->city()`、Company は `faker->company()` でよいかな。faker のメソッドについては「faker チートシート」等で検索しよう。

```php
public function definition()
{
    return [
       'name' => $this->faker->city() . 'プロジェクト',
    ];
}
```

これでダミーレコードが作成できるようになった。tinker で動作を確認する。

```php
$ php artisan tinker
Psy Shell v0.11.4 (PHP 7.4.11 — cli) by Justin Hileman
>>> App\Models\Project::factory()->create()
=> App\Models\Project {#3624
     name: "吉本市プロジェクト",
     updated_at: "2022-06-25 01:09:23",
     created_at: "2022-06-25 01:09:23",
     id: 1,
   }

>>> App\Models\Company::factory()->create()
=> App\Models\Company {#3623
     name: "有限会社 坂本",
     updated_at: "2022-06-25 01:11:14",
     created_at: "2022-06-25 01:11:14",
     id: 1,
   }
```

#### シーダー

ファクトリーはあくまで１ダミーレコードの雛形にすぎない。
モデル同士の有機的な結びつきを考慮してテストデータを作成するためにシーダーを書く。

database/seeders/DatabaseSeeder.php

```php
public function run()
{
    // プロジェクトを先に作成して、その id を取得しておく
    \App\Models\Project::factory(10)->create();
    $project_ids = \App\Models\Project::pluck('id');

    // 会社を10社作成
    \App\Models\Company::factory(10)->create()->each(function ($company) use ($project_ids) {
        // それぞれの会社にランダムな人数のユーザーを作成
        \App\Models\User::factory(rand(3, 10))->create([
            'company_id' => $company->id,
        ])->each(function ($user) use ($project_ids) {
            // それぞれのユーザーにプロジェクトを紐づけ
            // プロジェクトはシャッフルして、ランダムな数でカット
            $ids = $project_ids->shuffle()->slice(rand(0, $project_ids->count()));
            foreach ($ids as $project_id) {
                DB::table('project_user')->insert([
                    'user_id'    => $user->id,
                    'project_id' => $project_id,
                ]);
            }
        });
    });
}
```

準備ができたらマイグレーションを実行しテストデータも同時に作成だ。

```bash
php artisan migrate:fresh --seed --step
```