Rails: предыдущая и следующая запись из предыдущего запроса - PullRequest
10 голосов
/ 28 марта 2011

В моем приложении есть фотографии, и пользователи могут искать фотографии, которые соответствуют определенным критериям.Допустим, пользователь ищет фотографии по тегу, и мы получаем что-то вроде этого:

@results = Photo.tagged_with('mountain')

Теперь @results будет стандартным запросом activerecord с несколькими записями.Они будут показаны в сетке, и пользователь может затем нажать на фотографию.Это приведет пользователей к действию photos#show.

Итак, предположим, что пользователь что-то ищет, и приложение находит 5 записей [1,2,3,4,5], и пользователь нажимает на фотографию # 3.

На странице photo#show я хотел бы иметь возможность отображать «Следующее фото», «Предыдущее фото» и «Назад к поиску».

Единственное другое ограничение - это еслипользователь просматривает фотографию напрямую (через другую страницу или закладку и т. д.), поэтому не было бы логичной «следующей» и «предыдущей» фотографии, поскольку не было запроса, который привел бы их к этой фотографии, поэтому в этом случае шаблонне должен отображать содержимое, связанное с запросами.

Итак, я думал о том, как делать такие вещи, и у меня не так много хороших идей.Я полагаю, что мог бы сделать что-то вроде сохранения запроса в сеансе, чтобы иметь возможность вернуться к нему, но я не знаю, как найти фотографии, которые отображались бы слева и справа от выбранной фотографии.

У кого-нибудь есть примеры того, как делать такие вещи?

Ответы [ 4 ]

12 голосов
/ 02 апреля 2011

Итак, после долгих проб и ошибок вот что я придумал:

На моей фотомодели:

# 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

Этот метод возвращает следующую и предыдущую запись из поиска или определенного представления папки, принимая массив идентификаторов этих записей. Я генерирую этот идентификатор в любом представлении контроллера, которое создает представление запроса (т.е. страницу поиска и страницу просмотра по папкам):

Так, например, мой контроллер поиска содержит:

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

А затем действие # show с фотографиями содержит:

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 или закладку) к другой фотографии, которая была частью предыдущего запроса, запрос будет сохранен в сеансе, а ссылки на связанные фотографии все равно будут видны на второй фотографии, даже если они не должны быть на фотографии, загруженной с помощью закладки.

Однако в реальных случаях использования эту ситуацию было довольно сложно воссоздать, и на данный момент код работает очень хорошо. В какой-то момент, когда я придумаю хорошее исправление для этой оставшейся ошибки, я опубликую ее, но сейчас, если кто-то использует эту идею, просто знайте, что такая возможность существует.

2 голосов
/ 01 марта 2012

Андрей, ваш метод не универсален и не дает гарантированного правильного результата. Есть лучший способ сделать это. В вашей модели:

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
1 голос
/ 22 декабря 2013

Драгоценный камень, который я написал под названием Nexter , делает это для вас.

Вы передаете ему комбинацию AR Scope (aka ActiveRelation) плюс текущий объект / запись, и Nexter проверит предложение order для построения sql, который будет извлекать записи до / предыдущей и после / следующей.

В основном он смотрит на ActiveRelation # order_values ​​в порядке (a, b, c) и выдает:

# 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 и т. Д. *

Вот рабочий пример из README

Модель:

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])
```
0 голосов
/ 28 марта 2011

Вы можете взглянуть на то, что сделано в 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) для получения записей, которые имеют идентификаторы меньше / большечем идентификатор записи, которую вы передали.

Так как они являются областями действия, вы можете зацепить предыдущую с помощью .first, чтобы получить первый элемент, созданный перед текущим элементом, и .tagged_with ('mountain'). firstчтобы получить первый такой предмет с тегом 'гора'.

...