Create Post and Comment in Ruby on Rails
Post
Generate Files for Post.
$ rails g scaffold Post
Ruby will create several files:
create db/migrate/20211010101809_create_posts.rb create app/models/post.rb invoke resource_route route resources :posts invoke scaffold_controller create app/controllers/posts_controller.rb create app/views/posts create app/views/posts/index.html.erb create app/views/posts/edit.html.erb create app/views/posts/show.html.erb create app/views/posts/new.html.erb create app/views/posts/_form.html.erb
Add fields to Post by editing the newly created migration file.
class CreatePosts < ActiveRecord::Migration[6.1] def change create_table :posts do |t| t.string :title t.text :text t.timestamps end end end
Create Post table by running:
$ rails db:migrate
Terminal output:
== 20211010101809 CreatePosts: migrating ====================================== -- create_table(:posts) -> 0.0032s == 20211010101809 CreatePosts: migrated (0.0040s) =============================
This means that the Post table was successfully created.
Update app/views/posts/_form.html.erb.
<%= form_with(model: post) do |form| %> <% if post.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2> <ul> <% post.errors.each do |error| %> <li><%= error.full_message %></li> <% end %> </ul> </div> <% end %> <%= form.label :title, "Title:" %> <%= form.text_field :title %> <%= form.label :text, "Text:" %> <%= form.text_area :text %> <div class="actions"> <%= form.submit %> </div> <% end %>
This form will be used to create or update posts.
Go to app/controllers/posts_controller.rb. Add fields to the strong params to the controller for it to accept values from the app/views/posts/_form.html.erb. This is done for added security.
class PostsController < ApplicationController #--------- # Other actions #--------- def post_params params.require(:post).permit(:title, :text) end end
We're almost done with the post. Now we're just gonna edit the views to properly show the data saved in the post. Edit app/views/posts/index.html.erb.
<p id="notice"><%= notice %></p> <h1>Posts</h1> <table> <thead> <tr> <th colspan="3"></th> </tr> </thead> <tbody> <% @posts.each do |post| %> <tr> <td><%= post.title %></td> <td><%= link_to 'Show', post %></td> <td><%= link_to 'Edit', edit_post_path(post) %></td> <td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table> <br> <%= link_to 'New Post', new_post_path %>
This will show the post's title in "localhost:3000/posts".
<! -- app/views/posts/show.html.erb -- > <p id="notice"><%= notice %></p> <%= @post.title %> <br> <%= @post.text %> <br> <%= link_to 'Edit', edit_post_path(@post) %> | <%= link_to 'Back', posts_path %>
This will show post title and text values at the "localhost:3000/posts/[id]".
We're done at the first half. Now we'll begin at Comment.
Comment
Generate files for Comment.
$ rails g scaffold Comment
Ruby will create some files:
create db/migrate/20211017054528_create_comments.rb create app/models/comment.rb invoke scaffold_controller create app/controllers/comments_controller.rb invoke erb create app/views/comments create app/views/comments/index.html.erb create app/views/comments/edit.html.erb create app/views/comments/show.html.erb create app/views/comments/new.html.erb create app/views/comments/_form.html.erb
Comments are usually rendered under Post so we will be using only _form.html.erb.
Add fields to Comment by editing the newly created migration file.
class CreateComments < ActiveRecord::Migration[6.1] def change create_table :comments do |t| t.integer :post_id t.text :text t.timestamps end end end
If you noticed, I added the post_id field to the comment. This will be used as a reference to the Post table.
Create the Comment table by running:
$ rails db:migrate
Terminal output:
== 20211017054528 CreateComments: migrating =================================== -- create_table(:comments) -> 0.0215s == 20211017054528 CreateComments: migrated (0.0218s) ==========================
This means that the Comment table was successfully created.
Now we will be adding the relationship between Post and Comment.
#models/post.rb class Post < ApplicationRecord has_many :comments end #models/comment.rb class Post < ApplicationRecord belongs_to :post end
Update app/views/comments/_form.html.erb.
<%= form_with(model: comment) do |form| %> <% if comment.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(comment.errors.count, "error") %> prohibited this comment from being saved: </h2> <ul> <% comment.errors.each do |error| %> <li><%= error.full_message %></li> <% end %> </ul> </div> <% end %> <%= form.hidden_field :post_id %> <%= form.label :text, "Text:" %> <%= form.text_area :text %> <div class="actions"> <%= form.submit %> </div> <% end %>
We're getting the comment form to be added under the post.
Update strong params to accept values from comment form at the comments_controller.rb.
class CommentsController < ApplicationController #--------- # Other actions #--------- def comment_params params.require(:comment).permit(:post_id, :text) end end
Add comment form in app/views/posts/_form.html.erb.
<p id="notice"><%= notice %></p> <%= @post.title %> <br> <%= @post.text %> <br> <%= render "comments/form", comment: @comment%> <%= link_to 'Edit', edit_post_path(@post) %> | <%= link_to 'Back', posts_path %>
This may not work yet. We need to
@comment
variable used for the comment form.Update post_controller.rb
class PostsController < ApplicationController #--------- # Other actions #--------- def show @comment = @post.comments.build end #--------- # Other actions #--------- end
This will create
@comment
each time you show a post. The comment form under the post should work now but if the form has been submitted, the page redirects to "localhost:3000/comments/[id]" instead of "localhost:3000/posts/[id]".To redirect to "localhost:3000/posts/[id] after submitting a comment we have to update comments_controller.rb.
class CommentsController < ApplicationController #--------- # Other actions #--------- def create @comment = Comment.new(comment_params) respond_to do |format| if @comment.save format.html { redirect_to @comment.post, notice: "Comment was successfully created." } else format.html { render :new, status: :unprocessable_entity } end end end #--------- # Other actions #--------- end
We're almost done. Now we only have to render the comments under the post. Edit app/views/posts/show.html.erb.
<p id="notice"><%= notice %></p> <%= @post.title %> <br> <%= @post.text %> <br> <b>Comments</b> <br> <%- @post.comments.each do |comment|%> <%= comment.text%> <br> <% end %> <%= render "comments/form", comment: @comment%> <%= link_to 'Edit', edit_post_path(@post) %> | <%= link_to 'Back', posts_path %>
To check posts go to 'localhost:3000/posts'
That's it we're done ๐
If you want to see the code, you could check here: Github repository
Update 01/30/2022
I created a new blog post that adds real-time loading of comments for this, which can be seen here: Ruby on Rails - Comment real-time loading