###### tags: `rails` `モデル` `関連付け`
# rails でのモデル間の関連付け (1対多)
# 前説
これまで[会員登録](https://hackmd.io/@good1luck21/rJ1hJ9bJO)と[新規投稿](https://hackmd.io/@good1luck21/HyU-55bk_)などをまとめましたが、
**今回は上記2つの機能の掛け合わせのご説明をしたいと思います。**
みなさんがよく使われるSNSアプリなどは誰が会員登録 or ログインをしていて、その人がどのような投稿をしたのかを**紐付け**ています。
その**紐付け**によってログインするとみなさんが投稿した内容の一覧などが見れるようになっています。
今回はどのように**紐付け**を行うのかについて解説していきたいと思います。
## 関連付け前準備
- [ ] UserモデルとPostモデルの作成
- [ ] 会員登録用にBcryptの機能をインストールする
- [ ] パスワードの暗号化を行うため、`has_secure_password`をUserモデルに記載する
今までの記事で上記内容に関してご説明させて頂きましたので、準備に関してご対応頂けそうでしょうか?
以下に簡単に流れを記載致しますが、まずは皆さんで一旦頑張ってみてもらえたらなと思います!
## コーディング事前準備解説
### UserモデルとPostモデルの作成
- Userモデル
```bash=
rails g model User name:string email:string password_digest:string
```
- Postモデル
```bash=
rails g model Post title:string content:string
```
一旦上記のようにモデルを作っておきましょう!紐付けを実装する段階でカラムを追加しますが、今は基本的な内容でカラムを定義しておけば大丈夫です!
モデルを作ったら`rails db:migrate`を忘れずに実行しましょう!
### 会員登録用にBcryptの機能をインストールする
```rails=
.
..省略
...
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'
```
`bcrypt`がデフォルトではコメントアウトされているので、コメントを取り除いてあげましょう!
コメントを取ったら忘れずに、`bundle install` を行います!
### has_secure_passwordをUserモデルに記載する
```rails=
class User < ApplicationRecord
has_secure_password
end
```
何も気にする必要はありません!文言を上記のようにモデルへ追記するだけです!
------------------
ここまでが事前準備の解説になりますが、出来ましたでしょうか?
出来なかった方は諦めずにもう一度トライしてみてください!
------------------
# 序章。会員登録機能の実装
解説は[**ここ**](https://hackmd.io/@good1luck21/rJ1hJ9bJO)を参照してください。
**会員登録の実装が出来ないことには、紐付け出来ない**ので、まずここの実装が出来ない方は復習を行いましょう!
# 本番。投稿との紐付け
さて、ここから本番になりますので、解説をしていきたいと思います。
まずこれから作成しようとしているユーザーと投稿の紐付けを図で見てみたいと思います。
```mermaid
classDiagram
User --|> Post : association
User --|> Post1 : association
User --|> Post2 : association
User --|> Post3 : association
class User
User : +id 1
User : +name TakeruSato
User : +mail "test@test.com"
User : +String password
User : +String password_confirmation
class Post
Post : +id 1
Post : +String Title
Post : +String Content
Post : +user_id 1
class Post1
Post1 : +id 2
Post1 : +String Title
Post1 : +String Content
Post1 : +user_id 1
class Post2
Post2 : +id 3
Post2 : +String Title
Post2 : +String Content
Post2 : +user_id 1
class Post3
Post3 : +id 4
Post3 : +String Title
Post3 : +String Content
Post3 : +user_id 1
```
上記のようにユーザー1(例:Takeru)は1人のユーザーですが、そのユーザーが投稿する内容はたくさんあると思います。
上記のような1人のユーザーと複数の投稿を紐付けるのを**モデルの関連付け**といいます。
モデルの関連付けでは、たくさんある側、今回はPost側ではUserに所属する。User側ではたくさんのPostがあるよとそれぞれモデルに定義して上げる必要があります。
今回は下記の通りとなります。
## モデルの関連付け & Postテーブルへカラム追加
### モデルの関連付け
- Userモデル
```rails=
class User < ApplicationRecord
has_secure_password
has_many :post
end
```
- Postモデル
```rails=
class Post < ApplicationRecord
belongs_to :user
end
```
上記でモデルの関連付けは完了です!
### Postテーブルへカラム追加
カラム追加に関しては[こちら](https://hackmd.io/@good1luck21/BJoMq_w1_#%E3%82%AB%E3%83%A9%E3%83%A0%E3%81%AE%E8%BF%BD%E5%8A%A0)を御覧ください!
```bash=
rails g migration AddUserIdToPosts user_id:integer
```
Postテーブルに対してどのユーザーが投稿した内容なのかを紐付けるかを識別するために、user_idカラムを追加します。
こうすることで、user_idでPostテーブルの検索なども出来るようになるため、忘れずにつけるようにしましょう。
マイグレーションファイルを作成したら、`rails db:migrate`を実行しましょう!
--------------------
ここまでで関連付けに関しては終了です!
以下では実際にrails consoleを利用してユーザーと投稿を紐付けての投稿やユーザーの投稿一覧の取得の仕方を見てみましょう!
rails consoleで出来た内容はコントローラーで貼り付けても同様のことが出来ますので、実装に苦労している方はまずコンソールで試してみましょう!
--------------------
# rails console で投稿紐付け!
```rails=
rails c
# 新規User作成
irb(main):001:0> user = User.new(name: "takeru", email: "test@test.com", password: "123456", password_confirmation: "123456")
=> #<User id: nil, name: "takeru", email: "test@test.com", password_digest: "$2a$12$w9L0gvTlgwNyTegKsMQacu/bhsBVWtwcHCfbt0SW2K3...", created_at: nil, updated_at: nil>
irb(main):002:0> user.save
(0.1ms) begin transaction
User Create (0.9ms) INSERT INTO "users" ("name", "email", "password_digest", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["name", "takeru"], ["email", "test@test.com"], ["password_digest", "$2a$12$w9L0gvTlgwNyTegKsMQacu/bhsBVWtwcHCfbt0SW2K3xqZV/ZzemS"], ["created_at", "2021-01-24 07:54:15.596628"], ["updated_at", "2021-01-24 07:54:15.596628"]]
(1.4ms) commit transaction
=> true
# Userに紐付いた投稿の作成
irb(main):001:0> user = User.find_by(id: 1)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "takeru", email: "test@test.com", password_digest: "$2a$12$w9L0gvTlgwNyTegKsMQacu/bhsBVWtwcHCfbt0SW2K3...", created_at: "2021-01-24 07:54:15", updated_at: "2021-01-24 07:54:15">
irb(main):002:0> user.posts.build(title: "test", content: "this is test post for user 1")
=> #<Post id: nil, title: "test", content: "this is test post for user 1", user_id: 1, created_at: nil, updated_at: nil>
irb(main):003:0> user.save
(0.1ms) begin transaction
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Post Create (0.6ms) INSERT INTO "posts" ("title", "content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["title", "test"], ["content", "this is test post for user 1"], ["user_id", 1], ["created_at", "2021-01-24 07:59:25.656421"], ["updated_at", "2021-01-24 07:59:25.656421"]]
(1.1ms) commit transaction
=> true
# Postを見てみるとuser_idにも「1」が入り、ユーザーに紐付いて投稿されている。
irb(main):006:0> Post.all
Post Load (0.2ms) SELECT "posts".* FROM "posts" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Post id: 1, title: "test", content: "this is test post for user 1", user_id: 1, created_at: "2021-01-24 07:59:25", updated_at: "2021-01-24 07:59:25">]>
irb(main):007:0>
# ユーザーの投稿一覧を取得
irb(main):007:0> user = User.find_by(id: 1)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "takeru", email: "test@test.com", password_digest: "$2a$12$w9L0gvTlgwNyTegKsMQacu/bhsBVWtwcHCfbt0SW2K3...", created_at: "2021-01-24 07:54:15", updated_at: "2021-01-24 07:54:15">
irb(main):008:0> user.posts
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Post id: 1, title: "test", content: "this is test post for user 1", user_id: 1, created_at: "2021-01-24 07:59:25", updated_at: "2021-01-24 07:59:25">]>
irb(main):009:0>
# 何件か投稿を作成して、一覧として取得できるか見てみる。
irb(main):009:0> user = User.find_by(id: 1)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "takeru", email: "test@test.com", password_digest: "$2a$12$w9L0gvTlgwNyTegKsMQacu/bhsBVWtwcHCfbt0SW2K3...", created_at: "2021-01-24 07:54:15", updated_at: "2021-01-24 07:54:15">
irb(main):010:0> 15.times do |n|
irb(main):011:1* user.posts.build(title: "test title #{n + 1}", content: "test content #{n + 1}")
irb(main):012:1> end
=> 15
irb(main):013:0> user.save
(5.5ms) commit transaction
=> true
irb(main):014:0> user.posts
Post Load (0.2ms) SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Post id: 1, title: "test", content: "this is test post for user 1", user_id: 1, created_at: "2021-01-24 07:59:25", updated_at: "2021-01-24 07:59:25">, #<Post id: 2, title: "test title 1", content: "test content 1", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 3, title: "test title 2", content: "test content 2", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 4, title: "test title 3", content: "test content 3", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 5, title: "test title 4", content: "test content 4", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 6, title: "test title 5", content: "test content 5", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 7, title: "test title 6", content: "test content 6", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 8, title: "test title 7", content: "test content 7", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 9, title: "test title 8", content: "test content 8", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, #<Post id: 10, title: "test title 9", content: "test content 9", user_id: 1, created_at: "2021-01-24 08:15:48", updated_at: "2021-01-24 08:15:48">, ...]>
irb(main):015:0>
```
上記のようにユーザーとポストを紐付けることで、新しく使えるメソッドもあり、そのメソッドを使用することで効率よく紐付けの投稿などを作成することが出来る。
関連付けを行ったことで、今回の場合使えるようになったメソッドの一覧を以下に記載します。
違ったモデル名などで関連付けを行った場合は適宜名前を変更してみましょう。
| メソッド | 用途 |
| -------- | -------- |
| user.posts | ユーザーの投稿一覧を返す。 |
| post.user | 投稿に紐付いたユーザーを返す。 |
| user.posts.build(title: "xxx", content: "yyy") | ユーザーに紐付いた新しい投稿を返す |
| user.posts.find(1) | ユーザーに紐付いた投稿ID1を返す。 |
関連付けを行うことで、どのユーザーが投稿したのか判別できるようになったのがわかりましたか?
記事を読むだけではご理解を深めるのは難しいかと思いますので、ここまでの内容をご自身でおさらいしてみましょう!