简体   繁体   中英

Active Admin filter params passed to ransacker

I am trying to pass a couple IDs through to a custom ransacker filter for active admin

   filter :hasnt_purchased_product_items_in, as: :select, collection: -> { ProductItem.all }, multiple: true

The ransacker code

ransacker :hasnt_purchased_product_items, formatter: proc { |product_id|
    users_who_purchased_ids = User.joins(:orders => :items).where("orders.status = 'success'").where("order_items.orderable_id IN (?)", product_id) # return only id-s of returned items.
    ids = User.where.not(id: users_who_purchased_ids).pluck(:id)
    ids.present? ? ids : nil # return ids OR nil!
} do |parent| # not sure why this is needed .. but it is :)
    parent.table[:id]
end 

This works for a single query but not for multiple, the SQL is doing 2 separate searches instead of one

It's running the following 2 statements

order.items.orderable_id IN ('90')

and

order.items.orderable_id IN ('91')

in 2 separate SQL statements, instead of what i want which is

order.items.orderable_id IN ('90','91')

the submitted info from the active admin page is

q[hasnt_purchased_product_items_in][]: 90
q[hasnt_purchased_product_items_in][]: 91

i think i need to do something to the incoming parameter at the ransacker stage but i'm not sure how to deal with that

It shouldn't be stepping in. The param should be |product_ids| and give us an array object inside the proc that then gets used. Your ransacker should return an active record association or an arel query.

I would try each of these:

  1. Use "_in" in the ransacker name, which bypasses the array logic.
ransacker :hasnt_purchased_product_items_in do |product_ids|
  users_who_purchased_ids = User.joins(orders: :items).where(orders: {status: 'success'}, order_items: {orderable_id: product_ids}) # return only id-s of returned items.
  User.where.not(id: users_who_purchased_ids)
end
  1. Same ransacker name, but with block use rather than formatter.
ransacker :hasnt_purchased_product_items do |product_ids|
  users_who_purchased_ids = User.joins(orders: :items).where(orders: {status: 'success'}, order_items: {orderable_id: product_ids}) # return only id-s of returned items.
  User.where.not(id: users_who_purchased_ids)
end 

I've had to guess the object to return because it's not clear which model this ransacker is on. If you can tell me the objective (eg filtering product recommendations for a user to exclude their purchases) then I can update the answer.

  1. Create a scope and make it available to ransack.

User.rb

  scope :hasnt_purchased_product_items_in, ->(product_ids){
   users_who_purchased_ids = joins(orders: :items).where(orders: {status: 'success'}, order_items: {orderable_id: product_ids}) # return only id-s of returned items.
   where.not(id: users_who_purchased_ids)
  }

  def self.ransackable_scopes(auth_object = nil)
    %i(hasnt_purchased_product_items_in)
  end

Then this works:

User.ransack({hasnt_purchased_product_items_in: [[1,2,3]]}).result

Note that the ransack want's an array of args. We want one arg, which is an array.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM