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

17