Header
2018-01-14
2020-06-19

【初心者・独学者向け】Ruby on Railsで中間テーブルを作成し、多対多を実現する

140986 osx7pe 443 iloveimg compressed

Ruby on Railsの中間テーブルを利用して多対多の関係を実現する方法を記載します。

多対多で何ができるか?

1記事に関連するカテゴリーを複数持たせたり、 ツイッターのツイートに対して、複数のタグを持たせたり などなどです。

多対多の関係とは?

記事から見るとカテゴリーを複数持っているので、記事に対して、カテゴリーは多になります。

カテゴリーから見ると1カテゴリーに複数の記事が関連しているので、カテゴリーから見ると記事は多になります。 この関係が多対多になります。

ここまでで、あまり理解出来ない方は、PC画面で この記事の上部へスクロールして、パンくずリストをご覧ください。

以下のようなパンくずリストがあり、こちらにマウスを当てると記事カテゴリーへのリンクとなっていますが

パンくずリスト

現在ご覧になっているこの記事に対して、Ruby on Railsや初心者向けというような複数のカテゴリーが設定されています。

また、パンくずリストのRuby on Railsのカテゴリーを選択すると複数の記事が表示されます

つまりRuby on Railsのカテゴリーに複数の記事が設定されています。

これが多対多の関係になります。

なおデータベース自体の概要が分からない方は以下から 【初心者・独学者向け】データベースとは何かを解説します

中間テーブルとは?

多対多の関係は2つのモデルでは実現出来ないため、 中間テーブルを利用し多対多の関係を実現します。

以下に3つのサンプルテーブルを記載しています。

articleテーブル(記事を投稿するテーブル)

article_id タイトル
2 RubyonRailsに関する記事
3 Javaに関する記事

categoryテーブル(記事カテゴリーのテーブル)

category_id タイトル
1 プログラミング全般
2 RubyonRails
3 Java

category_articleのテーブル(中間テーブル)

category_id article_id
1 2
2 2
1 3
3 3

上記の用にcategoryarticleテーブルでcategoryidとarticle_idをレコードで紐付け、多対多の関係を実現します。

上記の例題では、「RubyonRailsに関する記事」を表示すると その記事のパンくずリストに表示されるカテゴリーは 「プログラミング全般」と「RubyonRails」 が表示されます。

なぜかというと categoryarticleのテーブル(中間テーブル)でarticleid=2とcategoryid=1とcategoryid=2の紐づけを行っているからです。

Modelのアソシエーションの関連性設定

マイグレーションファイルは以下の様に記載して、マイグレーションを実行します。

class CreateCategoriesArticles < ActiveRecord::Migration
  def change
    create_table :categories_articles do |t|
      t.integer  :category_id
      t.integer  :article_id
      t.timestamps null: false
    end
  end
end

その後は中間テーブルのModel(CategoriesArticleModel)を設定します。

CategoriesArticleModelでArticleのModelとCategoryのModelを関連付けます。

class CategoriesArticle < ActiveRecord::Base
  belongs_to :category, optional: true
  belongs_to :article, optional: true
end

Rails5からデフォルトで外部キーのnilが許可されなくなったので、 nilを許可する場合は以下のようにbelongs_to へoptional: trueを設定する必要があります。

サンプルコードなので記載していませんが、外部キー設定をお願いします。

次はArticleModelの設定です。

class Article < ActiveRecord::Base

  has_many :categories_articles
  has_many :categories, through: :categories_articles

end

中間テーブルを通して繋がっているものには through: : categories_articlesというkeyをつけます。

throughオプションによりarticles経由でcategoryにアクセスできるようになります。

上記設定で、articles.categories_articlesで記事に関連するカテゴリーを取得できます。

次はCategoryModelの設定です。

class Category < ActiveRecord::Base
  has_many :categories_articles
  has_many :articles, through: :categories_articles
end

Articleと同様にthrough: :categories_articlesというkeyをつけます。

これもcategory.categories_articlesで、カテゴリーに属する記事へアクセス可能になります。

Modelの設定は以上になります。

viewでの呼び出し方

記事に紐づくカテゴリーを呼び出すには、 Article.modelに設定したキーarticles_categoriesを利用し 呼び出します。

ArticlesController 

  def show
    @article = Article.find(params[:id])
  end

show. slim

- @article.categories_articles.each do |categories_article|
  = categories_article.category.name

1記事に対して複数のカテゴリーを設定しているので、 viewで値を表示する際は繰り返し処理で呼び出しを行わないとエラーになります

こちらはslimで記載していますが、erbでも対応可能です。 slimの詳細は【初心者・独学者向け】Ruby on Railsのviewを劇的に変える?!Slim入門をご覧ください

※値を取得できなかった場合の処理も記載する必要がありますが、こちらは割愛しております。

新規記事投稿時にカテゴリーを紐付ける

newアクションで@article.categories_articles.buildとすると articleに紐づく中間テーブルのインスタンスが精製される

その後create アクションのストロングパラメーターで category_idsを受け取って登録処理が行われます。

ArticlesController 

  def new
    @article = Article.new
    @article.categories_articles.build
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to dashboards_path
    else
      render 'new'
    end
  end

  def article_params
    params.require(:article).permit(:title, :content, { :category_ids=> [] })
  end

下記フォームでは1記事に対して複数のカテゴリーを設定できるようにしています。

その為、collectioncheckboxesで複数選択できるようにしています。

また引数の項目はcategory_idsと複数形にし、controllerのストロングパラメーターで受け取ります。

new.html

= simple_form_for @article do |f|
  = f.input :title
  = f.collection_check_boxes :category_ids, Category.all, :id, :name

以上になります

その他の関連記事

Ruby on Rails でデータを取得するメソッド一覧

Ruby on Railsのscopeメソッドで検索を効率化する

27歳から未経験で自社サービス会社のエンジニアに転職された方の勉強法や通学したプログラミングスクール

27歳から未経験で自社サービス会社のエンジニアに転職された方の転職媒体やエンジニア情報の収集先など

Sequel PROでAWSのDBに接続する

【初心者・独学者向け】Ruby on RailsでテーブルやModelを削除する際のコマンド

Ruby on Rails5で簡易ブログを作成する1 | VIew Controller ルートを作成する

Ruby on RailsでRedisを利用してランキング機能を実装する

Ruby on Railsとtwitterの投稿機能を連携する

職歴を作るためにSESに就職して、Web系エンジニアへ転職は可能か

Rubyでサイトをスクレイピングし、ローカルに画像を保存する

MySQLでカラム名の変更と上書きする

MySQLでテーブルへカラムを追加するALTER TABLE ~ ADD

MySQLでNULLや空文字を検索する

【初心者・独学者向け】RubyでMySQLを操作する

あなたにお勧めの記事
前の記事
次の記事