14
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Rails]多階層カテゴリーから商品を検索・一覧表示する機能

Last updated at Posted at 2020-06-09

概要

以下の画像のように、選択したカテゴリーに所属する商品を一覧表示する機能です。
カテゴリーは多階層になっていて、下の階層に行くほど絞り込んだ検索が出来るようになります。
カテゴリーはgemのancestryを用いて作成しています。
d3a7f46ab307f384f41fe88f3917826b.gif

前提

・ancestryを用いてカテゴリーテーブルを作成している。
・カテゴリー:商品 = 1:多の関係になっている。
・商品モデルのcategory_idには、最下層のカテゴリーidが登録されている。

この記事のアプリでは3階層のカテゴリーを使用しています。
また、この記事内では一番上のカテゴリーを「親カテゴリー」、その下の階層を「子カテゴリー」、
そのまた下を「孫カテゴリー」と表現しています。

参考記事

ancestryの導入方法については以下の記事が参考になります。
https://qiita.com/Sotq_17/items/120256209993fb05ebac
https://qiita.com/pdm21/items/fe0055b3190af790f1c0

#実装
##カテゴリー一覧ページ
はじめに、全てのカテゴリーを表示する一覧ページを作成していきます。
categoriesコントローラーのindexアクションを使用します。
bda976886433b175ea19e2cecab7afa1.png

###indexアクション定義

app/controllers/categories_controller.rb
def index
  @parents = Category.where(ancestry: nil)
end

親カテゴリーのancestryカラムはnilのため、上の記述で親カテゴリーを取得出来ます。

###ビュー作成

app/views/categories/index.html.haml
%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アクション定義

app/controllers/categories_controller.rb
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により、カテゴリー内の商品を取得します。

###モデルメソッド定義

app/models/category.rb
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

###ビュー作成

app/views/categories/show.html.haml
.products-container
  .products-index
    .title
      = "#{@category.name}の商品一覧"
      .title__border
    - if @items
      %ul.lists
        = render "items/item", items: @items
    = paginate @items

全ての商品一覧ページで使用している部分テンプレートを共用。
@itemsの値に応じて表示内容を変化させます。

###他カテゴリーへのリンク作成
最後に、以下のように他カテゴリーへのリンクを追加し、より商品を探しやすくします。
4f5519ec96f8863baa48a2f49dd17281.png
表示カテゴリーが親 or 子の場合、一階層下のカテゴリーへのリンクを、
孫の場合は、同じ子カテゴリーに所属する孫カテゴリーへのリンクを表示するようにします。

app/controllers/categories_controller.rb
def set_category
  @category = Category.find(params[:id])
  # 追記---------------------------------
  if @category.has_children?
    @category_links = @category.children
  else
    @category_links = @category.siblings
  end
  # -------------------------------------
end
app/views/categories/show.html.haml

// 追記--------------------------------------------------
.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を整えれば完成になります。

#最後に
以上で多階層カテゴリーから商品を検索する機能は完成です。
ここまで読んでいただき、ありがとうございました。

14
33
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?