Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: Previous and Next record from previous query

My app has photos, and users can search for photos that meet certain criteria. Let's say a user searches for photos by tag, and we get something like this:

@results = Photo.tagged_with('mountain')

Now, @results is going to be a standard activerecord query with multiple records. These would be shown in a grid, and a user can then click on a photo. This would take the users to the photos#show action.

So, lets say the user searches for something and the app finds 5 records, [1,2,3,4,5], and the user clicks on photo #3.

On the photo#show page I'd like to be able to show a "Next Photo", "Previous Photo", and "Back to Search".

The only other constraint is, if the user browses to a photo directly (via another page or a bookmark etc) there wouldn't be a logical "next" and "previous" photo since there wasn't a query that led them to that photo, so in that case the template shouldn't render the query-related content at all.

So, I have been thinking about how to do this kind of thing and I don't really have a lot of good ideas. I suppose I could do something like store the query in session to be able to go back to it, but I don't know how to find the photos that would have shown up to the left and right of the selected photo.

Does anyone have any examples of how to do this kind of thing?

like image 488
Andrew Avatar asked Mar 27 '11 22:03

Andrew


2 Answers

Andrew, your method not universal and dont give guaranteed right result. There is better way to do this. In your model:

def previous
  Photo.where('photos.id < ?', self.id).first
end

def next
  Photo.where('photos.id > ?', self.id).last
end

And in views:

- if @photo.previous
  = link_to 'Previous', @photo.previous
- if @photo.next
  = link_to 'Next', @photo.next
like image 155
jbmeerkat Avatar answered Oct 14 '22 23:10

jbmeerkat


So, after much trial and error, here is what I came up with:

In my Photo model:

# NEXT / PREVIOUS FUNCTIONALITY
def previous(query)
  unless query.nil?
    index = query.find_index(self.id)
    prev_id = query[index-1] unless index.zero?
    self.class.find_by_id(prev_id)
  end
end

def next(query)
  unless query.nil?
    index = query.find_index(self.id)
    next_id = query[index+1] unless index == query.size
    self.class.find_by_id(next_id)
  end
end

This method returns the next and previous record from a search or a particular folder view by accepting an array of those records ids. I generate that ID in any controller view that creates a query view (ie the search page and the browse by folders page):

So, for instance, my search controller contains:

def search
  @search = @collection.photos.search(params[:search])
  @photos = @search.page(params[:page]).per(20)
  session[:query] = @photos.map(&:id)
end

And then the photo#show action contains:

if session[:query]
  @next_photo = @photo.next(session[:query])
  @prev_photo = @photo.previous(session[:query])
end

And lastly, my view contains:

- if @prev_photo || @next_photo
  #navigation
    .header Related Photos
    .prev
      = link_to image_tag( @prev_photo.file.url :tenth ), collection_photo_path(@collection, @prev_photo) if @prev_photo
      - if @prev_photo
        %span Previous
    .next
      = link_to image_tag( @next_photo.file.url :tenth ), collection_photo_path(@collection, @next_photo) if @next_photo
      - if @next_photo
        %span Next

Now it turns out this works great in regular browsing situations -- but there is one gotcha that I have not yet fixed:

Theoretically, if a user searches a view, then jumps to a photo they've generated a query in session. If, for some reason, they then browse directly (via URL or bookmark) to another photo that was part of the previous query, the query will persist in session and the related photos links will still be visible on the second photo -- even though they shouldn't be on a photo someone loaded via bookmark.

However, in real life use cases this situation has actually been pretty difficult to recreate, and the code is working very well for the moment. At some point when I come up with a good fix for that one remaining gotcha I'll post it, but for now if anyone uses this idea just be aware that possibility exists.

like image 20
Andrew Avatar answered Oct 14 '22 23:10

Andrew