【Rails】ブログに記事の下書き保存機能を実装する

2021年3月25日 23:03

はじめに

Railsアプリで作成したブログで記事を途中保存したいときがあります。保存はしたいのですが、書いている途中の記事を一般公開するわけにはいきません。記事を一般公開はせずに保存しておく下書き機能があると便利です。

本記事では、ブログの記事を下書き保存する機能を実装する方法について説明します。

下書き保存機能を実装

今回実装する下書き保存機能の概要は以下の通りです。

  • 記事投稿/編集画面で「下書き」または「公開中」を選択
  • 記事が「公開中」の場合、誰でも閲覧可能
  • 記事が「下書き」の場合、管理者のみ閲覧可能
    • 管理者以外が直接アクセスしたらログイン画面へ遷移
  • いつでも「下書き」⇔「公開中」を変更可能

モデルの実装

変数の追加

モデルに記事の状態を表す変数をEnum(列挙型)として追加します。Enum(列挙型)とは、モデルの数値カラムに対して文字列による名前定義をマッピングする機能です。詳しくは以下のページを参照してください。

モデルに以下を追記します。

  enum status: { draft: 0, published: 1 }
  validates :status, inclusion: { in: Article.statuses.keys }

statusをEnum(列挙型)として宣言することで、0/1という数値がそれぞれdraft/publishedという文字列にマッピングすることができます。

カラムの追加

データベーススキーマに記事の状態を保存しておくstatusカラムを追加します。以下のコマンドを実行して、マイグレーションファイルを作成します。

$ rails generate migration AddStatusToArticles status:integer

作成されたマイグレーションファイルYYYYMMDDHHMMSS_add_status_to_articles.rbを以下のように編集します。デフォルト値は0とし、NOT NULL制約を設定します。

class AddStatusToArticles < ActiveRecord::Migration[6.0]
  def change
    add_column :articles, :status, :integer, default: 0, null: false
  end
end

以下のコマンドを実行して、マイグレーションを実行します。

$ rails db:migrate

データベーススキーマに記事の状態を保存するstatusカラムが追加されました。

既存記事の更新

statusカラムを追加する前に作成した記事がある場合、statusカラムにはデフォルト値の0が設定されているので1に更新します。この手順はデプロイ後に本番データベースでも行う必要があります。

使用しているデータベースがSQLiteの場合、Railsコンソールから更新を行います。

$ rails console
> Article.update_all(status: "published")
> exit

使用しているデータベースがMySQLの場合、以下のクエリを実行します。

UPDATE articles SET status = 1;

クエリの実行にMySQL Workbenchを使っている場合、以下のエラーが出て更新できないことがあります。

Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnect.

MySQL Workbenchでは、セーフアップデートモードが有効になっていると全件更新(条件未指定)ができないようになっています。セーフアップデートモードを無効にするには以下の手順を行います。

メニューバーから [MySQLWorkbench] - [Preferences] をクリックします。設定ウインドウの左のメニューから「SQL Editor」をクリックし、下のほうにある「Safe Updates ...」というチェックボックスを外します。

サーバーに接続し直した後、再度クエリを実行します。

ビューの実装

フォームの実装

記事の投稿/編集フォームに記事の状態を選択するセレクトボックスを追加します。

<%= form.select :status, Article.statuses.map { |k, v| [t("article.status.#{k}"), k] }, selected: @article.status %>

Model.variablesでEnum(列挙型)の値をすべて取得することができます。mapメソッドでキーと値の組み合わせをマッピングし、それらをセレクトボックスの値として追加します。記事の編集時は現在の状態を初期値に設定する必要があるため、selected: @article.statusを記述します。

その他の実装

管理画面の記事一覧画面などで記事の状態を表示するには以下のように記述します。

<%= t("article.status.#{article.status}") %>

tメソッドはロケールファイルに記述されている多言語を呼び出すメソッドです。ロケールファイルの設定については次のセクションを参照してください。#{}を使った文字列と変数の結合を行うには、値をダブルクオーテーション("")でくくる必要があります(シングルクォーテーション('')は不可)。

ロケールファイルの設定

Railsアプリケーションを多言語対応するにはi18nというGemを使います。i18nはRails 2.2から標準同梱されているので、アプリケーション設定とロケールファイルを用意するだけで多言語対応ができます。

i18nのアプリケーション設定を行うには、config/application.rbに以下を追記します。

module ApplicationName
  class Application < Rails::Application
    # 以下を追記
    config.i18n.default_locale = :ja
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  end
end

アプリケーションのデフォルト言語を日本語(ja)に設定しています。これにより、ロケールファイルのja以下の設定がtメソッドで呼び出すことができるようになります。また、config/locales/ディレクトリ配下のすべてのファイルをロケールファイルとして設定しています。

新しくロケールファイルをconfig/locales/ディレクトリ配下に作成します。ファイル名は何でも構いません。新たにディレクトリを作成しても構いません。自分が管理しやすい構成で大丈夫です。

作成したロケールファイルに以下を記述します。

ja:
  article:
    status:
      draft: '下書き'
      published: '公開中'

ロケールファイルはビューの書き方とモデルの書き方があります。今回の設定対象はArticle.statusの値なのでモデルの書き方かと思いますが、モデルの書き方では日本語化できません。モデルの書き方はモデル名とカラム名の日本語化はできますが、その設定値までは日本語化できないので注意してください。

# モデルの書き方
ja:
  activerecord:
    models:
      article: '記事'
    attributes:
      article:
        title: '題名'
        content: '本文'
        status: '状態'
          draft: '下書き'       # この書き方は不可
          published: '公開中'   # この書き方は不可

ロケールファイルの詳しい書き方については以下の記事を参照してください。

コントローラーの実装

ストロングパラメーターの変更

ストロングパラメーターにstatusを追加します。

  private
    def article_params
      params.require(:article).permit(:title, :content, :status)
    end

記事の取得処理の変更

記事一覧画面などで取得している記事の取得処理を「公開中」記事に限定するには、以下のように変更します。nokogiriで使用するオプションや並べ替えのオプションなどはpublishedの後に記述していきます。

  def index
    @articles = Article.published
  end

サイドバーにある最新記事の取得処理や人気記事の取得処理なども同じように変更します。また、RSSフィード生成時の記事の取得処理(rss_controller.rb)やサイトマップの登録処理(config/sitemap.rb)も忘れずに変更してください。

アクセス権限の変更

記事一覧画面に「下書き」記事は表示されなくなりましたが、「下書き」記事のURLに直接アクセスされると誰でも見れてしまいます。「下書き」記事のアクセス権限を管理者に限定するには以下を追記します。

  def show
    @article = Article.find(params[:id])
    # 以下を追記
    require_login if @article.draft?
  end

取得した記事が「下書き」の場合require_login(ログイン済みかを確認するメソッド)を呼び出します。require_loginメソッドの内容は以下の通りです。logged_inメソッドはセッションのヘルパーメソッドです。

  private
    def require_login
      redirect_to login_path if !logged_in?
    end

まとめ

記事中にも書きましたが、今回追加したstatusカラムはデフォルト値が0のため、本番環境にデプロイすると元々あった記事がすべて「下書き」になってしまいます。デプロイ後に最後の仕上げとして本番データベースの更新を忘れないようにしてください。

本記事を参考にして、ブログに記事の下書き保存機能を実装していただければと思います。

関連記事

【Rails】Webサーバー「Unicorn」の基本情報と実装方法
# はじめに Railsアプリを本番環境で稼働させるには、クライアントからのリクエストを捌くWebサーバーを導入する必要があります。WebサーバーはクライアントからのリクエストをRailsアプリに伝達し、Railsアプリで処理されたレスポンスをク [...]
2021年4月15日 12:17
【Rails】デプロイツール「Capistrano」の基本情報と実装方法
# はじめに アプリを本番環境にアップロードして誰でもアクセスできる状態にすることをデプロイと言います。デプロイで行うべきことは多岐にわたります。Railsアプリの場合で言えば、本番環境にアップロードすることはもちろんですが、Gemのインストール [...]
2021年4月14日 9:56
【Rails】Webpackerの基本情報と実装方法
# はじめに Rails 6からWebpackerが正式採用されました。Rails 5ではオプションで追加することができたWebpackerですが、Rails 6からは普通にアプリを作成するだけでWebpackerがインストールされ、必要な設定も [...]
2021年4月12日 14:36
【Rails】アセットパイプライン(Sprockets)の基本情報と実装方法
# はじめに Ruby on Railsにはアセットパイプラインという機能があります。アセットパイプラインは画像、CSS、JavaScriptといったアセットファイルを連結/圧縮することでRailsアプリを高速化します。また、より高級な言語で書か [...]
2021年4月11日 14:08
【Rails】Turbolinksの基本情報と実装方法
# はじめに Ruby on Railsにはページの遷移を高速化するTurbolinksという機能があります。Turbolinksは優れたJavaScriptライブラリですが、Rails 5からは標準で有効化されているため、Turbolinksを [...]
2021年4月10日 17:59