I have some Articles, each with a 'show' page. I am making it so each Article has some comments on the show page as well. I have a 'create' action for Comments, but I need to add a 'redirect' to the 'create' action of the Comments Controller.
I want the redirect to go to the show page of the Article to which the newly created Comment belongs.
How would I write this redirect_to statement?
Here's what I have so far:
def create
#comment = Comment.new(comment_params)
if #comment.save
#article = ?________?
redirect_to #article
end
end
I appreciate your help because I have been befuddled by this concept for ages and really look forward to getting past this hump.
Provided your relationships are set up properly and your routes are defined properly, this is an extremely simple task.
Your models should be (at minimum):
class Article < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :article
end
This ensures the proper helper methods on each model. Now define your routes so comments belong to an article:
// somewhere in routes.rb
resources :articles do
resources :comments
end
This will give you nested routes, most notable the desired create route:
POST /articles/:article_id/comments
Which is where you direct your comment creations, your create will now look like:
// Comments controller
def create
#article = Article.find(params[:article_id]) # Probably should verify this gets something
if #comment = #article.comments.create(comment_params)
redirect_to article_path(#article)
else
render :new
end
end
And that should solve you issue. (If there are any syntax issues or other confusions please let me know - this was pulled all from memory).
Related
I've recently been working with controllers and routing. Typically when I write my new and create routes, they are:
get '/pages/new' => 'pages#new', as: :new_page
post '/pages/ => 'pages#create'
and the controller actions are:
def new
#page = Page.new
end
def create
#page = Page.new(page_params)
if #page.save
flash[:notice] = "Successfully created page."
redirect_to page_path(#page)
else
render action: 'new'
end
end
private
def page_params
params.require(:page).permit(:book_id, :text, :page_number)
end
So that works for new_page_path and post_pages_path.
But what if did new_page_path(book_id: #book.id)? (Also a page belongs to a book). What does this mean and how does that change my new and create methods? Also, if I were to create a new page, how would I change the count of the number of pages in my book?
I don't know how your models look like, but i suppose that you've got association: has_many.
If so, you might want to use nested resources in routes https://guides.rubyonrails.org/routing.html#nested-resources
resources :books do
resources :pages
end
that gives you always:book_id in params for pages actions. You can easily create a page associated with the book than by:
#book = Book.find params[:book_id]
#book.pages.build(pages_params)
And you don't really need to handle increasing count on create, if you properly set association. book.pages.count will tell you the truth.
The problem is i want to have both nested resources and normal resources pointing the the same action in controller but act differently based on whether the nesting resource in available or not.
Routes.rb
resources :users do
resources :comments //For having nested routes
end
resources :comments //For having normal routes
Nested Resource
For example
Class CommentsController < ApplicationController
def index
#user = User.find(params[:id])
#comment = #user.comments.find(params[:id])
end
Now,
If i want to just look up all the comments , i want to use the
/comments
Else if i am in the user page and click on the all comments i get to
/user/:user_id/comments
Hence, how to configure to make sure it generates the right page.
If I were you, I would do like this:
def index
#comment = Comment.find_by(user_id: params[:user_id], id: params[:id])
# It looked weird that you tried to find a #comment
# instead of #comments in action `index`
# I think you made a typing mistake and this can be help
#comments = Comment.where(user_id: params[:user_id])
end
So, I created app/services folder and then some classes inside with .call methods(I am trying to understand the logic of the services and query objects)
app/services/add_comment_to_post.rb
class AddCommentToPost
def initialize(post:, comment:)
#post = post
#comment = comment
end
def call
#post.comments.create(#comment)
#post
end
end
app/services/remove_comment_from_class.rb
class RemoveCommentFromPost
def initialize(post:, comment:)
#post = post
#comment = comment
end
def call
#post.comments.#comment.id.destroy
#post
end
end
and in the comments_controller.rb
def create
#this one works:
##post.comments.create! comment_params
AddCommentToPost.new(#post, #comment).call
redirect_to #post
def destroy
RemoveCommentFromPost.new(#post,#comment).call
redirect_to #post
Can anybody tell me what should I change to make it works, or where to look for similar examples? The posts and comments are scaffolded, and I use nested routes.
Rails.application.routes.draw do
resources :posts do
resources :comments
end
root "posts#index"
end
In general, it's helpful if you include the error you're getting from what you've tried. In this case, I've scanned the code and noticed a couple of errors that you should correct.
Your service objects define their initialize method to take keyword arguments like so:
def initialize(post:, comment:)
#post = post
#comment = comment
end
But you are initializing them by passing positional arguments like so:
AddCommentToPost.new(#post, #comment).call
You should initialize them with the expected keyword arguments like so:
AddCommentToPost.new(post: #post, comment: #comment).call
Additionally, as pasted above, your destroy method:
def destroy
RemoveCommentFromPost.new(#post,#comment).call
redirect_to #post
is missing an end.
Finally, you'll still want to check the return value of those service object calls to determine if the call succeeded or failed and handle appropriately. You are currently redirecting regardless.
resources :recipes do
resource :like, module: :recipes
resources :comments, only: [:new, :create, :show, :index], module: :recipes
end
recipe_comments GET /recipes/:recipe_id/comments(.:format) recipes/comments#index
POST /recipes/:recipe_id/comments(.:format) recipes/comments#create
Comments are in /recipes/:id
Recipe Controller
def show
#recipe = Recipe.find(params[:id])
#comment = #recipe.comments.new
#clean_recipe = Sanitizer.new(#recipe)
end
Recipes::CommentsController
Theres a before action that finds recipe.
def create
#comment = #recipe.comments.new(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to recipes_path
end
end
I've done
form_for([#article,#comment]) and form_for [#article,Comment.new]
and still the comment isn't persisted. I am wondering because of the module namespace, do I have to do something different?
Add logger.info 'I am inside create action in comments controller' inside the create action. If you see this logger message in the rails console, then the create action gets called. It means your routes are defined correctly, your form is using the right form helper.
the correct format is form_for([#recipe, #comment]) however, I had a length validation in my comment model which was failing to pass. I set too_short and too_long messages, however they didn't show up, which further confused me
I have a scenario being:
resources :magazines do
resources :articles do
resources :comments
end
end
So as to avoid nesting more than 2 levels deep I have re-factored this to be:
resources :magazines do
resources :articles
end
resources :articles do
resources :comments
end
My article show action URL is:
/magazines/3/articles/11
In this view I have a form for creating a new comment.
When a comment is saved successfully the form redirects which all works well.
When the form submission is not successful I wish to redisplay the view with validations errors displayed. I understand the correct way to do this is to render the 'articles/show' view. This also works and the view is redisplayed with the validation errors shown.
The problem is when the save fails and articles/show is rendered the URL is no longer correct and is shown as:
/articles/11/comments
class ArticlesController < ApplicationController
def show
#article = Article.find(params[:id])
#comments = #article.comments.order(created_at: :asc).page(params[:page]).per_page(5)
#comment = Comment.new
end
end
class CommentsController < ApplicationController
def create
#article = Article.find(params[:id])
#comment = #article.comments.new(discussion_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to #article
else
render 'articles/show'
end
end
private
def discussion_params
params.require(:comment).permit(:content)
end
end
I solved this by changing my routes back to the way it originally was and now the article show action includes the magazine in the url.
I understand this breaks the "no more than 2 levels deep" routing rule but it's the only way I can get it to work.