DEV Community

John Kevin Baluyot
John Kevin Baluyot

Posted on

Create Post and Comment in Ruby on Rails

Post

Generate Files for Post.

   $ rails g scaffold Post
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Create Post table by running:

   $ rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Terminal output:

   == 20211010101809 CreatePosts: migrating ======================================
   -- create_table(:posts)
      -> 0.0032s
   == 20211010101809 CreatePosts: migrated (0.0040s) =============================
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Terminal output:

 == 20211017054528 CreateComments: migrating ===================================
 -- create_table(:comments)
    -> 0.0215s
 == 20211017054528 CreateComments: migrated (0.0218s) ==========================
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

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

Discussion (0)