概要
以下の画像のように、選択したカテゴリーに所属する商品を一覧表示する機能です。
カテゴリーは多階層になっていて、下の階層に行くほど絞り込んだ検索が出来るようになります。
カテゴリーはgemのancestryを用いて作成しています。
前提
・ancestryを用いてカテゴリーテーブルを作成している。
・カテゴリー:商品 = 1:多の関係になっている。
・商品モデルのcategory_idには、最下層のカテゴリーidが登録されている。
この記事のアプリでは3階層のカテゴリーを使用しています。
また、この記事内では一番上のカテゴリーを「親カテゴリー」、その下の階層を「子カテゴリー」、
そのまた下を「孫カテゴリー」と表現しています。
参考記事
ancestryの導入方法については以下の記事が参考になります。
https://qiita.com/Sotq_17/items/120256209993fb05ebac
https://qiita.com/pdm21/items/fe0055b3190af790f1c0
#実装
##カテゴリー一覧ページ
はじめに、全てのカテゴリーを表示する一覧ページを作成していきます。
categoriesコントローラーのindexアクションを使用します。
###indexアクション定義
def index
@parents = Category.where(ancestry: nil)
end
親カテゴリーのancestryカラムはnilのため、上の記述で親カテゴリーを取得出来ます。
###ビュー作成
%h1 カテゴリー一覧
%ul.categories
- @parents.each do |parent|
%li.parents= link_to "#{parent.name}", category_path(parent)
%ul.categories__children
- parent.children.each do |child|
%li.childern= link_to "#{child.name}", category_path(child)
%ul.categories__grandchildren
- child.children.each do |grandchild|
%li= link_to "#{grandchild.name}", category_path(grandchild)
これで全てのカテゴリーを一覧表示出来ます。
それぞれのカテゴリー名は、後述するカテゴリー別商品一覧ページにリンクしています。
##カテゴリー別 商品一覧ページ
続いて、選択したカテゴリーに所属する商品を一覧表示するページを作成していきます。
categoriesコントローラーのshowアクションを使用します。
###showアクション定義
before_action :set_category, only: :show
def show
@items = @category.set_items
@items = @items.where(buyer_id: nil).order("created_at DESC").page(params[:page]).per(9)
end
private
def set_category
@category = Category.find(params[:id])
end
後述するモデルメソッド set_itemsにより、カテゴリー内の商品を取得します。
###モデルメソッド定義
has_many :items
has_ancestry
def set_items
# 親カテゴリーの場合
if self.root?
start_id = self.indirects.first.id
end_id = self.indirects.last.id
items = Item.where(category_id: start_id..end_id)
return items
# 子カテゴリーの場合
elsif self.has_children?
start_id = self.children.first.id
end_id = self.children.last.id
items = Item.where(category_id: start_id..end_id)
return items
# 孫カテゴリーの場合
else
return self.items
end
end
商品のcategory_idには孫カテゴリーのidが登録されています。
そのため、単純に @items = @category.items と記述するだけでは、@categoryが孫の場合しか商品情報を取得出来ません。
よって、上記のようにカテゴリーが親 or 子 or 孫の内のどれに当たるかで条件分岐します。
カテゴリーが親 or 子の場合は、自身が持つ孫カテゴリーのid範囲を指定して商品を取得しています。
root?やindirectsなど、ancestry独自のメソッドを使用する際は以下の記事が参考になります。
https://qiita.com/Rubyist_SOTA/items/49383aa7f60c42141871
###ビュー作成
.products-container
.products-index
.title
= "#{@category.name}の商品一覧"
.title__border
- if @items
%ul.lists
= render "items/item", items: @items
= paginate @items
全ての商品一覧ページで使用している部分テンプレートを共用。
@itemsの値に応じて表示内容を変化させます。
###他カテゴリーへのリンク作成
最後に、以下のように他カテゴリーへのリンクを追加し、より商品を探しやすくします。
表示カテゴリーが親 or 子の場合、一階層下のカテゴリーへのリンクを、
孫の場合は、同じ子カテゴリーに所属する孫カテゴリーへのリンクを表示するようにします。
def set_category
@category = Category.find(params[:id])
# 追記---------------------------------
if @category.has_children?
@category_links = @category.children
else
@category_links = @category.siblings
end
# -------------------------------------
end
// 追記--------------------------------------------------
.category_wrapper
.category_links
- @category_links.each do |category|
= link_to category.name, category_path(category)
// -----------------------------------------------------
.products-container
.products-index
.title
= "#{@category.name}の商品一覧"
.title__border
- if @items
%ul.lists
= render "items/item", items: @items
= paginate @items
あとは適宜CSSを整えれば完成になります。
#最後に
以上で多階層カテゴリーから商品を検索する機能は完成です。
ここまで読んでいただき、ありがとうございました。