【Ruby on Rails】アプリ開発の基本!アソシエーションを理解してより良い開発を目指す

SPONSORED LINK

他の言語を触った後にRuby on Railsを学んでみると、とっても便利なことに気付きます。本気で使う言語をRubyに乗り換えちゃおうかな・・・と思うくらいです。

今ではRuby on Railsのほうが得意ですがね。

そんな便利なRuby on Railsですが、アソシエーションを初めて見た時はビックリしました。最初は理解できなかったんですが、もう一度ちゃんと学んでみるとかなり便利なんですね。という訳で説明していきます!これは覚えておかねばなりません!

Modelのアソシエーション

モデルのアソシエーションってなんだ?ってなりますよね。今一度、サクッとモデルについて確認だけしておきます。

モデルとはデータベースの骨格のようなものであり、コントローラーを通してモデルとデータベースをやり取りすると、データベースの中身がモデルのインスタンスとして生成させるという特徴をもちます。

簡単にいうと下のような感じです。

そんなモデルにはアソシエーションというものを定義することができます。これは何かというと、アプリ内に存在するテーブルの相互関係を改めてプログラミングとして定義するものになります。

一旦、アソシエーションの定義を確認しておきます。

 

アソシエーションの定義

モデルの主従関係、および一対多をきめるときに使用されるのがアソシエーションです。

app/models/以下に存在するモデルファイルに直接書き込むことで、モデルとモデルの主従関係を定義することができる。またモデルに記述する時は下のように書いていくとされています。

 

モデル間の主従関係を見極める方法

モデルの相互関係の見極め方は下の画像を参照してみてください。

Twitter(画像内のTwitter、間違えてますね!)を考えてみましょう。みなさんも使ったことあるでしょう。そんなTwitterですが、あなたが呟くツイートはTweetモデルに生成されます。そのツイートに対して友達がリツートしたとしましょう。そうするとRetweet_idが1番のリツイートが、Tweet_id = 1をリツイートしたということになります。

それがいくつも重なっていくと、Retweet_idが2のリツイートがtweet_id = 1をリツイートした、というようになり、Tweet_id = 1のツイートに対して幾つものリツイートを持つことになります。

このような状況を1対多というように定義します。

最初の方に説明したhas_manyとbelongs_toを使って書いてみましょう。また各モデルはそれぞれtweet.rbとretweet.rbというファイル名とします。

tweet.rb

retweet.rb

それぞれのモデルフォルダ内に存在するファイルに定義することで完了です。tweet.rbは先程の説明の通り、retweetをたくさん持っていますからhas_manyで定義します。

それに対してretweet.rbでは、1つのtweetに幾つかのretweetが存在する可能性を含みます。ですのでbelongs_toを使って対応させます。これはまさにretweetはtweetに属しているのと同様ですからね。

 

こうしてそれぞれ、アソシエーションを組んでモデル間の関係性を定義しておくことで、データのやり取りがスムーズに行われようになるのです。ではそのアソシエーションを実装して実際に使用する方法を説明していきます。

 

アソシエーションを実装する

それではアソシエーションを定義してた後の実装方法について学んでいきます。アソシエーションを定義する利点は、モデルを跨いだデータの呼び出しをより直感的に行うことができることにあります。

今回はツイッターにおけるツイートとユーザーの関係の場合について考えておきます。それでは、アソシーションを利用した場合としない場合、それぞれの方法を比べてみましょう。

 

アソシエーションを使用しない場合

今回はアソシエーションを利用せずにTweetとUserのモデル間でのやり取りする方法を再度確認します。一対多の関係としては「Tweetが多でUserが一」という状態です。

 

まずはログインしているユーザーのツイートをすべて変数@tweetsに代入するという状況を想定しています。使いみちとしてはindex.html.erbで、マイページを表示する際のすべてのツイートを表示させるような場合に使えます。

という訳で、ログインしているユーザーのツイートをすべて@tweetsに代入します。

Tweetモデルのインスタンスを生成し、user_idカラムのuser.idを検索して代入するように記述します。このuser_idというのはUserモデル内に存在するもので、user.idは現在ログインしているユーザーのidです。

 

それに対して、アソシエーション定義した場合は以下の通り。超簡単です。

userモデルにUser has many Tweetsの状態を記述して定義したので、userモデルのインスタンス.tweetsと記述するだけで、そのインスタンス(user)が所持しているツイートを取得することができる。

 

うーむ、非常に便利ですね。では、次の例について考えてみましょう。

今回は変数@tweetsに代入するのは、現在ログインしているユーザー(deviseコントローラーを使用しています)のツイートを一つのページに5件ずつ代入し、ポストされた日時の降順、つまり最新の投稿を一番上に表示する、というものです。

 

まずはアソシエーションを利用しない場合をみていきます。

それに対してアソシエーションを利用する場合です。

 

アソシエーションを利用しない場合には、whereメソッドを使用して特定のツイートのみを選択するように指示を出します。この場合はTweetsテーブル内のuser_idカラムと、現在のログインしているユーザーが一致するツイートのみを抽出します。

SQL文を学んだ人には理解しやすいかもしれません。

ちなみにcurrent_user.〇〇というのは、deviseコントローラーのメソッドであり、現在ログインしているユーザーの情報を取ってくるメソッドです。アプリを開発している最中、deviseコントローラーにお世話になると思いますので、覚えておくと良いでしょう。

 

こんな感じでアソシエーションを利用すると、より直感的にテーブルから情報を持ってくることができるので、覚えておくと良いかもしれません。

 

アソシエーションに内包する問題

非常に便利なアソシエーションですが、少しだけ問題をはらんでいます。その代表的なものがn+1問題です。これはSQL文の二重発行といわれるものです。

 

n+1問題とはなんぞや?

モデルを利用してデータベースの情報にアクセスする際はつねにSQLが発行されることになります。ただし発行されるごとに通信が走るので、大量に情報を通信するとその分だけ処理が重くなってしまいます。

アソシエーションを利用する際に発生してしまうn+1問題とは、データを呼び出す際に問題となます。例えばindexアクション内でユーザーに限らないすべてのユーザーの全ツイートを取得する処理をしていて、さらにアソシエーションを利用して、ツイートの数だけユーザー情報を呼び出しているような状況です。

ここでは二重でSQLを発行しているので、n+1問題が発生していると言えます。

 

includesメソッド

このn+1問題を解決するためには、includes(:モデル名)を使用するincludesメソッドを使用する。引数は関連モデルをシンボル型で指定します。

今回の状況においてtweetsテーブルのレコードは、必ず1つのusersテーブルのレコードに属しています。そこでincludesメソッドを利用すると、tweetsテーブルのレコードを取得する段階で関連するusersテーブルのレコードも一度に取得できるようになります。

 

まとめ

アソシエーションについて学んできました。少しだけイメージが掴めたでしょうか。

Ruby on Railsは、確かに手軽にアプリを作り上げることができます。しかしながら、それ以上に多くのことを学ばなくてはならないと感じています。多くの情報を整理しつつ学ばなくてはならないので情報過多になるかもしれませんが・・・

では頑張ってRuby on Railsを一緒に勉強していきましょう。まだまだ先は長そうです。

この記事が気に入ったら
いいね ! しよう

Twitter で
SPONSORED LINK

ABOUTこの記事をかいた人

HIRO

旅・音楽・プログラミング・ブログを愛するコンテンツ・クリエーター、ミニマリスト。ガジェット、海外ドラマ、旅行が好きで、趣味は読書。都内のIT企業でSEとして働いています。※このブログでの発言はすべて個人に帰属し、企業や団体とは一切関連がありません(PRを除く)。