I need to get the previous and next active record objects with Rails. I did it, but don\'t know if it\'s the right way to do that.
What I\'ve got:
Controller
I think it would be faster to do it with only two SQL requests, that only select two rows (and not the entire table). Considering that your default order is sorted by id (otherwise, force the sorting by id) :
@previous_product = Product.where('id < ?', params[:id]).last
@next_product = Product.where('id > ?', params[:id]).first
If the product is the last, then @next_product will be nil, and if it is the first, then, @previous_product will be nil.
Write these methods in your Product
model:
class Product
def next
self.class.where("id > ?", id).first
end
def previous
self.class.where("id < ?", id).last
end
end
Now you can do in your controller:
@product = Product.friendly.find(params[:id])
@previous_product = @product.next
@next_product = @product.previous
Please try it, but its not tested. Thanks
There's no easy out-of-the-box solution.
A little dirty, but working way is carefully sorting out what conditions are there for finding next and previous items. With id
it's quite easy, since all id
s are different, and Rails Guy's answer describes just that: in next
for a known id
pick a first entry with a larger id
(if results are ordered by id
, as per defaults). More than that - his answer hints to place next
and previous
into the model class. Do so.
If there are multiple order criteria, things get complicated. Say, we have a set of rows sorted by group
parameter first (which can possibly have equal values on different rows) and then by id
(which id different everywhere, guaranteed). Results are ordered by group
and then by id
(both ascending), so we can possibly encounter two situations of getting the next element, it's the first from the list that has elements, that (so many that):
group
and a larger id
group
Same with previous element: you need the last one from the list
group
and a smaller id
group
Those fetch all next and previous entries respectively. If you need only one, use Rails' first
and last
(as suggested by Rails Guy) or limit(1)
(and be wary of the asc/desc ordering).
This is what order_query does. Please try the latest version, I can help if it doesn't work for you:
class Product < ActiveRecord::Base
order_query :my_order,
[:product_sub_group_id, :asc],
[:id, :asc]
default_scope -> { my_order }
end
@product.my_order(@collection.products).next
@collection.products.my_order_at(@product).next
This runs one query loading only the next record. Read more on Github.