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

はじめに

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】Paranoiaを使用した論理削除(ソフトデリート)
# はじめに Paranoiaは、Railsアプリケーションで論理削除(ソフトデリート)を実現するためのGemです。 論理削除は、データベースのレコードを物理的に削除するのではなく、削除フラグを設定することで「削除済み」とみなす方法です。こ [...]
2024年7月20日 21:33
【Rails】activerecord-multi-tenantを使用したマルチテナントアプリケーションの作成
# はじめに マルチテナントアプリケーションでは、複数の顧客(テナント)が同じアプリケーションを利用するため、データの分離が必要です。 activerecord-multi-tenantは、このようなマルチテナント環境をサポートするための便 [...]
2024年7月18日 16:50
【Rails】RubyとRailsにおけるattr_reader, attr_writer, attr_accessorの概念と使用方法
# はじめに RubyとRailsの開発において、`attr_reader`,`attr_writer`,`attr_accessor`は非常に便利なメソッドです。これらは、クラス内でインスタンス変数に対するゲッターおよびセッターメソッドを簡単に [...]
2024年7月17日 18:11
【Rails】RubyとRailsにおけるyieldの概念と使用方法
# はじめに RubyとRailsにおける`yield`は、メソッドやテンプレートの中で動的にコードブロックを実行する能力を提供し、これによってコードの再利用性と拡張性が大幅に向上します。本記事では、RubyとRailsにおける`yield`の概 [...]
2024年7月17日 13:15
【Rails】AASMを使用してオブジェクトの状態遷移を効率的に管理
# はじめに Railsアプリケーションにおいて、オブジェクトの状態管理は重要な課題の一つです。AASM (Acts As State Machine) gemは、複雑な状態遷移を効率的に管理します。本記事では、AASMの基本的な使い方を解説して [...]
2024年7月16日 18:00
【Rails】RSpec + Swagger + rswagでアプリケーションのAPIをテストおよびドキュメント化する方法
# はじめに Railsアプリケーションの開発において、APIのテストとドキュメント化は重要な要素です。 RSpecはテストフレームワークとして広く利用されており、SwaggerはAPIの設計とドキュメント化を支援します。これらを統合するr [...]
2024年7月16日 14:27
【Rails】mailcatcherを使用して開発環境でメール送信をテストする方法
# はじめに mailcatcherは、開発環境でのメール送信をキャプチャするためのツールです。ローカルで送信されたメールをブラウザ上で簡単に確認できるようにします。mailcatcherをRailsアプリケーションで使用する方法について説明しま [...]
2024年7月15日 16:37
【Rails】impressionistを使用してページビューやクリック数を追跡する方法
# はじめに impressionist Gemを使用してRailsアプリケーションでページビューやクリック数を追跡する方法について説明します。 # 実装方法 ## impressionist Gemのインストール まず、impre [...]
2024年7月15日 14:18