Create Post and Comment in Ruby on Rails

ยท

5 min read

Post

  1. 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
    
  2. 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
    
  3. 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.

  4. 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.

  5. 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
    
  6. 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

  1. 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.

  2. 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.

  3. 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.

  4. 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
    
  5. 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.

  6. 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
    
  7. 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.

  8. 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]".

  9. 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
    
  10. 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

ย