1
0

More than 5 years have passed since last update.

Ruby on Rails によるモデルの自己参照

Last updated at Posted at 2019-03-17

はじめに

こんにちは!
見習いエンジニアの 古内 と申します。

今回は以前作成したものに実装した、モデルの自己参照の記事となります。
対象にしているのは、progate などで勉強を終えたが「モデルのアソシエーションがわからない!」といった方々です。
実際に私自身も「基礎的なことはなんとなくわかったけど、モデル同士のやり取りとかその先が全然わからない!」と苦手意識を持っていました。苦手意識はそれに関する知識が乏しいためにできてしまったので、繰り返し学習することで払拭することができると思います(私もこの記事を書きながら復習しました!)。

ここで必要なのはアソシエーションに関する知識です。
アソシエーションとは日本語に訳すと「関連」という意味で、Rails ではモデル同士の関連付けに使われます。
始めはわかりにくいものだと思いますが、使う場面も多いのでこの記事で少しでも理解してもらえたら幸いです。

環境

  • Ruby 2.5.1
  • Ruby on Rails 5.2.2
  • Docker
  • Docker Compose

準備

では環境の準備から始めましょう。
私はこちらを参考に環境構築をしました。
簡単に Rails 開発環境の構築ができるので、みなさんも活用してみてください!(こちらを使うときは最後の Docker Compose の起動まで進めてください)

実践

じゃんけんを例にとって説明させてもらいます。
グーはチョキに強く、チョキはパーに強く、パーはグーに強い。
この三竦みの関係をモデルで実装していきたいと思います。

モデルの作成

まずじゃんけんモデルを作成します。
カラムは name のみで OK です。

$ rails g model janken name:string

次に中間テーブルを作成します。
中間テーブルとはモデル間で多対多のアソシエーションをするとき、その間に挟むものです。今回はじゃんけんなので勝つ相手と負ける相手はそれぞれ1つずつだけなのですが、実際はここまで単純なことは滅多にないと思うので、多対多で実装します。

$ rails g model win to_janken_id:integer from_janken_id:integer

どれ ( from_janken_id ) が、どれ ( to_janken_id ) に対して勝てるか、という関係を表しています。

モデルの作成が終わったらマイグレーションを実行しましょう。

$ rails db:migrate

それではモデルの状態を確認してみましょう。
db ディレクトリの中に schema.rb というファイルがあります。
これには現在のモデルの状態が書かれているので、

schema.rb
ActiveRecord::Schema.define(version: 2019_03_16_092939) do

    create_table "jankens", force: :cascade do |t|
        t.string "name"
        t.datetime "created_at", null: false
        t.datetime "updated_at", null: false
    end

    create_table "wins", force: :cascade do |t|
        t.integer "to_janken_id"
        t.integer "from_janken_id"
        t.datetime "created_at", null: false
        t.datetime "updated_at", null: false
    end

end

こうなっていれば成功です。

モデル間のアソシエーション

続いてモデル間のアソシエーションを追加します。
先程作成した Janken モデルと Win モデルにそれぞれ次のように追加してください。

janken.rb
class Janken < ApplicationRecord
    has_many :wins, foreign_key: 'to_janken_id' #追加
    has_many :wins, foreign_key: 'from_janken_id' #追加
    has_many :win, through: :wins, source: :to_janken #追加
end
win.rb
class Win < ApplicationRecord
    belongs_to :to_janken_id, class_name: 'Janken' #追加
    belongs_to :from_janken_id, class_name: 'Janken' #追加
end

has_many とは一対多のアソシエーションする際、そのアソシエーション元に定義するものです。他にも has_one というものがありますが、ここでは説明を省かせてもらいます。
1 つ目の has_many は誰の ( wins ) 何を ( to_janken_id ) 関連付けるかを定義しています。
2 つ目も同じですね。
3 つ目は最後の確認の際に必要なものです。

belongs_to は has_many で定義したアソシエーション先に定義するものです。
こちらも has_many と一緒で誰の ( Janken ) 何を ( to_janken_id ) 関連付けるか定義しています。
このように中間テーブルを挟むことで一対多の実装しかできない has_many で多対多を実装することができます。

インスタンスの生成

ではインスタンス ( グー、チョキ、パー ) を生成してみましょう。
まずはコンソールを開き、

$ rails c

インスタンスを生成します。

$ rock = Janken.new(id: 1, name: 'rock')
$ rock.save
$ scissors = Janken.new(id: 2, name: 'scissors')
$ scissors.save
$ paper = Janken.new(id: 3, name: 'paper')
$ paper.save

次にじゃんけんの相性を作ります。

$ rock_win = Win.new(to_janken_id: scissors.id, from_janken_id: rock.id).save
$ scissors_win = Win.new(to_janken_id: paper.id, from_janken_id: scissors.id).save
$ paper_win = Win.new(to_janken_id: rock.id, from_janken_id: paper.id).save

検証

では実際に確認してみましょう。

$ janken_win = Janken.find(1).win

Janken モデルの id が 1 のものは rock ( グー ) なので scissors ( チョキ ) が返ってきます。

#<ActiveRecord::Associations::CollectionProxy [#<Janken id: 2, name: "scissors", created_at: "2019-03-16 15:25:32", updated_at: "2019-03-16 15:25:32">]>

以上で完成です!

最後に

自己参照はユーザー同士のフォローなど、一つのモデルの中でアソシエーションしなければいけないので、わかりくいですが様々なところで役に立つ知識です。
この解説で少しでも理解に近づいてもらえたら幸いです。

参考

RAILS GUIDES - Active Record の関連付けこちらに has_one や has_many 等の詳しい説明がされています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0