Rails:上一个查询的上一个和下一个记录

时间:2011-03-27 22:31:46

标签: ruby-on-rails ruby-on-rails-3 activerecord views

我的应用有照片,用户可以搜索符合特定条件的照片。假设用户按标签搜索照片,我们会得到类似的结果:

@results = Photo.tagged_with('mountain')

现在,@results将成为具有多个记录的标准activerecord查询。这些将以网格显示,然后用户可以点击照片。这会让用户进行photos#show操作。

因此,假设用户搜索某些内容,该应用会找到5条记录[1,2,3,4,5],并且用户点击了第3张照片。

photo#show页面上,我希望能够显示“下一张照片”,“上一张照片”和“返回搜索”。

唯一的另一个限制是,如果用户直接浏览照片(通过另一个页面或书签等),则不会有逻辑“下一个”和“上一个”照片,因为没有一个查询导致他们到那张照片,所以在这种情况下,模板根本不应该呈现与查询相关的内容。

所以,我一直在考虑如何做这种事情,我并没有太多好主意。我想我可以做一些事情,比如在会话中存储查询以便能够回到它,但我不知道如何找到所选照片左右两侧显示的照片。

有没有人有任何关于如何做这种事情的例子?

4 个答案:

答案 0 :(得分:12)

所以,经过多次试验和错误,我想出了这个:

在我的照片模型中:

# 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

此方法通过接受这些记录ID的数组,从搜索或特定文件夹视图返回下一条和上一条记录。我在任何创建查询视图的控制器视图中生成该ID(即搜索页面和按文件夹浏览页面):

因此,例如,我的搜索控制器包含:

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

然后照片#show action包含:

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

最后,我的观点包含:

- 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

现在事实证明这在常规浏览情况下效果很好 - 但是还有一个 gotcha 我尚未修复:

理论上,如果用户搜索视图,则会跳转到他们在会话中生成查询的照片。如果由于某种原因,他们然后直接(通过URL或书签)浏览到上一个查询中的另一张照片,查询将在会话中保留,相关的照片链接仍将在第二张照片上显示 - 即使他们不应该在通过书签加载的照片上。

然而,在现实生活中的用例中,这种情况实际上很难重新创建,而且代码目前运行良好。在某些时候,我想出了一个很好的解决方案,我会发布它,但是现在,如果有人使用这个想法,请注意存在这种可能性。

答案 1 :(得分:2)

安德鲁,你的方法并不普遍,也没有保证正确的结果。有更好的方法来做到这一点。 在你的模型中:

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

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

在观点中:

- if @photo.previous
  = link_to 'Previous', @photo.previous
- if @photo.next
  = link_to 'Next', @photo.next

答案 2 :(得分:1)

我写的一个名为Nexter的宝石为你做了。

你传递AR Scope组合(又名ActiveRelation)加上当前的Object / Record和Nexter将检查order子句以构建将获取before / previous和after / next记录的sql。

基本上,它按顺序(a,b,c)查看ActiveRelation#order_values,并附带:

# pseudo code
where(a = value_of a AND b = value of b AND c > value of c).or
where(a = value_of a AND b > value of b).or
where(a > value of a)

这只是它的要点。它也适用于关联值,并且很聪明地找到前一部分的反向值。要保持搜索状态(或范围组合),您可以使用其他库,如siphon,ransack,has_scope等...

以下是自述文件中的working example

模特:

class Book
  def nexter=(relation)
    @nexter = Nexter.wrap(relation, self)
  end

  def next
    @nexter.next
  end

  def previous
    @nexter.previous
  end
end

控制器

class BookController
  before_filter :resource, except: :index

  def resource
    @book_search = BookSearch.new(params[:book_search])

    @book ||= Book.includes([:author]).find(params[:id]).tap do |book|
      book.nexter = siphon(Book.scoped).scope(@book_search)
    end
  end
end

观点:

<%= link_to "previous", book_path(@book.previous, book_search: params[:book_search]) %>
<%= link_to "collection", book_path(book_search: params[:book_search]) %>
<%= link_to "next", book_path(@book.next, book_search: params[:book_search])
```

答案 3 :(得分:0)

您可以查看ActsAsAdjacent中的内容:

named_scope :previous, lambda { |i| {:conditions => ["#{self.table_name}.id < ?", i.id], :order => "#{self.table_name}.id DESC"} }
named_scope :next, lambda { |i| {:conditions => ["#{self.table_name}.id > ?", i.id], :order => "#{self.table_name}.id ASC"} }

基本上,它们是范围(前Rails 3语法)来检索ID小于/大于您传入的记录的ID的记录。

由于它们是作用域,你可以用.first连接上一个以获得在当前项之前创建的第一个项目,以及.tagged_with('mountain')。首先获得第一个用'mountain'标记的项目。< / p>