Rails: “Next post” and “Previous post” links in my show view, how to?

前端 未结 7 1070
闹比i
闹比i 2020-12-01 00:51

I\'m new in Rails... smile

In my blog aplication I want to have a \"Previous post\" link and a \"Next post\" link in the bottom of my show view.

How do I do

相关标签:
7条回答
  • 2020-12-01 01:21

    You really just need to run 2 queries, one for each of "prev" and "next". Lets assume you have a created_at column.

    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
    

    Of course "this_post" is the current post.

    If your posts are stored with an auto_increment column and you dont re-use IDs you can just use the id column in place of created_at - the id column should already be indexed. If you want to use the created_at column then you will definitely want to have an index on that column.

    0 讨论(0)
  • 2020-12-01 01:22

    If each title is unique and you need alphabetical, try this in your Post model.

    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
    

    You can then link to those in the view.

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

    Untested, but it should get you close. You can change title to any unique attribute (created_at, id, etc.) if you need a different sort order.

    0 讨论(0)
  • 2020-12-01 01:25

    Give the will_paginate Gem a try. It provides all the features you need to paginate your post entries. learn here too

    You can look at here too for example code if you want add next and previous buttons.

    0 讨论(0)
  • 2020-12-01 01:33

    This is how I did it. Firstly, add a couple of named scopes to your Post model:

    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 the use of the :select option to limit the fields because you probably don't want to retrieve a fully-populated Post instance just for showing the links.

    Then in my posts_helper I have this method:

    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
    

    I'm sure this could be refactored to be less verbose, but the intent is clear. Obviously your markup requirements will be different to mine, so you may not choose to use an unordered list, for example.

    The important thing is the use of the if statements because if you're on the first post then they'll be no previous post and conversely, if you're on the last post they'll be no next post.

    Finally, simply call the helper method from your view:

    <%= sidebar_navigation_links %>
    
    0 讨论(0)
  • 2020-12-01 01:35

    I've created gem proximal_records especially for this kind of task and it works on any dynamically created scope in your model.

    https://github.com/dmitry/proximal_records

    Basic example:

    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
    
    0 讨论(0)
  • 2020-12-01 01:36

    My method will allow you to automatically use model scopes. For example, you may only want to display posts that are "published."

    In your model:

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

    In your view

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

    In your controller

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

    Associated RSpec tests:

    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
    

    Note: there will be small issues when you reach the first/last post in a collection. I recommend a view helper to conditionally show the previous or next button only if it exists.

    0 讨论(0)
提交回复
热议问题