改訂版!!Laravel8でTwitterクローンを作る【Step01 : DB設計とMigration】

## はじめに

どーも [弁護士ドットコム](https://corporate.bengo4.com/) でエンジニアしていますナミザトです👨‍🚀

以前QiitaでLaravel5.8で作成した記事が好評だったのでそのリニューアルLaravel8.x版です!
せっかくなのでLaravel8から実装された`Jetstream🚀`や変更された実装方法なども試してみたいと思います!

> *2020.09 現在時点*

## 概要

最近では色々教材も増えてきて、Laravel5.8で執筆していた頃よりも需要は少ないと思いますが、
スクール形式(知ってる前提もあるけどなるべく機能の一つ一つを説明)でやりたい方や、
Laravel8.xを試してみたいけど情報がなさすぎて詰まった時が怖いなどの方達の助けになれればと思い執筆します!

> あとめちゃくちゃ書き直してるのでLaravel5.8やったよ!って人もぜひ><

早速記念すべき第1回はDB設計とMigration(マイグレーション)を学んでいきます

## 前提

- PHP&DB知識がある程度ある
- MVC構造をある程度理解している
- 環境構築済み(DB接続含め)

> Laravel初心者を脱却したい！という方はこちらも合わせてどうぞ！
[Laravelﾜｶﾝﾈ(ﾟ⊿ﾟ)から「完全に理解した（）」までステップアップ](https://qiita.com/namizatop/items/801da1d03dc322fad70c)


## 環境

- Mac
- Docker
- Laravel8.x

>  今回はDockerを使用していますが、Vagrant/MAMP/ローカル何でもOKです！

## 要件定義

まずはTwitterチュートリアルと言えど、Twitterをそのまま作るのは不可能()なので、
今回最低限必要な機能をざっと洗い出してみましょう!

- ユーザーは新規登録&ログインができる
- ログイン状態でなければ記事の閲覧&投稿はできない
- ユーザーは記事を投稿できる(ツイート)
- ツイートに対してコメントといいねができる
- コメントにはいいねとコメントはできない(~~めんどくさい~~)
- ユーザー同士はフォロー&フォロー解除ができる
- 自身のタイムラインにはフォローしているユーザーのツイートのみ閲覧できる

## DB設計

では上記の要件定義で洗い出した機能から必要になりそうなテーブルを洗い出してみましょう
ざっとこんな感じでしょうか。

- `users`テーブル
  - ユーザーを管理するテーブル
- `tweets`テーブル
  - ユーザー毎のツイートを管理するテーブル
- `comments`テーブル
  - ツイートに対したコメントを管理するテーブル
- `favorites`テーブル
  - ツイートに対したいいねを管理するテーブル
- `followers`テーブル
  - フォロー関係を管理するテーブル 

文字だけじゃ分かりづらいと思うので、画像にしてみましょう

![164immexc5s4htf54vukg744u9b5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293438/c50c4db0-4623-bc0e-a429-94d1315272de.jpeg)

> 0と1の区別してないのは許してください

何となく一対多の関係を想像できたと思いますので(~~無理やり~~)
早速次の章で上記のテーブルを作成していきましょう!

## Migration(マイグレーション)

`Migration`とは言うならばテーブルの設計図/記録書のようなものです
Laravelではざっくり3つのメリットがあります

1. SQLを書かずにLaravelの仕様に沿った書き方でテーブルを作成/削除などができる
2. テーブルの作成/更新/削除などをファイルで日付順で管理するため現状把握しやすい
3. ファイル形式なのでわざわざSQLファイルをimportせずとも簡単に共有でき、実行も簡単

> :point_up: Laravelに限らず最近のwebフレームワークではほとんどMigration機能を実装しています

### artisanコマンド

Laravelには`artisan`コマンドというターミナルで使用できる便利なコマンドが用意されています
例えば以下のコマンドを入力すると`app/Http/Controllers`の中に`SampleContorller.php`というファイルが最初からクラスやnamespaceを定義した状態で生成されます

```terminal
php artisan make:controller SampleController
```

上記の要領でMigrationとModelを作っていきたいと思います!

### Migrationファイルの作成

テーブルを作成するときは下記のようなコマンドを入力します

```terminal
php artisan make:migration create_example_table
```

Laravelでは大抵の場合テーブル1つに付き1つのモデルを用意することが多いので、(中間テーブル等は除く)
今回は`artisan`コマンドに用意されているMigrationとModelを同時に作成するコマンドで試していきたいと思います


> :point_up: 以下は全てターミナルでLaravelのプロジェクト内で実行します

`tweets`テーブル

```terminal
php artisan make:model Tweet -m
```

`comments`テーブル

```terminal
php artisan make:model Comment -m
```

`followers`テーブル

```terminal
php artisan make:model Follower -m
```

`favorites`テーブル

```terminal
php artisan make:migration create_fovorites_table
```

> :warning: こちらは中間テーブルとして使用するのでモデルは不要

### Migrationの書き方

ユーザー関連のみ最初からMigrationが用意されているのでそれを元に説明していきます

> 2014_10_12_000000_create_users_table.php

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
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');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

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

今回はテーブルを作成するので`up()`メソッドに作成するテーブルと作成するカラムを定義しています
逆に`down()`メソッドにはrollbackする際の定義を書きます(テーブルを削除する)

> カラムの書き方はSQLでINSERT TABLEしたことある人なら何となく分かると思うので省略します
> 詳しく知りたい方は [こちら](https://readouble.com/laravel/8.x/ja/migrations.html#creating-columns) から

Migrationの使い方が何となく伝わったと思うので、実際に`users`テーブルに対して変更を加えていきます

> :point_up: 以下からコードを省略して`up()`メソッドだけ記述します

#### usersテーブル

> 2014_10_12_000000_create_users_table.php

```php
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('screen_name')->unique()->comment('アカウント名');
        $table->string('name')->comment('ユーザー名');
        $table->string('profile_image')->nullable()->comment('プロフィール画像');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}
```

#### tweetsテーブル

> 2020_09_25_060211_create_tweets_table.php

```php
public function up()
{
    Schema::create('tweets', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->comment('ユーザID');
        $table->string('text')->comment('ツイート本文');
        $table->softDeletes();
        $table->timestamps();

        $table->index('user_id');
    });
}
```

#### commentsテーブル

> 2020_09_25_060252_create_comments_table.php

```php
public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->comment('ユーザID');
        $table->foreignId('tweet_id')->constrained()->comment('ツイートID');
        $table->string('text')->comment('コメント本文');
        $table->softDeletes();
        $table->timestamps();

        $table->index(['user_id', 'tweet_id']);
    });
}
```

#### favoritesテーブル

> 2020_09_25_060332_create_favorites_table.php

```php
public function up()
{
    Schema::create('fovorites', function (Blueprint $table) {
        $table->foreignId('user_id')->constrained()->comment('ユーザID');
        $table->foreignId('tweet_id')->constrained()->comment('ツイートID');

        $table->unique(['user_id', 'tweet_id']);
    });
}
```
favoriteテーブルを中間テーブルとして使用する為、カラムは`user_id`と`tweet_id`のみとしてます

> :point_up: この部分では複合インデックスを作成しています

```php
$table->unique(['user_id', 'tweet_id']);
```

#### followersテーブル

自分自身は`following_id`になり、フォローしているユーザーは`followed_id`

> 2020_09_25_060412_create_followers_table.php

```php
public function up()
{
    Schema::create('followers', function (Blueprint $table) {
        $table->foreignId('following_id')->constrained('users')->comment('フォローしているユーザーのID');
        $table->foreignId('followed_id')->constrained('users')->comment('フォローされているユーザーのID');

        $table->unique(['following_id', 'followed_id']);
    });
}
```

ここまでさらりと書きましたが、実はMigrationの書き方もLaravel7.xから新しい記述方法が推奨されており、今回はその推奨方法に従って記述しています

> 詳しくは [こちらの記事](https://qiita.com/ucan-lab/items/69f05576c2f1e38c34d9) から

> :warning: Migrationは日付順で上から実行されるため外部キー制約を定義している場合は順番に気をつけましょう

### Migrationを実行

以下のコマンドで先ほど作成したMigrationファイルを実行してDBにテーブルを作成します!

```terminal
php artisan migrate
```

`Migration table created successfully.`と表示されたら成功です
第1回はとりあえずここまで!

---

## 最後に

弁護士ドットコムでは一緒に働く仲間を募集しています
求人情報は [こちら](https://hrmos.co/pages/bengo4/jobs) から!!

![](https://storage.googleapis.com/zenn-user-upload/umeb5nr4uakwc0qe7g9dyqafq0cl)