Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 5 previous or next post ONLY from certain tag

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
like image 316
mazing Avatar asked Oct 13 '17 12:10

mazing


3 Answers

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
like image 69
Sean Avatar answered Nov 04 '22 20:11

Sean


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

like image 34
Peter Balaban Avatar answered Nov 04 '22 21:11

Peter Balaban


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.

like image 4
arjun Avatar answered Nov 04 '22 19:11

arjun