我的应用有照片,用户可以搜索符合特定条件的照片。假设用户按标签搜索照片,我们会得到类似的结果:
@results = Photo.tagged_with('mountain')
现在,@results
将成为具有多个记录的标准activerecord查询。这些将以网格显示,然后用户可以点击照片。这会让用户进行photos#show
操作。
因此,假设用户搜索某些内容,该应用会找到5条记录[1,2,3,4,5]
,并且用户点击了第3张照片。
在photo#show
页面上,我希望能够显示“下一张照片”,“上一张照片”和“返回搜索”。
唯一的另一个限制是,如果用户直接浏览照片(通过另一个页面或书签等),则不会有逻辑“下一个”和“上一个”照片,因为没有一个查询导致他们到那张照片,所以在这种情况下,模板根本不应该呈现与查询相关的内容。
所以,我一直在考虑如何做这种事情,我并没有太多好主意。我想我可以做一些事情,比如在会话中存储查询以便能够回到它,但我不知道如何找到所选照片左右两侧显示的照片。
有没有人有任何关于如何做这种事情的例子?
答案 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>