カテゴリー機能を作る(Rails6)
ブログ機能にカテゴリーをつけて記事を絞れるようにします
中間テーブルの作成
articleとcategoryは
article : 複数のcategoryを持つ
category : 複数のarticleに属する
ので多対多の関係にあります。
この時articleテーブルでcategoryを管理すると、categoryを増やすたびにカラムも増やさないといけなくなるため冗長なものとなります
そのため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タグのテキストに相当
"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]の有無で分岐させることで該当カテゴリーの記事のみを一覧画面に表示することできます
カテゴリーで記事を絞り込めるようになりました
参考
qiita.com