【问题标题】:Rails: "Next post" and "Previous post" links in my show view, how to?Rails:我的显示视图中的“下一篇文章”和“上一篇文章”链接,如何?
【发布时间】:2009-08-14 03:54:28
【问题描述】:

我是 Rails 的新手...微笑

在我的博客应用程序中,我希望在我的显示视图底部有一个“上一篇文章”链接和一个“下一篇文章”链接。

我该怎么做?

谢谢!

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    如果每个标题都是唯一的并且您需要按字母顺序排列,请在您的 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
    

    然后您可以链接到视图中的那些。

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

    未经测试,但它应该让你接近。如果您需要不同的排序顺序,可以将 title 更改为任何唯一属性(created_atid 等)。

    【讨论】:

    • 抱歉,Ryan,我一定是瞎了眼,你可以 :conditions => ["title > ? and id > ?", title, id] 来解决。除非它是一个玩具应用程序,否则在标题上添加索引在这里很重要。
    • 语法略有不同:self.class.where("today_date &lt; ?", today_date).order(today_date: :desc).first
    【解决方案2】:

    我使用下面的模型方法来避免 id + 1 不存在但 id + 2 存在的问题。

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

    在我的视图代码中,我只是这样做:

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

    【讨论】:

      【解决方案3】:

      我的方法将允许您自动使用模型范围。例如,您可能只想显示“已发布”的帖子。

      在您的模型中:

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

      在你看来

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

      在你的控制器中

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

      相关的 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
      

      注意:当您到达收藏集中的第一个/最后一个帖子时,会有一些小问题。我推荐一个视图助手,仅在存在时有条件地显示上一个或下一个按钮。

      【讨论】:

        【解决方案4】:

        我就是这样做的。首先,在您的 Post 模型中添加几个 named scopes

        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
        

        请注意使用 :select 选项来限制字段,因为您可能不希望仅为了显示链接而检索完全填充的 Post 实例。

        然后在我的posts_helper我有这个方法:

        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
        

        我确信这可以重构为不那么冗长,但意图很明确。显然,您的标记要求与我的不同,因此您可能不会选择使用无序列表。

        重要的是使用if 语句,因为如果您在第一篇文章中,那么它们将不是以前的文章,相反,如果您在最后一篇文章中,它们将不是下一篇文章.

        最后,只需从您的视图中调用辅助方法:

        <%= sidebar_navigation_links %>
        

        【讨论】:

        • 嗨约翰,谢谢你的回答...但是我有一个问题...如果我销毁一个帖子,我会丢失连续的 ID,我不知道下一个帖子是谁或上一篇文章,因为 Post.find_by_id(id - 1, :select => 'title, slug etc...') 另一个问题是我没有永久链接,我现在按 id 导航,但如果我替换“ :href => previous_post.permalink" by ":href => previous_post.id" href = emty :) 你能帮帮我吗?再次感谢!
        【解决方案5】:

        试试will_paginate Gem。它提供了对帖子条目进行分页所需的所有功能。也学here

        如果您想添加下一个和上一个按钮,您也可以查看 here 示例代码。

        【讨论】:

        • will_paginate 不适合这个问题。当您显示帖子时,您处理的是单个帖子,而不是集合。
        【解决方案6】:

        我专门为此类任务创建了 gem proximal_records,它适用于您模型中任何动态创建的范围。

        https://github.com/dmitry/proximal_records

        基本示例:

        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
        

        【讨论】:

          【解决方案7】:

          您实际上只需要运行 2 个查询,“prev”和“next”各一个。假设您有一个 created_at 列。

          伪代码:

          # 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
          

          “this_post”当然是当前帖子。

          如果您的帖子使用 auto_increment 列存储并且您不重复使用 ID,则可以使用 id 列代替 created_at - id 列应该已经被索引。如果您想使用 created_at 列,那么您肯定希望在该列上有一个索引。

          【讨论】:

          • 是的,你是对的,需要订购。我更新了我的回复。
          • 谢谢!但如果需要检测下一个或上一个字母:标题帖子?
          最近更新 更多