2011-03-27 10 views
8

Mi aplicación tiene fotos y los usuarios pueden buscar fotografías que cumplan con ciertos criterios. Digamos que un usuario busca fotos por etiquetas, y obtenemos algo como esto:Rieles: registro anterior y siguiente de la consulta anterior

@results = Photo.tagged_with('mountain') 

Ahora, @results va a ser una consulta activerecord estándar con varios registros. Estos se mostrarán en una cuadrícula, y un usuario puede hacer clic en una foto. Esto llevaría a los usuarios a la acción photos#show.

Digamos que el usuario busca algo y la aplicación encuentra 5 registros, [1,2,3,4,5], y el usuario hace clic en la foto # 3.

En la página photo#show me gustaría poder mostrar una "Foto siguiente", una "Foto anterior" y una "Volver a buscar".

La única otra restricción es que si el usuario navega hacia una foto directamente (a través de otra página o un marcador, etc.) no habría una foto lógica "siguiente" y "previa" ya que no había una consulta que los llevó a esa foto, por lo que en ese caso la plantilla no debería representar el contenido relacionado con la consulta.

Por lo tanto, he estado pensando en cómo hacer este tipo de cosas y realmente no tengo muchas buenas ideas. Supongo que podría hacer algo así como almacenar la consulta en sesión para poder volver a ella, pero no sé cómo encontrar las fotos que se habrían mostrado a la izquierda y a la derecha de la foto seleccionada.

¿Alguien tiene algún ejemplo de cómo hacer este tipo de cosas?

+0

posible duplicado de [Carriles: "Next publicar "y" Previo nosotros publicamos "enlaces en mi vista de programa, ¿cómo?] (http://stackoverflow.com/questions/1275963/rails-next-post-and-previous-post-links-in-my-show-view-how- a) –

Respuesta

11

Así que, después de mucho ensayo y error, esto es lo que ocurrió:

En mi modelo de la foto:

# 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 

Este método devuelve el siguiente y anterior registro de una búsqueda o un particular, vista de carpeta al aceptar una matriz de esos identificadores de registros.Genero ese ID en cualquier vista controlador que crea una vista de consulta (es decir, la página de búsqueda y el navegar por la página de carpetas):

Así, por ejemplo, mi controlador de búsqueda contiene:

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

Y entonces el foto # show de acción contiene:

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

Y por último, mi vista contiene:

- 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 

Ahora resulta que esto funciona muy bien en situaciones de navegación regulares - pero hay una Gotcha que todavía no he fijado:

En teoría, si un usuario busca en una vista, luego salta a una foto que han generado una consulta en sesión. Si, por algún motivo, navegan directamente (a través de URL o de marcadores) a otra foto que formaba parte de la consulta anterior, la consulta se mantendrá en sesión y los enlaces de las fotos relacionadas seguirán visibles en la segunda foto, aunque no deberían estar en una foto que alguien haya cargado a través de un marcador.

Sin embargo, en casos de uso reales, esta situación ha sido muy difícil de recrear, y el código funciona muy bien por el momento. En algún momento cuando encuentre una buena solución para ese gotcha restante, lo publicaré, pero por ahora, si alguien usa esta idea, tenga en cuenta que existe la posibilidad.

+0

Puede resolver su _gotcha_ poniendo el ID de la consulta persistente en la url para los enlaces siguientes/anteriores y los enlaces de la página de búsqueda. De esta forma, si ve la ID y coincide con la última consulta persistente, muestre los botones siguiente/anterior. Si la identificación no está allí porque el usuario escribió la URL o llegó a la página de otra manera, no muestre los botones. –

+0

@Brian: ¡Oooh! Es una buena idea, ¡trabajaré para implementar algo así! – Andrew

+0

Muchas gracias por publicar esto, ha sido realmente útil. – snowangel

0

Usted podría tomar un vistazo a lo hecho en 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"} } 

En esencia, son ámbitos (carriles 3 pre sintaxis) para recuperar los registros que tienen ID menor/mayor que el ID del registro que pasado.

Como son alcances, puede encadenar anterior con .first para obtener el primer elemento creado antes del elemento actual, y .tagged_with ('montaña') primero para obtener el primer elemento etiquetado con ' montaña'.

2

Andrew, su método no es universal y no da el resultado correcto garantizado. Hay una mejor manera de hacer esto. En su modelo:

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

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

Y en vistas:

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

Eso no * hace * lo que estaba tratando de hacer. No solo quiero la siguiente foto, quiero la siguiente foto de los resultados de búsqueda más recientes de los usuarios específicos. Estoy seguro de que mi método no es perfecto, e hice una lista de mi "truco" más conocido, pero funciona según lo necesito en mi aplicación. – Andrew

+1

No hizo lo que quería, pero fue perfecto para mí :) – Arcolye

+0

Para mí, usar Rails 4 no funciona del todo bien. Tu "próximo" me da el "último" y el "anterior" dame el "primero". –

1

Una joya que escribí llamado Nexter lo hace por usted.

Usted le pasa una combinación AR Scope (también conocida como ActiveRelation) más el Objeto/Registro actual y Nexter inspeccionará la cláusula order para construir el sql que obtendrá los registros before/previous y after/next.

Básicamente se observan las ActiveRelation # order_values ​​en orden (a, b, c) y sale con:

# 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) 

eso es sólo la esencia de la misma. También funciona con valores de asociación y es inteligente para encontrar los valores inversos para la parte anterior. Para mantener el estado de su búsqueda (o combinación de alcance) puede usar otra lib como siphon, saqueo, has_scope, etc ...

Aquí hay una working example desde el README

El modelo:

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

    def next 
    @nexter.next 
    end 

    def previous 
    @nexter.previous 
    end 
end 

El controlador

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 

La vista:

<%= 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]) 
``` 
Cuestiones relacionadas