勉強会での小話5

# はじめに

この記事は2023年度の振り返りです。

こちらの続きとなります。

https://qiita.com/youfuku/items/09f387f1e9f14e9469da

問題を解いてる時の実話を元に、作成しております

# 勉強会内の小話

講師：前回、joinの中で紐づけをしてたけど、直接呼べるようにリレーションを組んでおけば楽なので、今回はそれをやってみよう！

初学者達：わかりました！has_manyのthroughですよね！参考書で勉強してる時に読みました！

講師：知ってるなら話は早い。やってみよう！

初学者達：はい！

初学者達：こんな感じでどうでしょう！
```ruby
class Comic < ApplicationRecord
  has_many :writes, foreign_key: 'book_id'
  has_many :author, through: :writes
end
```

```ruby
p Comic.joins(:author).where(author: { name: '冨樫 義博' })
```

講師：発行されるクエリを確認してみよう
```sql
SELECT `comics`.* 
FROM `comics` 
INNER JOIN `writes` ON `writes`.`book_id` = `comics`.`id` 
INNER JOIN `authors` `author` ON `author`.`id` = `writes`.`user_id` 
WHERE `author`.`name` = '冨樫 義博'
```

講師：うん。前回と同じになって、rubyのソースは少し短くできたね。ここの問題としては終了だけど、せっかくhas_manyを触ってるから深堀をしてみよう。

講師：今リレーションの名前をモデルもしくはテーブル名を割り当ててるけど、それを変えてみよう

初学者達：ん？こんな感じですか？
```ruby
class Comic < ApplicationRecord
  has_many :kaku, foreign_key: 'book_id'
end
```

初学者達：これ絶対つながらないので、どのクラスにつなぐかオプションが必要ですよね。。。

https://railsdoc.com/page/has_many

初学者達：これでどうでしょう？
```ruby
class Comic < ApplicationRecord
  has_many :kaku, foreign_key: 'book_id', class_name: 'Write'
end
```

講師：確認してみよう
```ruby
comic = Comic.all.first
comic.kaku
```
```sql
SELECT `writes`.* FROM `writes` WHERE `writes`.`book_id` = 1
```

講師：OK。あってるね。これも前回同様にリレーション名に関連モデル名を指定したことで紐づけするクラス名をrailsが解釈をしててくれてたわけだ。

初学者達：なるほど・・・

講師：じゃぁ、同じ要領で他のも修正してみよう

初学者達：はい！

初学者達：せんせー。ここまで書いたんですが、詰まりました！
```ruby
class Comic < ApplicationRecord
  has_many :kaku, foreign_key: 'book_id', class_name: 'Write'
  has_many :kaita_hito, through: :kaku
end
```
```ruby
class Write < ApplicationRecord
  belongs_to :hito, foreign_key: 'user_id', class_name: 'Author'
  belongs_to :manga, foreign_key: 'book_id', class_name: 'Comic'
end
```
```ruby
class Author < ApplicationRecord
  has_many :kaita, foreign_key: 'user_id', class_name: 'Write'
end
```
```ruby
comic = Comic.all.first
comic.kaita_hito
/usr/local/bundle/gems/activerecord-7.1.2/lib/active_record/reflection.rb:1092:in `check_validity!': Could not find the source association(s) "kaitahito" or :kaitahito in model Write. Try 'has_many :kaitahito, :through => :kaku, :source => <name>'. Is it one of hito or manga? (ActiveRecord::HasManyThroughSourceAssociationNotFoundError)
```

講師：エラーに全てヒントが出てるじゃん。。。

初学者達：sourceを使えっていうのは分かったんですが、なんで「hito or manga」なのかがイマイチ。。。

講師：なるほど。フローで考えると、こんな感じかな
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632357/c8bc5b51-eae0-0564-07f0-e332192593e2.png)

初学者達：なるほど！。sourceって、リレーション先でのリレーション名を指定するのですね！

初学者達：ん？ってことは、前回のも？

講師：そう。前回のcomicモデルにauthorのリレーションを組んだ際も、railsがsourceはauthorと解釈して、こんな感じにリレーションを組んでくれてたんだ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632357/ea8e8fdd-0215-9849-afa4-4dd27e357f64.png)

初学者達：へーーー。すごいですね

講師：そうだな。ただ、今回はリレーション名を関連モデル名とは異なる名称にしてるので、自分で指定してやる必要がある

初学者達：納得しました！これでどうでしょう！？
```ruby
class Comic < ApplicationRecord
  has_many :kaku, foreign_key: 'book_id', class_name: 'Write'
  has_many :kaita_hito, through: :kaku, source: :hito
end
```
```ruby
class Write < ApplicationRecord
  belongs_to :hito, foreign_key: 'user_id', class_name: 'Author'
  belongs_to :manga, foreign_key: 'book_id', class_name: 'Comic'
end
```
```ruby
class Author < ApplicationRecord
  has_many :kaita, foreign_key: 'user_id', class_name: 'Write'
end
```

講師：確認してみよう
```ruby
comic = Comic.all.first
comic.kaita_hito
```
```sql
SELECT `authors`.* 
FROM `authors` 
INNER JOIN `writes` ON `authors`.`id` = `writes`.`user_id` 
WHERE `writes`.`book_id` = 1
```
```ruby
=> 
[#<Author:0x00007f18600fad50
  id: 1,
  name: "岸本 斉史",
  created_at: Sat, 23 Dec 2023 02:56:25.453889000 UTC +00:00,
  updated_at: Sat, 23 Dec 2023 02:56:25.453889000 UTC +00:00>]
```

講師：うんいい感じですね。

初学者達：やったー！

講師：次回は、さらに発展型をしてみよう

次回へ続く（次回が最終回です）

# さいごに
ここはよく甘い認識のまま利用してました！っていう声が多かったので、常々記事にしたいと思っていました。
この部分を説明したいがゆえに、長い説明を入れていたといっても過言ではありません。(前部分が理解できていないと関係上、説明しづらいのです。)
ここまで連続で読まれた方、読みにくい文章をご拝読頂きましてありがとうございました。次回が最終回となります。最後まで読んで頂けますと嬉しい限りです。