referencesを使った外部キー制約があるカラムの作成方法（Rails）

# はじめに

[Rails チュートリアル](https://railstutorial.jp/)などで、Railsの勉強をしていて、Twitterでいう、ツイート機能を作成する際、UserモデルやTweetモデルなどのマイグレーションファイルの作成をしていた時に、UserモデルとTweetモデルを関連させたいモデルがあった時などに使う、外部キーのカラムの追加に関して調べていたら勉強になったので、まとめておきます。

# 環境

この記事では以下の環境（2018年6月19日時点）で動作確認できました。

- Ruby: 2.4.1
- Rails: 5.0.7

# 前提

お題「お気に入り機能を作成したい」と思った時、現状、ユーザー情報がある`users`テーブルと、tweetのようなデータを扱う`microposts`テーブルを関連づけて、お気に入り機能を作りたいと思ったとを仮定します。

テーブル名同士の関係としては、お気に入り機能なので、`users : microposts = 多 : 多`の関係にしたいとします。モデル同士が多対多の関係になる場合には、中間テーブルを設置するのが有効な方法です。

## 中間テーブルとは

中間テーブルとは、`users`や`microposts`の関係を接続するテーブルです。例えば、`User`が特定の`Micropost`をお気に入りする場合、`users`テーブルの id と`microposts`テーブルのidを接続するテーブルを作成します。その接続するテーブル（例：`likes`テーブル）には`user_id`と`micropost_id`を設置します。　そのテーブルのレコードに`user_id`が1で、`micropost_id`が2のものがあったとすると、idが 1の`User`がidが10の`Micropost`を好き、ということを意味します。

そんな関係の中間テーブルを作るために、まず、reference型を使ってモデルを作成していきましょう。

# モデル作成

`users : microposts = 多 : 多`の関係になるような中間テーブルである（`Like`モデル）を作りたいとします。


```bash
$ rails g model Like user:references micropost:references

Running via Spring preloader in process 4905
      invoke  active_record
      create    db/migrate/20180619073616_create_likes.rb
      create    app/models/like.rb
```

今回はログイン済みユーザー（user_idをもつユーザー）でないとコメントできないようにするために`references`でモデルを生成をしました。

また、`Like`モデルはユーザーと投稿内容の両方が存在しなければいけないのでバリデーションも設けます。

```rb:app/models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :micropost
  
  validates :user_id, presence: true
  validates :micropost_id, presence: true
end
```

## マイグレーションファイル

`rails g model`コマンドによって、下記のマイグレーションファイルが作成されました。

```ruby:20180619073616_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.references :user, foreign_key: true
      t.references :micropost, foreign_key: true

      t.timestamps
    end
  end
end
```

- `t.references :user, foreign_key: true`
    - 実際のデータベース上では、`user_id`カラムとして存在しています
- `t.references :micropost, foreign_key: true`
    - 実際のデータベース上では、`micropost_id`カラムとして存在しています

`foreign_key`は外部キー制約というもので、保存されるテーブルの整合性を高め、間違ったデータをできるだけ排除できるかという性質があります。

## マイグレーションの実行

マイグレーションの実行をすることでテーブルを作成していきましょう。

```bash
$ rails db:migrate

== 20180619073616 CreateLikes: migrating ======================================
-- create_table(:likes)
   -> 0.0059s
== 20180619073616 CreateLikes: migrated (0.0068s) =============================
```

`likes`テーブルが作成されました。

モデル生成では、`user:references micropost:references`と書きましたが

```bash
$ rails g model Like user:references micropost:references
```

実際にDBに作成された`likes`テーブルのカラムには、

- `user_id`
- `micropost_id`

が、出来ているとわかります。

```sql
mysql> show tables;
+----------------------------------+
| Tables_in_microposts_development |
+----------------------------------+
| ar_internal_metadata             |
| likes                            |
| microposts                       |
| relationships                    |
| schema_migrations                |
| users                            |
+----------------------------------+
6 rows in set (0.00 sec)

mysql> describe likes;
+--------------+----------+------+-----+---------+----------------+
| Field        | Type     | Null | Key | Default | Extra          |
+--------------+----------+------+-----+---------+----------------+
| id           | int(11)  | NO   | PRI | NULL    | auto_increment |
| user_id      | int(11)  | YES  | MUL | NULL    |                |
| micropost_id | int(11)  | YES  | MUL | NULL    |                |
| created_at   | datetime | NO   |     | NULL    |                |
| updated_at   | datetime | NO   |     | NULL    |                |
+--------------+----------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
```


# reference型を使うメリット

reference型を使うメリットは、`user`という名前で`rails g model`して、`user_id`というカラム名を自動で作成してくれることで、`users`テーブルの`id`を自動で参照（インデックス）してくれます。Rails便利。

---

以上です！

# この記事を読んだ方に

この記事を読んで、誤っている箇所をみつけたり、追記した方がいい内容などありましたら、編集リクエストやコメント欄で指摘していただけると助かります。

# 参考
- https://qiita.com/ryouzi/items/2682e7e8a86fd2b1ae47
- http://tkymtk.hatenablog.com/entry/rails-4-three-way-to-write-migration-2014-1