Далее, предыдущие записи с использованием именованной области

#ruby-on-rails #activerecord

#ruby-on-rails #activerecord

Вопрос:

У меня есть модель, для которой я хочу получить следующую (ые) и предыдущую (ые) записи. Я хочу сделать это через named_scope в модели, а также передать в качестве аргумента X количество следующих / предыдущих записей для возврата.

Например, допустим, у меня есть 5 записей:

  • Запись 1
  • Record2
  • Запись 3
  • Record4
  • Запись 5

Я хочу иметь возможность вызывать Model.previous или Model.previous(1), чтобы вернуть Record2. Аналогично, я хочу иметь возможность вызывать Model.next или Model.next(1), чтобы возвращать Record4. В качестве другого примера я хочу иметь возможность вызывать Model.previous(2) для возврата Record3. Я думаю, вы поняли идею.

Как я могу этого добиться?

Ответ №1:

Чтобы реализовать что-то вроде ‘Model.previous’, сам класс должен был бы иметь ‘текущее’ состояние. Это имело бы смысл, если бы «текущая» запись (возможно, в системе планирования публикации?) в вашем примере была Record3, но ваш пример этого не предполагает.

Если вы хотите взять экземпляр модели и получить следующую или предыдущую запись, ниже приведен простой пример:

 class Page < ActiveRecord::Base
  def previous(offset = 0)    
    self.class.first(:conditions => ['id < ?', self.id], :limit => 1, :offset => offset, :order => "id DESC")
  end

  def next(offset = 0)
    self.class.first(:conditions => ['id > ?', self.id], :limit => 1, :offset => offset, :order => "id ASC")
  end
end
  

Если это так, вы могли бы сделать что-то вроде:

 @page = Page.find(4)
@page.previous
  

Также работало бы:

 @page.previous(1)
@page.next
@page.next(1)
  

Очевидно, это предполагает, что идея ‘next’ и ‘previous’ заключается в поле ‘id’, которое, вероятно, не очень распространялось бы на срок службы приложения.

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

 named_scope :previous, lambda { |current, offset| { :conditions => ['id < ?', current], :limit => 1, :offset => offset, :order => "id DESC" }}
  

Что означает, что вы могли бы вызвать:

 Page.previous(4,1)
  

Где ‘4’ — это идентификатор записи, с которой вы хотите начать, а 1 — это номер, с которого вы хотите вернуться назад.

Комментарии:

1. Как насчет случая, когда ‘next’ и ‘previous’ находятся в поле ‘created_at’?

2. Просто измените запросы так, чтобы все ссылки на ‘id’ (включая предложение :order) были изменены на ‘created_at’, и все должно работать нормально.

Ответ №2:

Я добавил эту функцию в свой gem by_star в прошлом месяце. В нем могут быть другие функции, которые вас интересуют, так что проверьте это.

Это работает, когда у вас есть существующая запись, а затем вы вызываете для нее previous или next :

 Page.find(:last).previous
  

Комментарии:

1. Этот драгоценный камень выглядит гладко. Попытался установить ее, но возникли некоторые проблемы. Есть какая-нибудь документация, в которой говорится, как ее запустить?

2. Нужно ли вам использовать инструкцию include или require?

3. @keruilin: С какой именно проблемой вы столкнулись?

Ответ №3:

Я написал gem для выполнения этого для произвольных условий заказа, order_query:

 class Issue < ActiveRecord::Base
  include OrderQuery
  order_query :order_display, [
    [:priority, %w(high medium low)],
    [:valid_votes_count, :desc, sql: '(votes - suspicious_votes)'],
    [:updated_at, :desc],
    [:id, :desc]
  ]
  def valid_votes_count
    votes - suspicious_votes
  end
end

Issue.order_display         #=> ActiveRecord::Relation<...>
Issue.reverse_order_display #=> ActiveRecord::Relation<...>

# get the order object, scope default: Post.all
p = Issue.find(31).order_list(scope) #=> OrderQuery::RelativeOrder<...>
p.before         #=> ActiveRecord::Relation<...>
p.previous       #=> Issue<...>
p.position       #=> 5
p.next           #=> Issue<...>
p.after          #=> ActiveRecord::Relation<...>