最近接手一个代码,流程同我们平时看到的一样:
这样就导致每个 controller/model 充斥了不同的判断条件和查询的组装,如
result = Booking.where(corp_id: corp_id).includes(:member,:coach,:training_item)
result = result.where(coach_id: coach_id) if coach_id.present?
result = result.where(member_id: member_id) if member_id.present?
result = result.where(training_item_id: training_item_id) if training_item_id.present?
result = result.where(train_status: train_status) if train_status.present?
result = result.where("name like ?",'%#{member_name}%') if member_name.present?
...
太过于繁琐!
于是考虑将中间的共性提取出来,做成一个单独的 concern。
直接上代码,刚写的,还热乎呢 加了几个条件,更多的条件慢慢加上
#encoding: utf-8
module ActiveRecordSupport
extend ActiveSupport::Concern
def filter params={}
result = self
if respond_to?(:filter_mapping)
filter_mapping.each do |field, config|
if config.is_a?(Hash)
method = config[:method]
type = config[:type]
default_value = config[:default]
else
method = config
type = nil
end
value = params[field.to_sym]||default_value
next if type.nil? && value.blank?
case method.to_sym
when :equal
result = result.where("#{field.to_s}=?", value)
when :like
result = result.where("#{field.to_s} like ?", "%#{value}%")
when :compare
op = value[0]
next unless %w{= > < >= <=}.include?(op)
result = result.where("#{field.to_s} #{op} ?", "#{value[1..value.length]}")
when :between
value_begin = params["#{field.to_s}_begin"]
value_end = params["#{field.to_s}_end"]
if type == :date
value_begin = value_begin.to_date.beginning_of_day if value_begin.present?
value_end = value_end.to_date.end_of_day if value_end.present?
elsif type == :time
value_begin = value_begin.to_time.beginning_of_day if value_begin.present?
value_end = value_end.to_time.end_of_day if value_end.present?
end
result = result.where("#{field.to_s} >= ?", value_begin) if value_begin.present?
result = result.where("#{field.to_s} <= ?", value_end) if value_end.present?
else
# type code here
end
end
end
result
end
end
end
在 model 定义如下的元数据
class Account < ActiveRecord::Base
include ActiveRecordSupport
class<<self
def filter_mapping
{
account: :equal,
account_name: :like,
status: :equal
}
end
end
end
这样在 Controller 中直接如下操作
class AccountsController < ConsoleController
def index
@accounts = Account.filter(params).page(@page)
end
....
end
class<<self
def filter_mapping
{
account: :equal,
day: {method: :between, type: :date}
}
end
end
前段页面支持选择时间段,格式:字段_begin 和 字段_end
页面如下:
<div class="col-sm-2 m-b-xs">
<input type="text" name="day_begin" placeholder="开始时间" class="input-sm datepicker">
</div>
<div class="col-sm-2 m-b-xs">
<input type="text" name="day_end" placeholder="结束时间" class="input-sm datepicker">
</div>
class<<self
def filter_mapping
{
request_time: {method: :between,type: :time}
}
end
end
class<<self
def filter_mapping
{
count: :compare,
}
end
end
注意前段需要将比较字符放在第一位,如:
<div class="col-sm-2 m-b-xs">
<select name="count">
<option value="">数量</option>
<option value="=0">等于0</option>
<option value=">0">大于0</option>
</select>
</div>
支持默认值,如查询默认是查询 大于 0 的内容
class<<self
def filter_mapping
{
account: :equal,
count: {method: :compare, default: '>0'},
}
end
end
想了想,主要用到也就这几个条件了,有再加,一会整理下放到 github。