はじめに
RUNTEQでの学習の一環として、日本株の投資記録を残せるミニアプリを作っています。
前回までの記事で、マイグレーションとバリデーションについて書いてきましたが、今回は「CRUD」を実際に実装してみて学んだことをまとめてみようと思います。
正直、CRUDという言葉自体は知っていたのですが、「new と create って何が違うの?」「edit と update は別々に用意する意味あるの?」みたいなところがふわっとしたまま進めていたので、自分の頭の整理も兼ねて書いていきます。
作っているアプリについて
日本株の売買記録(銘柄名・株数・価格・売買日・売買理由)を残せる、投資記録アプリを作っています。
「なぜこの株を買ったんだっけ?」を後から振り返れないという自分の悩みを解決するために作り始めました。
CRUDって結局何なのか
CRUDとは、以下の4つの頭文字みたいです。
- Create(作る)
- Read(読む)
- Update(更新する)
- Delete(消す)
Railsではこれを、コントローラーの7つのアクション(index, show, new, create, edit, update, destroy)で表現するのが基本形になっているようです。
最初、この7つ全部いる?と思ったのですが、実際に手を動かしてみると役割がちゃんと分かれていて納得感がありました。
new と create の違いでつまずいた話
一番最初に「あれ?」となったのがここでした。
def new
@trade = current_user.trades.build
end
def create
@trade = current_user.trades.build(trade_params)
if @trade.save
redirect_to @trade, notice: "取引記録を登録しました"
else
render :new, status: :unprocessable_entity
end
end
最初は「同じようなことしてるのに何で2つに分かれてるんだろう」と思っていたのですが、書きながら理解が深まりました。
-
newは、空っぽの入力フォームを表示するだけの係 -
createは、フォームが送信された後に、実際に保存する係
newの方はデータベースに何も保存しません。ただ画面に空のフォームを出すためだけの処理みたいです。実際に保存が走るのはcreateが呼ばれたときだけ、という役割分担になっていることが分かりました。
editとupdateの関係も全く同じ考え方で、editが表示担当、updateが保存担当という構造になっていました。
自分のデータしか見れないようにする工夫
これは実装していて「なるほど」と思った部分です。
def show
@trade = current_user.trades.find(params[:id])
end
最初、Trade.find(params[:id])と書いてしまいそうになったのですが、これだとURLの数字を変えるだけで他人の取引記録が見えてしまう可能性があるとのことでした。
current_user.trades.find(params[:id])と書くことで、「今ログインしている人の記録の中からだけ探す」という絞り込みになるみたいです。これはUserモデル側にhas_many :tradesがあることで成り立っている書き方らしく、アソシエーションの大事さを実感しました。
ルーティングも整理した
# 変更前
resources :trades, only: [:index]
# 変更後
resources :trades
only: [:index]を外すだけで、一覧・詳細・新規登録・保存・編集・更新・削除の7種類のURLが一気に使えるようになりました。地味ですが、この1行の意味がちゃんと分かると気持ちいいなと思いました。
フォームを共通化してみた
newとeditって見た目がほぼ同じフォームだったので、_form.html.erbという部品として切り出して、両方から呼び出す形にしました。
<%= form_with model: @trade, local: true do |f| %>
<%= f.label :stock_name, "銘柄名" %>
<%= f.text_field :stock_name %>
<%= f.label :trade_type, "売買区分" %>
<%= f.select :trade_type, Trade.trade_types.keys.map { |t| [t == "buy" ? "買い" : "売り", t] } %>
<%= f.submit %>
<% end %>
form_with model: @tradeと書くと、@tradeが新規なのか既存のレコードなのかをRailsが自動で判断して、送信先(新規登録用か更新用か)を勝手に切り替えてくれるみたいです。これのおかげで、同じ_form.html.erbをnewとeditの両方から使い回せています。
バリデーションエラーの表示も忘れずに
<% if @trade.errors.any? %>
<ul>
<% @trade.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
保存に失敗したときにエラーメッセージが表示される仕組みも、_form.html.erbの中に組み込みました。空欄のまま登録しようとすると「銘柄名を入力してください」のようなメッセージが出るようになって、地味に感動しました。
つまずいたポイントまとめ
- new/createの役割分担が最初分からなかった → 表示担当と保存担当、と覚えたら腑に落ちた
-
Trade.findとcurrent_user.trades.findの違いを意識していなかった → セキュリティに直結する部分だと知って重要さを実感 - フォームの共通化(
_form.html.erb)は最初「別々に書けばいいのでは」と思っていたが、修正が1箇所で済むメリットに後から気づいた
おわりに
CRUDという単語だけ知っていても、実際に手を動かして「なぜ7つのアクションに分かれているのか」を体感すると理解が全然違うなと感じました。
もし間違っている理解があれば、ぜひコメントで指摘していただけると嬉しいです。