🤖

[Rails]下書き保存機能

2023/03/10に公開

投稿機能に、下書き保存機能を実装していきます。
投稿時に投稿するのではなく、一時保存する機能です。
投稿時に投稿するか下書きするかを選べるようにし、下書き一覧ページを作成していきます。

<実装環境>
Ruby:3
Rails6
devise使用

<実装手順>

  1. postテーブルにstatusカラムを追加
  2. statusをenumで設定
  3. posts/new画面で下書きするを選択できるようにする
  4. コントローラ設定
  5. 下書きページ作成

下書き機能実装

大まかにこのような感じにしていきます!

  • 下書き保存ボタン押す => 下書き一覧リストへいく
  • 下書き一覧ページの下書きtitleリンクを押す => editページへ
  • editページにも、投稿ボタンと下書き保存ボタン 
     => 下書き保存ボタンは投稿済み投稿編集の場合は表示させない

<routing>※post部分のみ記述
下書き保存機能は"draft"で設定。

scope module: :public do
 :
 :
    resources :posts do
      collection do
        get "drafts"
      end
 end

postテーブルにstatusカラムを追加

  • 投稿時に投稿するか下書きするかを選べるようにする
    => 公開statusの設定で選択できるようにする!!
    = postテーブルに公開statusの設定が必要!!!今回はenumで設定していきます。
<migration file>
class CreatePosts < ActiveRecord::Migration[6.1]
  def change
    create_table :posts do |t|
      t.integer :user_id, null: false
      t.integer :post_status, null: false, default: 0
      t.string :title, null: false
      t.text :body, null: false
      t.timestamps
    end
  end
end

できたらrails db:migrate忘れずに!

enumの設定

enumについてと設定方法:詳しくはここに記述しています!
ので、少々省略いたします。今回必要なもののみ記述します
<enum設定>

アクション 名前 番号
投稿 published 0
下書き保存 draft 1
<post.rb>
class Post < ApplicationRecord
:
:
 enum post_status: { published: 0, draft: 1 }
			      

posts/new画面で下書きするを選択できるようにする

<div class="form-group">
    <%= f.submit '投稿する', name: 'commit' ,class: "btn btn-success"%>
    <%= f.submit '下書き保存', name: 'commit',class: "btn btn-primary" %>
</div>
  • ここで記述した name: 'commit'に関してはのちに説明しますが、
    簡単に説明を書くと :commitパラメーターは、送信されたボタンの名前を示し、どのボタンが押されたのか判断するために使用している。
  • f.submitを使って送信ボタンを複数設置した場合、:commitパラメーターはそれぞれのボタンの名前を表すために使用される。

edit部分のview

<div class="form-group">
          <%= f.submit '投稿する', name: 'commit' ,class: "btn btn-success"%>
          <% if @isDraft %>
            <%= f.submit '下書き保存', name: 'commit' ,class: "btn btn-primary"%>
          <% end %>
        </div>
  • ここではnewとほぼ同じではあるが、少々違う点が。"下書き保存ボタン"はeditページでは、
    投稿済みのものには表示しないような設定
    が必要。
  • コントローラーの部分でも載せるが edit部分でこのように定義する。

@post = Post.find(params[:id])
@isDraft = @post.draft?

2行目のコードで、@isDraftにdraft(下書き保存)なのか?を埋め込みます。

? は、Rubyの慣習的な命名規則で、メソッドが真偽値を返すことを示すために使用される。

これを利用して、edit viewにて使用し、
もし下書きだったら? を <% if @isDraft %>として記述している!!

drafts view

<div class="container">
  <h1 class="my-4">下書き一覧</h1>
  <div class="row">
    <div class="col-md-6">
      <% @draft_posts.each do |draft| %>
        <div class="card mb-3">
          <div class="card-body">
            <h5 class="card-title">
              <%= link_to draft.title, edit_post_path(draft) %>
            </h5>
          </div>
        </div>
      <% end %>
    </div>
  </div>
</div>
  • ここのviewに関してはindex作成するときとほぼ一緒です!!
  • ただ、ここには下書きのもののみ掲載することをコントローラーで記述が必要です。

postモデルに追記

投稿を下書き状態で保存するためのメソッドを作っておく

def save_draft
    self.post_status = :draft
    save(validate: false)
end
  • self.post_status = :draft :投稿statusがdraft(下書き保存)だった時
  • save(validate: false) :バリデーションをスキップして保存

=> 下書き状態で保存する場合は必要な情報が揃っていないことがあるため

controllerの記述

記述が少々多いのですが、先に必要な部分を全て記述します。
したで大事な部分を記述していきます。

<post controller>

class Public::PostsController < ApplicationController
  def new
    @post = Post.new
  end

  def create
    if params[:commit] == "下書き保存"
      @post = Post.create(post_params.merge(user_id: current_user.id))
      if @post.save_draft  
        redirect_to drafts_posts_path
      else
        render :new
      end
    else
      @post = Post.create(post_params.merge(user_id: current_user.id))
      if @post.save
        redirect_to post_path(@post)
      else
        render :new
      end
    end
  end

  def drafts
    @published_posts = Post.where(user_id: current_user.id).where(post_status: :published).order(created_at: :desc)
    @draft_posts = Post.where(user_id: current_user.id).where(post_status: :draft).order(created_at: :desc)
  end
						     
:
省略
:
						     
  def edit
    @post = Post.find(params[:id])
    @isDraft = @post.draft?
  end

  def update
    @post = Post.find(params[:id])

    if @post.update(post_params)
      if params[:commit] == "下書き保存"
        @post.update(post_status: :draft)
        redirect_to posts_path, notice: "下書きを保存しました。"
      else
        @post.update(post_status: :published)
        redirect_to @post, notice: "投稿を更新しました。"
      end
    else
      render :edit
    end
  end

:
省略
:
  private

  def post_params
    params.require(:post).permit(:title, :body, :post_status)
  end
end
    

create部分

フォームから送信されたパラメータ(post_params)を元に、Postモデルのインスタンスを作成する.

  • フォームの送信ボタンが「下書き保存」であった場合、作成された投稿は下書き状態で保存
  • そうでない場合は、公開状態で保存
    上記2点を条件分岐で記述している。
  • if @post.save_draft  
    ここはmodelで定義したものを使用し「下書き保存」であった場合、作成された投稿は下書き状態で保存ということ。
  • .mergeは、post_paramsとuser_idを結合し、ハッシュを作成するために使用。
    これにより、フォームから送信されたデータに加えて、投稿の作成者の情報も含めることができる。
    => Post.createメソッドを使用して、ユーザーIDを含む投稿を作成.

mergeメソッド
2つのActiveRecord::Relationオブジェクトをマージして1つの新しいオブジェクトを作成するメソッド

draftsメソッド

  • ユーザーが作成した公開された投稿と,下書きされた投稿
    @published_postsと,@draft_postsに格納
    公開状態と下書き状態を区別し,whereメソッドを使用して、user_idとpost_statusに基づいて、
    それぞれの投稿を取得するようにしている。

edit

ここは上記のedit viewのところで説明済み○

update

  • ここでの記述として、createの時と同じく、
    下書き保存であった場合と投稿だった場合を条件分岐しています。

:commitについて

:commitRailsアプリケーションでフォームを送信する際に使用される、
ボタンの名前または値を表すハッシュキー。

  • f.submitを使って送信ボタンを複数設置した場合、:commitパラメーターはそれぞれのボタンの名前を表すために使用される。

今回、"公開(published)"と"下書き保存(draft)"の2つのボタンがある。
view部分で、
<%= f.submit '投稿する', name: 'commit' ,class: "btn btn-success"%>
<%= f.submit '下書き保存', name: 'commit',class: "btn btn-primary" %>
というようにname: 'commit'と記述しキーを与えた。
これらのボタンが押された場合、送信されるパラメータの:commitキーには、それぞれ"公開する"と"下書き保存"という値が設定されるようになる。
コントローラー側で、params[:commit]を使用し、どのボタンが押されたかを判断することができるようになる。


これで一通り完成です。

Discussion