[RUBY] [Rails] Function to search and list products from multi-level categories

Overview

As shown in the image below, it is a function to display a list of products belonging to the selected category. The categories are multi-level, and the lower the level, the more narrowed the search becomes. Categories are created using gem's ancestry. d3a7f46ab307f384f41fe88f3917826b.gif

Premise

-A category table is created using ancestry. ・ Category: Product = 1: There are many relationships. -The category id of the lowest layer is registered in the category_id of the product model.

The app in this article uses three levels of categories. Also, in this article, the top category is the "parent category" and the hierarchy below it is the "child category". Below that is expressed as the "grandchild category".

Reference article

The following articles will be helpful for how to install ancestry. https://qiita.com/Sotq_17/items/120256209993fb05ebac https://qiita.com/pdm21/items/fe0055b3190af790f1c0

Implementation

Category list page

First, we will create a list page that displays all categories. Use the index action of the categories controller. bda976886433b175ea19e2cecab7afa1.png

index action definition

app/controllers/categories_controller.rb


def index
  @parents = Category.where(ancestry: nil)
end

Since the ancestry column of the parent category is nil, you can get the parent category with the above description.

View creation

app/views/categories/index.html.haml


%h1 category list
%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)

Now you can list all the categories. Each category name is linked to the product list page by category described later.

Product list page by category

Next, we will create a page that lists the products that belong to the selected category. Use the show action of the categories controller.

show action definition

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

Use the model method set_items described later to get the products in the category.

Model method definition

app/models/category.rb


has_many :items
has_ancestry

def set_items
  #For parent category
  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

    #For child categories
  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

    #For grandchild categories
  else
    return self.items
  end
end

The id of the grandchild category is registered in the category_id of the product. Therefore, if you simply write @items = @ category.items, you can get the product information only when @category is a grandchild. Therefore, as described above, conditional branching is performed depending on which of the parent, child, or grandchild the category corresponds to. If the category is parent or child, the product is acquired by specifying the id range of the grandchild category that it owns.

The following articles will be helpful when using ancestry's own methods such as root? And indirects. https://qiita.com/Rubyist_SOTA/items/49383aa7f60c42141871

View creation

app/views/categories/show.html.haml


.products-container
  .products-index
    .title
      = "#{@category.name}Product list"
      .title__border
    - if @items
      %ul.lists
        = render "items/item", items: @items
    = paginate @items

The partial template used on all product list pages is shared. The displayed contents are changed according to the value of @items.

Create links to other categories

Finally, add links to other categories as shown below to make it easier to find products. 4f5519ec96f8863baa48a2f49dd17281.png If the display category is parent or child, link to the category one level below, For grandchildren, display links to grandchildren categories that belong to the same child category.

app/controllers/categories_controller.rb


def set_category
  @category = Category.find(params[:id])
  #Postscript---------------------------------
  if @category.has_children?
    @category_links = @category.children
  else
    @category_links = @category.siblings
  end
  # -------------------------------------
end

app/views/categories/show.html.haml



//Postscript--------------------------------------------------
.category_wrapper
  .category_links
    - @category_links.each do |category|
      = link_to category.name, category_path(category)
// -----------------------------------------------------
.products-container
  .products-index
    .title
      = "#{@category.name}Product list"
      .title__border
    - if @items
      %ul.lists
        = render "items/item", items: @items
    = paginate @items

After that, if you arrange the CSS appropriately, it will be completed.

Finally

This completes the function to search for products from multi-level categories. Thank you for reading this far.

Recommended Posts

[Rails] Function to search and list products from multi-level categories
[Rails] Implement search function
Rails search function implementation
Rails learning How to implement search function using ActiveModel
Try to implement tagging function using rails and js
[Ruby On Rails] How to search and save the data of the parent table from the child table
Rails fuzzy search function implementation
[Rails] Implement User search function
Search function using [rails] ransack
[Rails 6] Implementation of search function
[Rails] Search from multiple columns + conditions with Gem and ransack
[Rails] I tried to implement "Like function" using rails and js
Add a search function in Rails.
[Rails] About the Punk List function
[Java] Conversion from array to List
Rails API mode I tried to implement the keyword multiple search function using arrays and iterative processing.
How to implement search function with rails (multiple columns are also supported)
Strict_loading function to suppress the occurrence of N + 1 problem added from rails 6.1
Rails Tutorial record and memorandum # 1 "From installation to hello_app deployment + error handling"
From pulling docker-image of rails to launching
[Ruby on Rails] Search function (not selected)
How to implement search functionality in Rails
Install Webpacker and Yarn to run Rails
[Rails] How to convert from erb to haml
Rails Addition of easy and easy login function
[Rails] Assigning variables from controller to JavaScript
[Rails] [Memo] When to add = to <%%> and when not
Ability to display a list of products
Soaring tech skills and declining tech skills from 2014 to 2019
Use Stream # collect to retrieve and list only specific fields from a JavaBean list
[Rails] Set validation for the search function using Rakuten API (from the implementation of Rakuten API)