Rails 5仅限某个标签的上一篇或下一篇文章

时间:2017-10-13 12:41:41

标签: ruby-on-rails ruby

我有一个名为posts的资源,其中有很多。但是,每个帖子可以有多个标签。我希望用户能够从所选的标签转到上一篇文章和下一篇文章。我让它适用于前一个数据库中的所有帖子,但当我点击一个标签并显示所有标签时,prev / next不符合标签的内容。

如果我访问与routes.rb get 'tags/:tag', to: 'posts#index', as: :tag中定义的代码相关联的url,它将列出索引中的所有标记。我不希望这样,我希望用户能够单击上一个或下一个,并且仅对与标签关联的帖子执行此操作。

注意:我使用的是friendly_id gem

控制器/ posts_controller.rb

  def index
    @posts = Post.all

    if params[:tag]
      @posts = Post.tagged_with(params[:tag])
    else
      @posts = Post.all
    end

  end

模型/ post.rb

# tags 
  acts_as_taggable # Alias for acts_as_taggable_on :tags

def next
    Post.where("id > ?", id).order(id: :asc).limit(1).first
end

def prev
     Post.where("id < ?", id).order(id: :desc).limit(1).first
end

show.html.erb

<%= link_to "← Previous Question", @post.prev, :class => 'button previous-question' %>

<%= link_to "Next Question →", @post.next, :class => 'button next-question' %>

的routes.rb

 # TAGS
  get 'tags/:tag', to: 'posts#index', as: :tag

4 个答案:

答案 0 :(得分:7)

我认为你将不得不传递tag参数(尽管你应该把它作为辅助方法)

模型/ post.rb

def next tag
  Post.where("id > ?", id).tagged_with(tag).order(id: :asc).limit(1).first
end

def prev tag
  Post.where("id < ?", id).tagged_with(tag).order(id: :desc).limit(1).first
end

显示

<%= link_to "← Previous Question", post_path(@post.prev(current_tag).id, tag: current_tag), :class => 'button previous-question' %>

<%= link_to "Next Question →", post_path(@post.next(current_tag).id, tag: current_tag), :class => 'button next-question' %>

控制器/ posts_controller.rb

class PostsController < ApplicationController
  helper_method :current_tag

  #def show
  #def index

  private

  def current_tag
    params[:tag]
  end
end

答案 1 :(得分:4)

您可以将它放在您的控制器中,然后Post.where(["id < ?", id]).last表示前一个,Post.where(["id > ?", id]).first表示下一个。

您要做的是控制器的工作。您可以根据排序扩展它们。

我还找到了this gem。对你来说会更好用。

答案 2 :(得分:4)

这是Sean的回答。它现在应该工作。当prevnext方法返回nil

时,会出现此问题

模型/ post.rb

def next tag
  result = Post.where("id > ?", id).tagged_with(tag).order(id: :asc).limit(1).first

  result || very_first(tag)
end

def prev tag
  result = Post.where("id < ?", id).tagged_with(tag).order(id: :desc).limit(1).first

  result || very_last(tag)
end

def very_first tag
  Post.tagged_with(tag).order(id: :asc).limit(1).first
end

def very_last tag
  Post.tagged_with(tag).order(id: :asc).limit(1).last
end

显示

<% if @post.prev(current_tag) %>
  <%= link_to "← Previous Question", post_path(@post.prev(current_tag).id, tag: current_tag), :class => 'button previous-question' %>
<% end %>

<% if @post.next(current_tag) %>
  <%= link_to "Next Question →", post_path(@post.next(current_tag).id, tag: current_tag), :class => 'button next-question' %>
<% end %>

控制器/ posts_controller.rb

class PostsController < ApplicationController
  helper_method :current_tag

  #def show
  #def index

  private

  def current_tag
    params[:tag]
  end
end

P.S。对不起肖恩,我无法对你的答案发表评论,所以我只是复制并修复了它

答案 3 :(得分:3)

您还可以采用嵌套资源方法并更改您的路线,如下所示:

resource :tag do
  resource :post
end

它应该为您提供路由结构,以便/tags/:tag_id/posts指向给定标记的所有帖子,/tags/:tag_id/posts/:id将指向标记有该标记的确切帖子(或问题?)。

然后在posts控制器中,你应该像这样添加before_filter :set_tag

before_filer :set_tag

def set_tag
  @tag = Tag.find(params[:tag_id])
end

索引操作看起来像这样

 def index
    @posts = @tag.posts
 end

并始终显示该标记的帖子。

在帖子控制器的show动作中,您可以获得下一个和上一个帖子链接,就像上面的答案一样。

您还应该更改视图中使用的所有帖子网址助手,以包含当前标记,例如posts_path - &gt; tag_posts_path(@tag)其中tag是在before_filter中设置的当前标记。

我强烈建议您不要将所有这些方法放在模型上,并为帖子创建一个presenter对象,例如

class PostPresenter
  attr_reader :post
  alias_method :current, :post

  def initialize(post)
    @post = post
    @repo = post.class
  end

  def next
    @repo.where('id > ?', post.id).first
  end

  def previous
    @repo.where('id < ?', post.id).first
  end
end

@presenter = PostPresenter.new(@post)

以及下一篇文章的链接

<%= link_to "Next Question →", 
      tag_post_path(@presenter.next), 
      class: 'button next-question' if @presenter.next.present? %>