2009-08-14 12 views

Odpowiedz

41

Jeśli każdy tytuł jest wyjątkowy i trzeba alfabetycznej, spróbuj tego w modelu Post.

def previous_post 
    self.class.first(:conditions => ["title < ?", title], :order => "title desc") 
end 

def next_post 
    self.class.first(:conditions => ["title > ?", title], :order => "title asc") 
end 

Następnie można utworzyć łącze do tych w widoku.

<%= link_to("Previous Post", @post.previous_post) if @post.previous_post %> 
<%= link_to("Next Post", @post.next_post) if @post.next_post %> 

Niewygodne, ale powinno Cię do siebie zbliżyć. Możesz zmienić numer title na dowolny unikalny atrybut (created_at, id itp.), Jeśli potrzebujesz innej kolejności sortowania.

+0

Dziękujemy! jest doskonale! :) – pollinoco

+0

@Sam, tak, dlatego powiedziałem "jeśli każdy tytuł jest unikalny". – ryanb

+0

Przepraszam Ryan, muszę być ślepy, twoje może: conditions => ["title>? And id>?", Title, id] do obejścia. dodanie indeksu na tytuł jest tu krytyczne, chyba że jest to aplikacja zabawkowa. –

-1

Naprawdę wystarczy uruchomić 2 zapytania, po jednym dla każdego z "prev" i "next". Załóżmy, że masz kolumnę created_at.

Psuedo-code:

# get prev 
select * from posts where created_at < #{this_post.created_at} order by created_at desc limit 1 

# get next 
select * from posts where created_at > #{this_post.created_at} order by created_at desc limit 1 

Oczywiście "this_post" jest obecny postu.

Jeśli twoje posty są przechowywane z kolumną auto_increment i nie korzystasz ponownie z identyfikatorów, możesz po prostu użyć kolumny id w miejsce created_at - kolumna id powinna już być zindeksowana. Jeśli chcesz użyć kolumny created_at, to na pewno chcesz mieć indeks w tej kolumnie.

+1

będziesz potrzebował zamówienie, jak również ... –

+0

Tak, masz rację, zamówienia przez to potrzebne. Zaktualizowałem moją odpowiedź. –

+0

thak you !, ale jeśli potrzebujesz wykryć następny lub poprzedni alfabetyczny: tytuł postu? – pollinoco

0

Podaruj klej will_paginate spróbować. Zapewnia wszystkie funkcje potrzebne do tworzenia stronicowania wpisów w postach. uczyć się here zbyt

Możesz również spojrzeć na kod here, jeśli chcesz dodać następny i poprzedni przycisk.

+3

will_paginate jest kiepskim rozwiązaniem tego problemu. Gdy wyświetlasz post, masz do czynienia z konkretnym wpisem, a nie z kolekcją. –

1

Tak to zrobiłem. Po pierwsze, dodać kilka named scopes do modelu Post:

def previous 
    Post.find_by_id(id - 1, :select => 'title, slug etc...') 
end 

def next 
    Post.find_by_id(id + 1, :select => 'title, slug etc...') 
end 

Note użycie opcji :select do ograniczenia pola, ponieważ prawdopodobnie nie chcą odzyskać w pełni wypełnione Post instancji tylko do pokazywania linków .

Wtedy w moim posts_helper mam tej metody:

def sidebar_navigation_links 
    next_post = @post.next 
    previous_post = @post.previous 
    links = '' 
    if previous_post 
    links << content_tag(:h3, 'Previous') 
    links << content_tag(:ul, content_tag(:li, 
           content_tag(:a, previous_post.title, 
              :href => previous_post.permalink))) 
    end 
    if next_post 
    links << content_tag(:h3, 'Next', :class => 'next') if previous_post 
    links << content_tag(:h3, 'Next') if previous_post.nil? 
    links << content_tag(:ul, content_tag(:li, 
           content_tag(:a, next_post.title, 
              :href => next_post.permalink))) 
    end 
    content_tag(:div, links) 
end 

jestem pewien, że to może być refactored być mniej gadatliwy, ale intencja jest jasna. Oczywiście twoje wymagania dotyczące oznaczania będą inne niż moje, więc nie możesz na przykład użyć listy nieuporządkowanej.

Ważną rzeczą jest użycie instrukcji if, ponieważ jeśli jesteś w pierwszym poście, to nie będzie żadnego poprzedniego postu i na odwrót, jeśli jesteś w ostatnim poście, to nie będzie następnego postu.

Wreszcie, po prostu wywołać metodę pomocnika z widoku:

<%= sidebar_navigation_links %> 
+0

Cześć John, dziękuję za odpowiedź ... ale mam problem ... Jeśli zniszczę post, stracę kolejny identyfikator i nie wiem, kto jest następny post lub poprzedni post, ponieważ Post.find_by_id (id - 1,: select => 'title, slug etc ...') innym problemem jest to, że nie mam bezpośredniego łącza i nawiguję w tym momencie przez id, ale jeśli zmienię ": href => previous_post.permalink "przez": href => previous_post.id " the href = emty :) Czy możesz mi pomóc? dzięki jeszcze raz! – pollinoco

13

Użyłem metod modeli jak poniżej, aby uniknąć problemu, w którym id + 1 nie istnieje, ale id + 2 ma.

def previous 
    Post.where(["id < ?", id]).last 
end 

def next 
    Post.where(["id > ?", id]).first 
end 

W moim kodu widoku, po prostu to zrobić:

- if @post.previous 
    = link_to "< Previous", @post.previous 
    - if @post.next 
    = link_to "Next >", @post.next 
3

Moja metoda pozwala na automatyczne stosowanie modelowych zasięgów. Na przykład możesz wyświetlać tylko posty "opublikowane".

W modelu:

def self.next(post) 
    where('id < ?', post.id).last 
end 

def self.previous(post) 
    where('id > ?', post.id).first 
end 

W widoku

<%= link_to 'Previous', @posts.previous(@post) %> 
<%= link_to 'Next', @posts.next(@post) %> 

W kontrolerze

@photos = Photo.published.order('created_at') 

Associated rspec testy:

describe '.next' do 
    it 'returns the next post in collection' do 
    fourth_post = create(:post) 
    third_post = create(:post) 
    second_post = create(:post) 
    first_post = create(:post) 

    expect(Post.next(second_post)).to eq third_post 
    end 

    it 'returns the next post in a scoped collection' do 
    third_post = create(:post) 
    decoy_post = create(:post, :published) 
    second_post = create(:post) 
    first_post = create(:post) 

    expect(Post.unpublished.next(second_post)).to eq third_post 
    end 
end 

describe '.previous' do 
    it 'returns the previous post in collection' do 
    fourth_post = create(:post) 
    third_post = create(:post) 
    second_post = create(:post) 
    first_post = create(:post) 

    expect(Post.previous(third_post)).to eq second_post 
    end 

    it 'returns the previous post in a scoped collection' do 
    third_post = create(:post) 
    second_post = create(:post) 
    decoy_post = create(:post, :published) 
    first_post = create(:post) 

    expect(Post.unpublished.previous(second_post)).to eq first_post 
    end 
end 

Uwaga: po pierwszym/ostatnim poście w kolekcji pojawią się drobne problemy. Polecam przeglądarkę widoku, aby warunkowo wyświetlała poprzedni lub następny przycisk tylko wtedy, gdy istnieje.

0

Stworzyłem klejnot proximal_records specjalnie do tego rodzaju zadań i działa on na każdym dynamicznie tworzonym zakresie w twoim modelu.

https://github.com/dmitry/proximal_records

przykład podstawowa:

class Article < ActiveRecord::Base 
    include ProximalRecords 
end 


scope = Article.title_like('proximal').order('created_at DESC, title ASC') 
current_record = scope.to_a[5] 
p, n = current_record.proximal_records(scope) # will find record 5 and 7 
Powiązane problemy