Please, I need some help for a digital library project that I working on. I want to add some search filtering functionality to the application.
The application primarily has books that belong to sub-categories, and sub-categories that belong to categories.
I want to create a search filtering function where you can filter books based on their categories when you search for a particular book.
So first, you create categories, and then next you create subcategories and select the categories that these subcategories fall under, and then you create books and only select the subcategories that each book falls under, which automatically places that book in the category that that subcategory falls under.
For now, the search filters only work if I add categories to the books new form. That is if I modify the books new form, and then include category selection to the form. But I just want to include only subcategories to the books new form, since each subcategory belongs to a category already.
The codes are as follows
Book Model
class Book < ApplicationRecord
belongs_to :category, required: false
belongs_to :subcategory, required: false
def self.search(keywords)
if keywords
where('name LIKE ? OR description LIKE ? OR author LIKE ? OR abstract LIKE ?', "%#{keywords}%", "%#{keywords}%", "%#{keywords}%", "%#{keywords}%").order('id DESC')
else
order('id DESC')
end
end
end
Sub-Category Model
class Subcategory < ApplicationRecord
belongs_to :category
has_many :books
end
Category Model
class Category < ApplicationRecord
has_many :subcategories
has_many :books
end
Books Controller (Truncated)
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
def index
if params[:category].blank? or params[:category][:id].blank?
@books_view = Book.all
else
@books_view = Category.find(params[:category][:id]).books
end
@books = @books_view.search(params[:keywords]).paginate(page: params[:page], per_page: 12)
end
end
Books Index Page Seach Form
<%= form_tag(books_path, method: :get) do %>
<%= text_field_tag :keywords, params[:keywords], {placeholder: 'eg: Nursing', :class => 'searchForm'} %>
<%= collection_select :category, :id, Category.all.order('name ASC'), :id, :name,{include_blank: 'Select Category'}, { :class => 'form-control'} %>
<button type="submit">
Search
<%= image_tag("searchIcon.svg", :alt => "search", :class => "") %>
</button>
<% end %>
Please I need some assistance. It will be highly appreciated. Thank you.
Update
I have tried the solution from Dileep Nandanam, by updating by Books Controller this way
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
def index
if params[:category].blank? or params[:category][:id].blank?
@books_view = Book.all
else
@books_view = Book.joins(:subcatagory).joins('catagories on catagories.id = subcatagories.catagory_id').where(catagories: {id: params[:catagory][:id]})
end
@books = @books_view.search(params[:keywords]).paginate(page: params[:page], per_page: 12)
end
end
But it really didn't work as expected. I ran into some issues. Below is a screenshot of the issue.
![NoMethodError in BooksController#index undefined method `[]' for nil:NilClass]1
I believe your problems come from this particular thing:
The answer to your particular question is:
But let's elaborate on the answer:
Look, there is a big problem in your abstraction. If a Book belongs both to Subcategory (with a
subcategory_id) and to Category (with acategory_id), you really don't have a coherent data structure. If you use a relational database, you are violating db normalisation. What really should happen here is to split Category from Book and reference it through Subcategory, like this: