個人的勉強メモ置き場

プログラミングど素人のメモ置き場

カテゴリー機能を作る(Rails6)

ブログ機能にカテゴリーをつけて記事を絞れるようにします

f:id:zykb:20211224025936p:plain

中間テーブルの作成

articleとcategoryは

article : 複数のcategoryを持つ
category : 複数のarticleに属する

ので多対多の関係にあります。

この時articleテーブルでcategoryを管理すると、categoryを増やすたびにカラムも増やさないといけなくなるため冗長なものとなります
f:id:zykb:20211223222731p:plain

そのためarticleと紐づくcategoryのidを管理する中間テーブル(article_category)を作成します

rails g model Article_Category article:references category:references

:referencesをつけることでそれぞれのモデルと紐づけることができ、自動的に外部キーが付与されます。

class CreateArticleCategories < ActiveRecord::Migration[6.1]
  def change
    create_table :article_categories do |t|
      t.references :article, null: false, foreign_key: true
      t.references :category, null: false, foreign_key: true
      t.timestamps
    end
  end
end

migrateします

rails db:migrate
アソシエーションの記述

article_category.rb

class ArticleCategory < ApplicationRecord
  belongs_to :article
  belongs_to :category
end

中間テーブルは1つのarticleと1つのcategoryに属しているのでbelongs_toを記述します

article.rb

class Article < ApplicationRecord
  has_many :article_categories
  has_many :categories, through: :article_categories
end

category.rb

class Category < ApplicationRecord
  has_many :article_categories
  has_many :articles, through: :article_categories
end

throughオプションをつけることで中間テーブル経由でモデルにアクセスできるようになります

viewの設定

今回はとりあえずカテゴリーは初期データとしてお知らせとブログの二つを投入しておきました

記事の編集や新規作成からカテゴリーを選択できるようにします

<%= form_with model: @article, local: true do |f| %>
   ...
    <div class="form-group">
      <%= f.label :category %>
      <%= f.collection_check_boxes(:category_ids, Category.all, :id, :name) do |category| %> 
        <%= category.label do %>
          <%= category.check_box %>
          <%= category.text %>
        <% end %>
      <% end %>
    </div>
    ...
<% end %>

抜粋

f.collection_check_boxes(:category_ids, Category.all, :id, :name) do |category|

第一引数(:category_ids)
メソッド名。paramsで取り出せる
第二引数(Category.all)
コレクション。ここではカテゴリーのすべてを指定したいのでCategory.all
第三引数(:id)
inputタグのvalueに相当
第四引数(:name)
inputタグのテキストに相当

f:id:zykb:20211224021958p:plain
こんなチェックボックスになります
"article"=>{"title"=>"テスト記事", "content"=>"<div>これはお知らせです</div>", "category_ids"=>["", "1"]}, "commit"=>"記事の作成", "id"=>"3"}

送信するとこのように第一引数で指定したcategory_idsというキーで渡されます

controllerの設定

StrongParameterでcategory_idsを許可します

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


記事一覧ページではデフォルトではすべての記事を、サイドバーのカテゴリーリンクを押したときにはカテゴリーで記事を絞り込めるように以下のようにしました

def index
    @categories = Category.all
    if params[:category_id]
      @category = Category.find(params[:category_id])
      @articles = @category.articles.recent.page(params[:page])
    else
      @articles = Article.recent.page(params[:page])
    end
end

サイドバー部分

<h3 class="side-title">カテゴリー</h3>
  <ul>
    <% @categories.each do |category| %>
      <li>
        <%= link_to category.name, articles_path(category_id: category.id) %>
      </li>
    <% end %>
  </ul>

サイドバーのカテゴリーをクリックするとparams[:category_id]で該当カテゴリーを取得できます
params[:category_id]の有無で分岐させることで該当カテゴリーの記事のみを一覧画面に表示することできます

f:id:zykb:20211223220727g:plain

カテゴリーで記事を絞り込めるようになりました


参考
qiita.com