Reputation: 377
I working with ruby on rails and my products views is cached and I can find products with search form. I want to use pagination as well, but when added, it didn't worked and also search form. Found code line ,but not sure how to use it, code below.
<% cache ["v1-#{params[:page]-#{params[:q]", cache_key_for_products]
> do %>
My code
Index.html.erb
<div class="products">
<div class="container">
<center><h1>All products</h1></center>
<div class="row">
<% cache(cache_key_for_products) do %>
<%= render @products %>
<% end %>
</div>
<%= will_paginate @comments, :container => false %>
</div>
</div>
_product.html.erb
<% cache product do %>
<div class="col-md-3 col-lg-3 col-sm-6 col-xs-12">
<div class="product-block">
<h4 id="product-name"><%= product.name %></h4>
<%= link_to product_path(product), class: 'product_link' do %>
<%= image_tag(product.image_url, class: "img-responsive") %>
<% end %>
<div class="price">
<h4>£ <%= product.price %></h4>
</div>
</div>
</div>
<% end %>
products_helper.rb
module ProductsHelper
def cache_key_for_products
count = Product.count
max_updated_at = Product.maximum(:updated_at).try(:utc).try(:to_s, :number)
"products/#{params[:q] || "all"}-#{count}-#{max_updated_at}#{signed_in? && current_user.admin? ? "-admin" : "normal"}"
end
end
products_controller.rb
def index
if params[:q].present?
search_term = params[:q]
if Rails.env.development?
@products = Product.where("name LIKE ?", "%#{search_term}%")
else
@products = Product.where("name ilike ?", "%#{search_term}%")
end
else
@products = Product.all.paginate(:page => params[:page], :per_page => 30)
end
end
Upvotes: 0
Views: 777
Reputation: 5617
In your controller, looks like you are paginating correctly when not performing a search, but need to add the pagination to your search query also:
def index
if params[:q].present?
search_term = params[:q]
if Rails.env.development?
@products = Product.where("name LIKE ?", "%#{search_term}%").paginate(:page => params[:page], :per_page => 30)
else
@products = Product.where("name ilike ?", "%#{search_term}%").paginate(:page => params[:page], :per_page => 30)
end
else
@products = Product.all.paginate(:page => params[:page], :per_page => 30)
end
end
Also you are caching each search result set, this means that the same products could be potentially cached multiple times in many different searches. This will quickly bloat your cache. It would be better to cache each product once and fetch these products from cache regardless of the search.
I see you are caching each product (in _product.html.erb
partial). In index.html.erb
change the code to this:
<div class="products">
<div class="container">
<center><h1>All products</h1></center>
<div class="row">
<%= render @products, cache: true %>
</div>
<%= will_paginate @comments, :container => false %>
</div>
</div>
This will take advantage of multi fetch fragment caching which Rails 5 has built in:
1.3.1 Collection caching
The render helper can also cache individual templates rendered for a collection. It can even one up the previous example with each by reading all cache templates at once instead of one by one. This is done by passing cached: true when rendering the collection:
<%= render partial: 'products/product', collection: @products, cached: true %> All cached templates from previous renders will be fetched at once with much greater speed. Additionally, the templates that haven't yet been cached will be written to cache and multi fetched on the next render.
Otherwise if you are < Rails 5, use the Multi Fetch Fragments gem to enable this functionality.
In index.html.erb
you could modify the collection cache rendering to something this to use a custom cache key:
<%= render @products, cache: Proc.new{|item| [item, 'show']} %>
Upvotes: 1