Create Post and Comment in Ruby on Rails
Tags: rails, ruby, beginners, webdev
Published: 2021-10-24 12:25:50

## Post
**Generate Files for Post.**
   ```console
   $ rails g scaffold Post
   ```

   Ruby will create several files:

   ```console
   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.**

   ```ruby
   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:**

   ```console
   $ rails db:migrate
   ```

   Terminal output:
   ```console
   == 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.**

   ```html
   <%= 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.

   ```ruby
   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.**

   ```html
   <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".

   ```html
   <! -- 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.**

 ```console
 $ rails g scaffold Comment
 ```

 Ruby will create some files:

 ```console
 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.**

 ```ruby
 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:**

 ```console
 $ rails db:migrate
 ```

 Terminal output:
 ```console
 == 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.**

 ```ruby
 #models/post.rb
 class Post < ApplicationRecord
  has_many :comments
 end

 #models/comment.rb

 class Comment < ApplicationRecord
  belongs_to :post
 end
 ```

**Update app/views/comments/_form.html.erb.**

 ```html
  <%= 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.**

 ```ruby
 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.**

 ```html
 <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**

 ```ruby
 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.**

 ```ruby
 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.**

 ```html
 <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](https://github.com/jkevinbaluyot/PostAndComment)

---

### Update 1/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](https://dev.to/jkevinbaluyot/ruby-on-rails-comment-realtime-loading-2lin)

### Update 6/18/2022

Updated based on the comment