I have a resource named posts, of which there are many. However, each post can have multiple tags. I want users to be able to go to the previous post and next post, ONLY from the tag that was selected. I have it working for all posts from the database in previous next, but when I click on a tag and it shows all the tags, prev/next doesn't adhere to what the tag is.
If I visit the url in association with the code defined in routes.rb, get 'tags/:tag', to: 'posts#index', as: :tag
, it'll list all the tags in an index. I don't want this, I want a user to be able to click previous or next and only do so on posts that are associated with a tag.
Note: I am using the friendly_id gem
controllers/posts_controller.rb
def index @posts = Post.all if params[:tag] @posts = Post.tagged_with(params[:tag]) else @posts = Post.all end end
models/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 Answers
Answers 1
I think you are going to have to pass around that tag
parameter (although you should probably make it a helper method)
models/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
show
<%= 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' %>
controllers/posts_controller.rb
class PostsController < ApplicationController helper_method :current_tag #def show #def index private def current_tag params[:tag] end end
Answers 2
You can put this in your controller then, Post.where(["id < ?", id]).last
for previous and Post.where(["id > ?", id]).first
this for next.
What you are trying to do is the job of the controller. You can extend those based on your sorting.
I also found this gem. Would be much better for you to use.
Answers 3
Here is updated Sean's answer. It should work now. The problem appears when prev
or next
methods returning nil
models/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
show
<% 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 %>
controllers/posts_controller.rb
class PostsController < ApplicationController helper_method :current_tag #def show #def index private def current_tag params[:tag] end end
P.S. sorry Sean, I can't comment on your answer, so I just copied and fixed it
Answers 4
You can also take nested resources approach and change your routes like this:
resource :tag do resource :post end
It should give you routes structure so that /tags/:tag_id/posts
will be pointing to all the posts for a given tag, and /tags/:tag_id/posts/:id
will be pointing to exact post (or question?) tagged with that tag.
Then in posts controller, you should add before_filter :set_tag
like this
before_filer :set_tag def set_tag @tag = Tag.find(params[:tag_id]) end
index action would look like this
def index @posts = @tag.posts end
and will always show posts for that tag.
In the show action of posts controller you can get next and previous post links just like in the answers above.
You should also change all the post url helpers used in views to include current tag, e.g. posts_path
-> tag_posts_path(@tag)
where tag is current tag that was set in before_filter.
I highly recommend you not putting all those methods on model and create a presenter object for a post, e.g.
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
and
@presenter = PostPresenter.new(@post)
and a next post's link
<%= link_to "Next Question →", tag_post_path(@presenter.next), class: 'button next-question' if @presenter.next.present? %>
0 comments:
Post a Comment