🕵️‍♂️

[Rails]has_manyで追加されるメソッドの中で、今まで知らなかったメソッドを触ってみる

2024/04/02に公開

1. はじめに

初めまして、Open8でバックエンドエンジニアをやっているハガと申します。
Railsの知見を深めるために、今回はhas_manyで追加されるメソッドの中で、今まで知らなかったメソッドを触ってみようと思いますー!

2. (ざっくりおさらい)Railsのアソシエーションとは何か

タイトルの件に入る前に、Railsのアソシエーションについてざっくりおさらいしようと思います。

Railsのアソシエーションとは、ActiveRecordモデル間の繋がりのことです。
モデル間にアソシエーションを設定することで、モデル間の操作をシンプルに操作できるメソッドが自動で定義されたり、アプリケーション側でモデル間のデータに不整合がないかをチェックできます。
モデル間にアソシエーションを設定するためには、has_manybelogns_toなどを利用して明示的にアソシエーションの設定を宣言する必要があります。

2.1 アソシエーションを設定しない場合

class Author < ApplicationRecord
end

class Book < ApplicationRecord
end

既存の著者の新しい書籍を作成する場合、以下のようなコードを書く必要があります。

@book = Book.create(published_at: Time.now, author_id: @author.id)

ある1人の著者を削除して、その著者に紐づく書籍も削除する場合、以下のようなコードを書く必要があります。

@books = Book.where(author_id: @author.id)
@books.each do |book|
  book.destroy
end
@author.destroy

2.2 アソシエーションを設定した場合

class Author < ApplicationRecord
  has_many :books, dependent: :destroy
end

class Book < ApplicationRecord
  belongs_to :author
end

既存の著者の新しい書籍を作成する場合、以下のようにシンプルにかけます。

@book = @author.books.create(published_at: Time.now)

ある1人の著者を削除して、その著者に紐づく書籍も削除する場合、以下のようにシンプルにかけます。

@author.destroy

アソシエーションを設定することでモデル間の操作がシンプルになって扱いやすくなったことが分かりました。
アソシエーションについてより詳細に知りたい場合はRailsガイドに載っているので、そちらを参照していただけるようお願いします。
https://railsguides.jp/association_basics.html

3. has_manyで追加されるメソッドの中で、今まで知らなかったメソッドを触ってみる

3.1 collection_singular_ids

collection_singular_idsメソッドは、コレクションに含まれるオブジェクトのidの集合を配列として返してくれます。
collection_singular_idsのcollection_singularとは、 has_many: usersを定義した際のuserという単数形を指しています。collection_singular_idsは、以下のコード例の場合だとuser_idsにあたります。
関連先のidの集合だけ欲しい場合に使えるなと思いました。

irb(main):006:0> organization = Organization.first
  Organization Load (6.0ms)  SELECT `organizations`.* FROM `organizations` ORDER BY `organizations`.`id` ASC LIMIT 1
=>
#<Organization:0x0000ffff6fa88df0
...
irb(main):007:0> organization.user_ids
   (3.9ms)  SELECT `users`.`id` FROM `users` WHERE `users`.`organization_id` = 1
=> [1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]

3.2 collection.exists?(...)

collection.exists?メソッドは、指定された条件に一致するオブジェクトがコレクションの中に存在するかをチェックして、真偽値を返します。
collection.exists?のcollectionとは、has_many: usersを定義した際のusersという複数形を指しています。collection.exists?は、以下のコード例の場合だとusers.exists?にあたります。
関連先のモデルが存在するかどうかで条件分岐したい時に使えるなと思いました。

irb(main):010:0> organization.users.exists?(deleted_at: nil)
  User Exists? (5.8ms)  SELECT 1 AS one FROM `users` WHERE `users`.`organization_id` = 1 AND `users`.`deleted_at` IS NULL LIMIT 1
=> true

3.3 collection.create!(attributes = {})

このメソッド自体は知っていたのですが、複数個オブジェクトを生成することもできるようです。
organization_idをuserにセットする手間を省けて、複数個のオブジェクトを生成できるので、便利です。

organization.users.create([
  { name: "hoge" },
  { name: "fuga" },
])

4. まとめ

アソシエーションについて、まだまだ知らないメソッドがあるので、また近いうちに学習しようと思いますー!

5. 参考記事

https://railsguides.jp/association_basics.html
https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
https://logmi.jp/tech/articles/318739

OPEN8 テックブログ

Discussion