2009-08-14 6 views

Répondre

41

Si chaque titre est unique et vous avez besoin alphabétique, essayez dans votre modèle 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 

Vous pouvez ensuite lier à ceux de la vue.

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

Non testé, mais il devrait vous aider à fermer. Vous pouvez remplacer title par un attribut unique (created_at, id, etc.) si vous avez besoin d'un ordre de tri différent.

+0

Merci! est parfait! :) – pollinoco

+0

@Sam, oui, c'est pourquoi j'ai dit "si chaque titre est unique". – ryanb

+0

désolé Ryan, je devrais être aveugle, votre pourrait: conditions => ["titre>? Et id>?", Titre, id] pour contourner. l'ajout d'un index sur le titre est un peu critique ici, sauf s'il s'agit d'une application jouet. –

-1

Vous avez juste besoin d'exécuter 2 requêtes, une pour chaque "prev" et "next". Supposons que vous avez une colonne 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 

Bien sûr "this_post" est le poste actuel. Si vos messages sont stockés avec une colonne auto_increment et que vous ne réutilisez pas les ID, vous pouvez simplement utiliser la colonne id à la place de created_at - la colonne id doit déjà être indexée. Si vous voulez utiliser la colonne created_at, vous voudrez certainement avoir un index sur cette colonne.

+1

vous allez avoir besoin d'un ordre par aussi bien ... –

+0

Eh oui, vous avez raison, une commande par est nécessaire. J'ai mis à jour ma réponse. –

+0

merci, mais si besoin détecter l'alphabétique suivant ou précédent: titre de poste? – pollinoco

0

Essayez le will_paginate. Il fournit toutes les fonctionnalités dont vous avez besoin pour paginer vos entrées de publication. apprendre here aussi

Vous pouvez regarder here aussi pour le code exemple si vous voulez ajouter les boutons suivant et précédent.

+3

will_paginate est un mauvais ajustement pour ce problème. Lorsque vous affichez un post, vous avez affaire à un post individuel, pas à une collection. –

1

Voici comment je l'ai fait. Tout d'abord, ajouter quelques named scopes à votre modèle 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 

Notez l'utilisation de l'option :select pour limiter les champs parce que vous ne voulez probablement pas récupérer un Post exemple entièrement peuplé juste pour montrer les liens .

Puis dans mon posts_helper J'ai cette méthode:

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 

Je suis sûr que cela pourrait être refactorisé être moins bavard, mais l'intention est claire. Évidemment, vos exigences de balisage seront différentes de la mienne, donc vous ne pouvez pas choisir d'utiliser une liste non ordonnée, par exemple. L'important est l'utilisation des instructions if parce que si vous êtes sur le premier poste, il n'y aura pas de post précédent et inversement, si vous êtes sur le dernier post, il n'y aura pas de post suivant.

Enfin, appelez simplement la méthode d'assistance de votre point de vue:

<%= sidebar_navigation_links %> 
+0

Salut John, merci pour votre réponse ... mais j'ai un problème .... Si je détruis un poste, je perds l'identité consécutive et je ne saurais pas qui est le prochain poste ou post précédent, parce que Post.find_by_id (id - 1,: select => 'titre, slug etc ...') autre problème est que je n'ai pas de permalien et je navigue dans ce moment par id mais si je remplace ": href => previous_post.permalink "par": href => previous_post.id " le href = emty :) pouvez-vous m'aider? merci encore! – pollinoco

13

J'ai utilisé les méthodes du modèle comme ci-dessous pour éviter le problème où id + 1 n'existe pas mais id + 2 fait.

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

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

Dans mon code de la vue, je fais ceci:

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

Ma méthode vous permettra d'utiliser automatiquement les étendues de modèle. Par exemple, vous souhaiterez peut-être uniquement afficher les publications "publiées".

Dans votre modèle:

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

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

À votre avis

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

Dans votre contrôleur

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

tests associés RSpec:

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 

Remarque: il y aura de petits problèmes lorsque vous atteindrez le premier/dernier message d'une collection. Je recommande un assistant d'affichage pour montrer conditionnellement le bouton précédent ou suivant seulement s'il existe.

0

J'ai créé le gem proximal_records spécialement pour ce type de tâche et il fonctionne sur n'importe quelle portée créée dynamiquement dans votre modèle.

https://github.com/dmitry/proximal_records

exemple de base:

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