はじめに
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
のため、本番環境にデプロイすると元々あった記事がすべて「下書き」になってしまいます。デプロイ後に最後の仕上げとして本番データベースの更新を忘れないようにしてください。
本記事を参考にして、ブログに記事の下書き保存機能を実装していただければと思います。