一週間のカリキュラム 1週間で、Ruby 〜 Ruby on Rails までを学ぶ
シリーズの3日目、データを読み書きする、CRUD について見ていきます。
3日目の内容
- Ruby on Rails の基礎を抑える。
CRUD は全てのアプリケーションの基本
CRUD とは
- Create
- Read
- Update
- Delete
の略称です。
ブログのような機能を考えるのならば
- 記事の作成、更新、削除
- 記事のリスト表示(+検索表示)、記事単体の表示
は必須の機能です。 ほとんどのフレームワークでは、この機能をなるべく少ない工程で実装できるように機能が作られています。
必要な準備
すでに記事全体を表示する機能は、前回作成してありますので、そこからの続きとなります。
まずはテンプレートは無視して URLのrouteと、それに対応するコントローラーのみを作成します。
./config/route.rb
Rails.application.routes.draw do
#get 'articles/index'
root "articles#index"
resources :articles
end
./app/controllers/articles_controller.rb にも大量ですが、下記のようなメソッドを追加しましょう。
class ArticlesController < ApplicationController
def index
@articles = Article.all()
end
#ここから追加
def show
@article = Article.find(params[:id])
end
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render :new, status: :unprocessable_entity
end
end
def edit
@article = Article.find(params[:id])
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render :edit, status: :unprocessable_entity
end
end
def destroy
@article = Article.find(params[:id])
@article.destroy
redirect_to root_path, status: :see_other
end
private
def article_params
params.require(:article).permit(:title, :body)
end
end
ここまで入力して下記のようにURLを確認してみましょう。
http://localhost:3000/
前回(http://localhost:3000/articles/index)で表示された内容と同じ内容が表示されると思います。 これはroute(一番上の階層)にarticle#indexコントローラーを追加した結果です。
root "articles#index" #この部分
では残りの
resources :articles
の部分はどんなコントローラーと紐付いてるのでしょうか? どのようなURLが作成されたかは以下のコマンドで確認します。
$ bin/rails routes
Prefix Verb URI Pattern Controller#Action
root GET / articles#index
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
色々なURLが、どのコントローラーメソッドと紐付いてるかが確認できると思います。
resourcesで追加された コントローラー & テンプレートを一つづつ確認、設定していく。
もう一度 resources :articlesの部分を確認します。
$ bin/rails routes
Prefix Verb URI Pattern Controller#Action
#0 リスト部分
root GET / articles#index
#1 単独記事を表示する。
articles GET /articles(.:format) articles#index
#2 新規作成保存
POST /articles(.:format) articles#create
#3 新規作成の開始
new_article GET /articles/new(.:format) articles#new
#4 再編集の開始
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
#5 アップデート処理
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
#6 削除
DELETE /articles/:id(.:format) articles#destroy
1 単独記事を表示する。
articles GET /articles(.:format) articles#index
対となるコントローラー
def show
@article = Article.find(params[:id])
end
これは /articles/1 などとアクセスされた場合を指します。
対応するテンプレートを設置しましょう。
./app/views/articles/show.html.erb
<h1><%= @article.title %></h1>
<%= @article.body %>
これで http://127.0.0.1:3000/articles/1 に記事1のタイトルと内容が表示されました。
このままでは、記事単独ページへのリンクがありませんので、リストページにリンクを追加します。
./app/views/articles/index.html.erb
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<a href="<%= article_path(article) %>">
<%= article.title %>
</a>
</li>
<% end %>
</ul>
※モデルやコントローラーがRailsの指定する命名規則に沿っていれば article_path(article)でリンク先URLが出力されます。
また、上記のテンプレートは link_to メソッドを利用することで下記のようにも表現できます。
<h1>Articles</h1>
<ul>
<% @articles.each do |article| %>
<li>
<%= link_to article.title, article %>
</li>
<% end %>
</ul>
表記として()が省略された形です。 link_to(文字列,モデル名) ↓ link_to 文字列,モデル名
2 新規作成保存
POST /articles(.:format) articles#create
対応するコントローラーは
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render :new, status: :unprocessable_entity
end
end
この部分に関してはテンプレートがありません。
次に紹介する 3新規作成テンプレートから保存をした時に対応するPOST先となってます。
3 新規作成の開始
new_article GET /articles/new(.:format) articles#new
テンプレートは ./app/views/articles/new.html.erb
<h1>New Article</h1>
<%= form_with model: @article do |form| %>
<div>
<%= form.label :title %><br>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :body %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
これで http://127.0.0.1:3000/articles/new
を開くと新しい記事を追加できるようになります。
しかしこのままだとデータが全て空でも保存が出来てしまいます。 モデルにタイトルを必須にして、記事内容も最低10文字以上いれないと保存できないように 設定します。
app/models/article.rb
class Article < ApplicationRecord
validates :title, presence: true
validates :body, presence: true, length: { minimum: 10 }
end
また、エラーメッセージを出せるよう ./app/views/articles/new.html.erb に下記を追加します。
<h1>New Article</h1>
<%= form_with model: @article do |form| %>
<div>
<%= form.label :title %><br>
<%= form.text_field :title %>
<%# この部分を追加 %>
<% @article.errors.full_messages_for(:title).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :body %><br>
<%= form.text_area :body %>
<%# この部分を追加 %>
<% @article.errors.full_messages_for(:body).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
最後に新規作成ページの導線をリストページに追加しましょう
<%= link_to "New Article", new_article_path %>
4 再編集の開始
次に一度作成した記事の編集です。
edit_article GET /articles/:id/edit(.:format) articles#edit
コントローラー部分
def edit
@article = Article.find(params[:id])
end
ほぼshowと同じです。 テンプレートですが、newと内容は同じで大丈夫です。 ただ、同じ処理を2箇所にのせるのも非効率なので、共通部分を renderメソッドで、読み込むようにします。
new.htm.erb をコピーして _form.html.erbを作る
$ cp new.html.erb _form.html.erb
new.html.erb edit.html.erb ともに下記の内容にしましょう。
<%= render "form", article: @article %>
これで編集用のテンプレートも準備できました。 編集用のテンプレートへの導線を作る前に削除部分も確認します。
6 削除
DELETE /articles/:id(.:format) articles#destroy
削除のURLもPOST専用でテンプレートは必要ありません。 先ほどのUPDATE用の導線も含め show.edit.erb部分に導線を作ります。
show.html.erb
<h1><%= @article.title %></h1>
<p><%= @article.body %></p>
<ul>
<%# 編集用導線 %>
<li><%= link_to "Edit", edit_article_path(@article) %></li>
<%# 削除用導線 %>
<li><%= link_to "Destroy", article_path(@article), data: {
turbo_method: :delete,
turbo_confirm: "Are you sure?"
} %></li>
</ul>
CRUD機能の後は・・・
今回は基本中の基本を抑えました。
今回実装してない機能として
- ページング
- 検索
- 他のデータとのリレーション(記事にコメントをつけるとか) などがあります。