21:00概要Railsでウェブアプリケーションを作っていると、例えば記事に複数のユーザが属していて、さらにユーザにも複数の記事が属しているような、多対多の関係をデータベースで定義したいというときがあると思います。このような多対多の関係を、Railsで表現する方法を紹介します。5.1前提ここでは、記事を表すArticleモデルと、ユーザを表すUserモデルのふたつを多対多の関係で表現する方法を紹介します。そのため、実際に利用する場合は、適切にモデル名などを書き換えてください。ArticleモデルとUserモデルは次のコマンドで作成します。User中間テーブル多対多の関係を表す場合、中間テーブルというものを使います。中間テーブルでは、記事とユーザのデータのIDが保存され、記事からユーザの情報をみるときには、中間テーブルに保存されている記事IDとユーザIDの情報を元にユーザの情報を見つけ出します。ユーザから記事の情報をみるときも同様に、中間テーブルに保存されている記事IDとユーザIDの情報を元に記事の情報を見つけ出します。中間テーブルを作りましょう。中間テーブルとして、ArticleUserモデルを作成します。user:referencesRailsで、一般的に中間テーブルの名前は多対多の関連を作りたい2つのモデルの名前を繋げたものを使います。ArticleUserという中間テーブルのモデルには、article:referencesとuser:referencesという多対多の関係を作るモデルの名前をカラム名とした参照を定義します。db:migrateなどを使うと、テーブルにはカラム名としてarticle_idやuser_idというように_idをつけて定義されます。ArticleUserモデルのソースコードを見ると、次のようになっていると思います。:userendbelongs_toは、どのモデルに属するかを指定します。ArticleモデルとUserモデルの2つの関連を表すために、belongs_toをふたつ書きます。db:migrateなどを実行してデータベースへ反映させます。次に、既存のArticleモデルやUserモデルに設定を追加して、それぞれのモデルを参照できるようにします。Articleモデルは次のようにします。:article_usersendUserモデルは次のようにします。:article_usersendhas_manyというものをモデルのなかで使って、モデル同士の関連を表現します。:destroyというものを書いています。これは、Articleモデル、UserモデルともにArticleUserモデルの情報を複数持っているということを表します。複数持っているということから、:article_usersのように複数形で書きます。dependent:は、ArticleモデルやUserモデルのデータを削除したときに、もしArticleUserモデルのデータを持っているときの挙動を制御するもので、:destroyを書いた場合、その中間テーブルのデータも削除します。デフォルトの挙動では、中間テーブルのデータを削除しないため、ゴミデータが溜まっていくことになるため、ここでは:destoryを指定しました。同じように、has_manyを書いていますが、Articleモデルの方では、:usersと書き、Userモデルのデータを複数持つということを定義し、Userモデルの方でも、:articlesと書き、Articleモデルのデータを複数持つということを定義しています。このとき、ArticleモデルやUserモデルが、それぞれuser_idカラムやarticle_idカラムを持っている場合はこのままでもいいのですが、そのようにすると、ArticleモデルのデータがひとつのUserモデルのデータとしか属せず、UserモデルのデータもひとつのArticleモデルデータとしか属することができません。そのため、throughを使って、中間テーブルを介してそれぞれのモデルにアクセスできるようにします。ここでは中間テーブルはArticleUserというモデルとしてアクセスするので、throguhには、:article_usersを指定します。これで、多対多の関連を表現できました。使い方has_manyを使うことで、そのモデルのデータに別な複数のモデルのデータが属することを表現できます。いくつかの便利なメソッドが追加され、操作が楽にできるようになります。Articleモデルのデータに複数のUserモデルのデータを属するようにするには、次のように代入するだけです。usersこのように、Articleモデルのインスタンスにメソッドが追加されるので、それを使って楽にUserモデルのデータを関連づけることができます。まとめ多対多の関連を表現するときには、中間テーブルを使いましょう。モデルのhas_manyにthroughオプションを追加して、中間テーブルを介して目標のモデルにアクセスできるようにしましょう。