はじめに
Railsの基礎を一通り学び終わったのですが、自分自身理解が追いついてないところもあると思うので、記事を作成する中でアウトプットし、理解を深めていくことが目的です。
概要
今回はタイトルにもあるとおり、簡単なメッセージ投稿アプリを解説も交えながら作成していきます。
アプリの作成からCRUD処理でメッセージ投稿機能の実装、それからリファクタリング、フラッシュやバリデーションの設定、エラーの処理、表示、最後にBootstrapでスタイルを付けるという感じで進めていきます。
なので、今回は4章に分けて記事を作成します。
開発手順
- アプリの作成 (第1章)
- CRUD処理で簡単なメッセージ投稿アプリを実装 (第1章)
- リファクタリング (第2章)
- フラッシュの実装 (第2章)
- バリデーションの設定 (第2章)
- エラーの処理 (第3章)
- Bootstrapでスタイルをつける (第4章)
開発環境
- Ruby 2.7.3
- Rails 6.1.6.1
- Postgresql
1. アプリの作成
rails new post_message_app -d postgresql -T
cd post_message_app
rails db:create
- データベースは
-d postgresql
でPostgreSQLを指定- データーベースを指定した場合は rails db:create が必要になります。
- -T で MiniTest のディレクトリを作成しないように設定
モデルとテーブルの作成
メッセージ投稿アプリなので、メッセージのタイトル(title)と内容(content)を投稿できるよう2つのカラムを用意し、モデルを作成します。
※ モデル名は必ず 単数形!
rails g model Post title:string content:text
上記コマンドによって2つのファイルが作成されます。
- app/models/post.rb (モデル)
- db/migrate/作成日_create_posts.rb (マイグレーションファイル)
この時点で Post モデルは作成されたが、テーブルは作成されていません。
ターミナルでrails db:migrate
コマンドを実行することにより、先ほど作成されたマイグレーションファイルが実行され、データベースに posts テーブルが作成される。
マイグレーション実行後は、db/schema.rb で作成したテーブルの中身を確認できます。
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
コントローラの作成
下記コマンドでコントローラとビューファイルまでまとめて作成。
※ コントローラ名は必ず複数形!
rails g controller posts new create index show edit update destroy
今回のアプリでは下記3つのファイルは使用しないので、この時点で削除しておきます。
- app/views/posts/create.html.erb
- app/views/posts/destroy.html.erb
- app/views/posts/update.html.erb
ここまでで、モデル、テーブル、コントローラ、ビューファイルなど初期設定が完了です。
2. CRUD処理
今回は簡単にRESTフルなアプリを作成するため、resources を使用しルーティングを設定。
※ RESフルとは、(Representational State Transfer)「ネットワーク上にあるリソース(データ)に対して、作成、表示、更新、削除などの操作を行う」というアプリケーションを作成する上で必要な概念となります。
RESTの機能を担当するのがルーティングであり、resources を使用することで、簡単にRESTフルなアプリを作成することが可能となります。
Rails.application.routes.draw do
root "posts#index"
resources :posts
end
root "posts#index"
を追記することにより、トップページを投稿一覧ページに設定します。
リンクの作成
全てのページで 投稿一覧ページ、新規投稿ページのリンクが表示されるように application.html.erb に追記。
<!-- 略 -->
<body>
<%= link_to "投稿一覧", posts_path %> <%= link_to "新規投稿", new_post_path %>
<hr>
<%= yield %>
</body>
</html>
※ Prefix を使用したパス名は下記コマンドで確認可能です。
rails routes | grep posts
新規投稿機能( new, create )
ルーティングは resources で設定しているので、コントローラから作成します。
class PostsController < ApplicationController
def new
@post = Post.new
end
def create
post = Post.create!(post_params)
redirect_to post_path(post)
end
# 略
private
def post_params
params.require(:post).permit(:title, :content)
end
end
createアクションでは変数に @ はつけず、また新規投稿後は、投稿詳細ページにリダイレクトするように実装します。
※ @ を付けると付けないの違いについては、付けたらその変数はアクションと紐づくビューファイルで使用できるが、付けなかった場合は、ビューファイルで使用できません。
今回 create.html.erb 使用しないので、@ は付けないようにしています。
private には Strong Parameter (post_params)を記述し、データベースに予期せぬ値が登録されないよう title と content のみに制限。
次に form_with ヘルパーメソッドを使用しビューファイルで新規投稿ページを作成します。
<h1>新規投稿</h1>
<%= form_with model: @post, local: true do |form| %>
<div>
<%= form.label :title, "タイトル" %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :content, "内容" %>
<%= form.text_field :content %>
</div>
<div>
<%= form.submit "送信" %>
</div>
<% end %>
form_with でモデルオブジェクト(@post)が posts テーブルに存在するかを判定し、存在しなければ createアクション、存在すれば updateアクションに処理を自動的に振り分ける役割を持ちます。
詳細(show)
まずはコントローラの処理から記述します。
class PostsController < ApplicationController
# 略
def show
@post = Post.find(params[:id])
end
# 略
end
findメソッドで, [:id] に対応する投稿をデータベースから取り出し、@post に代入。
※ params[:id] はshowアクションを実行した際の URL posts/:id と対応しています。
投稿詳細ページのビューファイルは下記の通り記述し、投稿したタイトルと内容を表示するようにします。
<h1>投稿詳細</h1>
<p>タイトル <%= @post.title %></p>
<p>内容 <%= @post.content %></p>
これで新規投稿ページで投稿し、詳細ページにリダイレクトされるところまでの流れが出来上がりです。
この記事を見ながら同じように作成している方がいるのであれば、ここで一旦サーバーを起動し、新規投稿ページにアクセスし、投稿ができ、詳細ページにリダイレクトされるか確認することをお勧めします。
URL:http://localhost:3000/posts/new
一覧表示(index)
投稿詳細ページでは、タイトルと内容が表示されるので、一覧ページではタイトルのみ表示する形とします。
まずはコントローラから
class PostsController < ApplicationController
# 略
def index
@posts = Post.order(:id :asc)
end
# 略
end
Post.order を使用した目的としては、Post.all だと編集した場合、順番が変わってしまうので、Post.order を使用し、id順で取得するようにします。
<h1>投稿一覧</h1>
<table>
<thead>
<tr>
<th scope="col">No.</th>
<th scope="col">タイトル</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<% @posts.each.with_index(1) do |post, i| %>
<tr>
<th scope="row"><%= i %></th>
<td><%= post.title %></td>
<td>
<%= link_to "詳細", post_path(post) %>
<%= link_to "編集", edit_post_path(post) %>
<%= link_to "削除", post, method: :delete, data: { confirm: "削除しますか?" } %>
</td>
</tr>
<% end %>
</tbody>
</table>
今回は詳細・編集・削除のリンクが縦に並ぶように、table ダグを使用。
each メソッドを使い、タイトルのみ繰り返し処理を行います。
ここで注意したいのが削除のリンクで、
link_to を使用した場合、指定がない限りHTTPメソッドは GET が使用されます。
しかし削除の場合は、DELETE なので指定してあげる必要があります。
また data: { confirm: "削除しますか?" } を記述することで、削除ボタンをクリックした際にダイアログを表示することができます。
削除機能(destroy)
先ほどの投稿一覧ページで削除のリンクは作成済みのため、コントローラの設定のみ行います。
class PostsController < ApplicationController
# 略
def destroy
post = Post.find(params[:id])
post.destroy!
redirect_to root_path
end
end
ビューファイルは削除済みなので、ここでも変数は @ は付けないようにします。
編集機能(edit, update)
この編集機能ができれば、CRUD処理は完成です。
長くなり、疲れも溜まってきたところですが、最後がんばりましょう!
まずはコントローラから
class PostsController < ApplicationController
# 略
def edit
@post = Post.find(params[:id])
end
def update
post = Post.find(params[:id])
post.update!(post_params)
redirect_to post_path(post)
end
# 略
end
編集機能も削除機能と似た感じで、投稿データの id を飛ばして、コントローラで受け取り、update メソッドを実行し、投稿詳細ページにリダイレクトする形とします。
<h1>編集</h1>
<%= form_with model: @post, local: true do |form| %>
<div>
<%= form.label :title, "タイトル" %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :content, "内容" %>
<%= form.text_field :content %>
</div>
<div>
<%= form.submit "送信" %>
</div>
<% end %>
ここは新規投稿機能(new.html.erb)とほぼ同じです。
form_with でデータベースに存在するかを判定し、今回は存在するので、updateアクションに自動的に振り分けてくれます。
これで完成となるので、サーバーを起動し、一通りの操作が可能か確認してみましょう。
次回は今回作成したCRUD処理で実装した部分を、リファクタリングし、フラッシュメッセージの設定、バリデーションの追加と進めていきます!
お疲れ様でした!