-
Notifications
You must be signed in to change notification settings - Fork 15
filtering
Vadim edited this page Apr 22, 2017
·
3 revisions
Steps for adding filters:
-
Redefine views/rademade_admin/_blocks/_list.html.erb to add:
<div> <% if @search_fields %> <div><%= render 'rademade_admin/_blocks/filters' %></div> <div class="clear"></div> <% end %> </div>
-
Create views/rademade_admin/_blocks/_filters.html.erb:
<div class="content-box custom-filter">
<div class="clear"></div>
<div class="custom-form">
<form action="<%= admin_list_uri(@model_info.model) %>" method="get" data-turboform="true"><%
%><label class="form-box">
<span class="form-label">По названию</span>
<span class="input-holder"><%=
text_field_tag :q, params[:q], class: "form-input", id: "item-name-filter", placeholder: "Поиск по названию"
%></span></label><%
%><%
(@search_fields[:text] || []).each do |field|
%><label class="form-box"><span class="form-label"><%= field[:label] %></span>
<span class="input-holder"><%=
text_field_tag field[:name], params[field[:name]], class: 'form-input', id: "item-name-filter", placeholder: field[:label]
%></span></label><%
end
%><%
(@search_fields[:multiple] || []).each do |field|
%><%=
render layout: '/rademade_admin/_blocks/select_input', locals: { name: field[:name], is_multiple: true, label_title: field[:label] } do
options_from_collection_for_select(
field[:model].all, "id", "name", @search_params[field[:name]].try(:map, &:to_i)
)
end
%><%
end
(@search_fields[:singular] || []).each do |field|
%><div class="clear"></div>
<label class="form-box select optional data_status form-group">
<span class="select optional form-label"><%= field[:label] %></span>
<div class="input-holder"><%
options = case field[:model]
when Hash
options_for_select(field[:model].stringify_keys, @search_params[field[:name]])
when Array
options_for_select(field[:model], @search_params[field[:name]])
else
options_from_collection_for_select(field[:model].all, "id", "name", @search_params[field[:name]])
end
%><%=
select_tag field[:name], options, include_blank: 'Выберите для поиска', class: "select optional form-input"
%></div>
</label><%
end
%><div class="btn-list">
<div class="btn-box align-left">
<button class="btn is-blue" id='items-search-submit' type="submit">Search</button><%=
link_to 'Reset', admin_list_uri(@model_info.model), class: "btn is-red"
%></div>
</div>
</form>
</div>
-
Add this to abstract admin controller:
include Health24Admin::Search::Searchable
-
And define that module and modify the code is needed(f.e. if you dont need translation filters - remove them)
# frozen_string_literal: true
module Health24Admin
class SearchParams
attr_reader :params, :order_conditions, :available_filters
def initialize(params, filters)
@filters_singular = init_available_filters(filters, :singular)
@filters_multiple = init_available_filters(filters, :multiple)
@filters_text = init_available_filters(filters, :text)
@available_filters = (@filters_singular + @filters_multiple + @filters_text).push :q
@order_conditions = params.permit(:sort, :direction)
@params = sanitize(params.symbolize_keys)
end
def singular?(param_name)
@filters_singular.include? param_name
end
def multiple?(param_name)
@filters_multiple.include? param_name
end
def text?(param_name)
@filters_text.include? param_name
end
def ready_for_search?
@params.count.positive? || @order_conditions[:sort].present?
end
private
def init_available_filters(filters, type)
return [] unless filters[type]
filters[type].map { |filter| filter[:name] }.flatten
end
def sanitize(init_params)
available_param_list = init_params.keys & available_filters
@sanitized_param_list = available_param_list.select do |param|
init_params[param] && !init_params[param].empty? && !empty_array?(init_params[param])
end
init_params.select { |key| @sanitized_param_list.include?(key) }
end
def empty_array?(array)
return false if array.is_a? String
array.all?(&:empty?)
end
end
end
# frozen_string_literal: true
class Health24Admin::ItemSearch
def initialize(model, params_source)
@model = model
@params_source = params_source
end
def filter
items = query_filter if @params_source.params.key?(:q)
return items if items && !items.exists?
collection = filter_items(items || @model)
if @params_source.order_conditions[:sort]
order(collection)
else
collection
end
end
private
def filter_items(collection)
collect_filters.each do |filter|
raise(Exception::NoFilterError.new(filter[:scope], @model.to_s)) unless @model.respond_to?(filter[:scope])
collection = collection.public_send(filter[:scope].to_sym, filter[:value])
end
collection
end
def order(collection)
@params_source.order_conditions[:direction] ||= :asc
# check if model is globalized
translated_attributes = collection.try(:translated_attribute_names)
# if globalized and current attribute is globalized we search by it
if translated_attributes&.include?(@params_source.order_conditions[:sort].to_sym)
where_string = <<-SQLL
"#{collection.translation_class.table_name}"."#{@params_source.order_conditions[:sort]}" IS NOT NULL AND
TRIM(LOWER("#{collection.translation_class.table_name}"."#{@params_source.order_conditions[:sort]}")) != ""
SQLL
order_string = <<-SQLL
TRIM(LOWER("#{collection.translation_class.table_name}"."#{@params_source.order_conditions[:sort]}")) #{@params_source.order_conditions[:direction]}
SQLL
collection.joins(:translations).where(where_string.gsub(/\s+/, ' ')).reorder(order_string.gsub(/\s+/, ' '))
else
collection.reorder(@params_source.order_conditions[:sort] => @params_source.order_conditions[:direction])
end
end
def collect_filters
@params_source.params.map do |key, value|
{ scope: filter_name(key), value: filter_value(value) }
end
end
def filter_name(param_name)
return param_name.to_s.singularize.foreign_key if @params_source.multiple?(param_name)
param_name.to_s.singularize if @params_source.singular?(param_name) || @params_source.text?(param_name)
end
def filter_value(value)
return value if value.is_a?(Array) || value.to_i.to_s == value
try_to_bool(value)
end
def try_to_bool(value)
return true if value == 'true'
return false if value == 'false'
value
end
def query_filter
Health24Admin::Search::SearchWithTranslation.new(
@model,
@params_source.params.delete(:q),
paginate: nil
).search_in(translated_locales)
end
def translated_locales
@model.try(:translated_locales)
end
end
module Health24Admin::Search::Searchable
DEFAULT_PAGE = 1
DEFAULT_PAGE_ITEMS = 20
def self.included(base)
base.before_action(:init_search_params, only: :index)
base.before_action(:set_search_fields, only: %i(autocomplete index))
end
def autocomplete_items
return super unless @search_fields && @search_fields[:q]
model.public_send(@search_fields[:q], params[:q])
end
def index_items(_ = true)
init_search_fields
result = found_items || if @translation_field
super.includes(:translations)
else
super
end
clear_empty_params
paginate_items(result)
end
private
# name is predefined as translated field
# redefine in controller to get search fields in admin
# example:
# def set_search_fields
# @search_fields = {
# singular: [
# { name: :characteristic_type, label: 'Фильтр по типу характеристики', model: CharacteristicType::MANIFESTS.invert },
# { name: :is_common, label: 'Фильтр по общая для человека', model: Type::BOOLEAN }
# ],
# multiple: [
# { name: :organs, label: 'Фильтр по органу', model: ::OrganInfo::Organ },
# { name: :body_parts, label: 'Фильтр по частям тела', model: ::BodyInfo::BodyPart },
# { name: :organ_systems, label: 'Фильтр по системам органов', model: ::OrganInfo::OrganSystem }
# ],
# text: [
# { name: :by_code, label: 'Фильтр по коду' }
# ]
# }
# end
# name = existing model scope name
# label = search input label
# model = model to apply scope to
def set_search_fields
@search_fields = nil
end
def init_search_params
@search_params ||= {}
end
WITHOUT_TRANSLATION_FIELD = {
name: :without_translations,
label: 'Без перевода на языке',
model: Type::LOCALES
}.freeze
def init_search_fields
return unless model_info.model.try(:translated_locales)
@search_fields ||= {}
@search_fields[:singular] ||= []
@search_fields[:singular] << WITHOUT_TRANSLATION_FIELD
end
def clear_empty_params
params.reject! { |_, v| v.blank? }
end
def search(params_source)
@search_params = params_source.params
Health24Admin::ItemSearch.new(model_info.model, params_source).filter
end
def paginate_items(items)
items.page(params[:page] || DEFAULT_PAGE).per(params[:paginate] || DEFAULT_PAGE_ITEMS)
end
def found_items
return unless @search_fields
search_params = Health24Admin::SearchParams.new(params, @search_fields)
return unless search_params.ready_for_search?
search(search_params)
end
end
- Define your own config method in controller on which route you want to perform filtering, example:
def set_search_fields
@search_fields = {
singular: [
{ name: :characteristic_type, label: 'Фильтр по типу характеристики', model: CharacteristicType::MANIFESTS.invert },
{ name: :is_common, label: 'Фильтр по общая для человека', model: Type::BOOLEAN }
],
multiple: [
{ name: :organs, label: 'Фильтр по органу', model: ::OrganInfo::Organ },
{ name: :body_parts, label: 'Фильтр по частям тела', model: ::BodyInfo::BodyPart },
{ name: :organ_systems, label: 'Фильтр по системам органов', model: ::OrganInfo::OrganSystem }
],
text: [
{ name: :by_code, label: 'Фильтр по коду' }
],
q: :by_username
}
end