はじめに
Ruby on Rails
を手っ取り早く学習したいと思ってToDoアプリを作りました。
作っていく中で個人的にハマったところなどを忘れないうちにメモっておきたいと思います。
ちなみにこの記事は fukuokarb
中に書きました。
初参加でしたが、今後とも継続して参加していきたいと思っております。
成果物
こんなの作りました。
どこからどう見ても CRUD
のお勉強をするのに最適とされるToDoリストアプリですね。
構築
開発環境の構築手順についてはこちら をご覧ください。
データベース準備
この辺はご多分に漏れずMigrationファイル作成→Migrate実行という流れになります。
事前に rake db:create
を行ってデータベースの生成を行っておきましょう。
# Migrationファイル生成
rails g migration create_tasks
# モデル生成
rails g model task
なお、Railsの規約によるとモデルは単数形、テーブルは複数形とのこと。
モデルは new
などの処理で単数を扱うから、テーブルは複数のレコードを持つからそれぞれそういう区分けになっているみたいです。
この辺はLaravelも一緒ですね。
続いてMigrationファイルを修正していきます。
class CreateTasks < ActiveRecord::Migration[5.2]
def change
create_table :tasks do |t|
# 設定できるもの
# string : 文字列
# text : 長い文字列
# integer : 整数
# float : 浮動小数
# decimal : 倍精度小数
# datetime : 日時
# timestamp : タイムスタンプ
# timestamps : created_at, updated_at
# time : 時間
# date : 日付
# binary : バイナリデータ
# boolean : Boolean
t.text :state
t.text :task
t.date :limit_date
t.timestamps
end
end
end
続いてテストデータの作成。
手で書いてもいいけど… Seeder
から流したほうが楽ですよね。
@task = Task.new
@task.task = 'task1'
@task.state = 'todo'
@task.limit_date = '2018-10-10'
@task.save
最後にMigrate実行、Seedingを行います。
rake db:migrate
rake db:seed
# ミスった場合は次のコマンドを投入
rake db:rollback
コントローラー生成
次にコントローラーを生成してきます。
ちなみにコントローラーは複数のリソースを一つのファイルで扱うので複数形。
# コントローラーだけを生成する場合
rails g controller tasks
# メソッドも含めてスケルトンを生成する場合
rails g controller tasks index show store update destroy
ルート修正
続いてコントローラーを修正していきます。
RESTfulに書きたかったので。
Rails.application.routes.draw do
get 'tasks' => 'tasks#index'
post 'tasks' => 'tasks#store'
get 'tasks/:id' => 'tasks#show'
put 'tasks/:id' => 'tasks#update'
delete 'tasks/:id' => 'tasks#destroy'
# この書き方でもOKとのこと
# get '/tasks', to: 'tasks#index'
end
間違いなくもっと効率的なルートの記述方法があるので、また調べて追記します。
追記
こちら にいい感じの記事がありました。
サーバーの起動
ここでサーバーを起動してみます。
なお、バックエンドの修正を行った場合、毎回止めてサーバーの再起動をしないといけない…?
bin/rails server -b 0.0.0.0 -p 2000
Viewファイル修正
それぞれのファイルを修正していきますが、細かい部分については下の「各種機能の実装」に記載します。
ただ少し注意点が。
View内コード実行
<% @tasks.each do |t| %>
<p><%= t.task %></p>
<% end %>
フォームデータを送る場合
Laravelで言うところの csrf_token
的なものを記載しておかないと怒られます。
Railsでは authenticity_token
と呼ばれているようですね。
これは form_authenticity_token
という関数で生成できるみたいです(参考)。
次のように記述しましょう。
<form method="POST" action="/tasks">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
</form>
参考:Laravelの場合
<form method="POST" action="/tasks">
{{ crsf_fileld() }}
</form>
PUT(PATCH), DELETEをする場合
これまたLaravelで言うところのヘルパ関数、 method_field('method')
的なものを用意しておかないと全て POST(GET)
で送られてしまいます。
Railsでも _method
という隠し要素を送ることで問題なくパスできます。
<form method="POST" action="/tasks">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
</form>
参考:Laravelの場合
<form method="POST" action="/tasks">
{{ crsf_fileld() }}
{{ method_field('PUT') }}
</form>
各種機能の実装
タスク一覧取得(GET)
まずはindexページに飛ばします。
スクショに貼り付けている画面ですね。
ルーティングは /tasks
、 コントローラーメソッドは index
。
Controller実装
class TasksController < ApplicationController
def index
@tasks = Task.order('limit_date').all
@status = ['todo', 'doing', 'done']
end
# ...
end
特に難しいことはやっていませんが、期限の近いものから順に並べたデータを取得しています。
細かい検索などをやりたければ こちら の記事が参考になるかと。
ちなみに @tasks
としているのは、インスタンス変数扱いにしてViewに引き渡すためのようです(参考)。
Laravelだと return view('tasks.index', ['tasks' => $tasks])
と書いていました。
記述を簡略化できる半面、少し慣れるまでに時間がかかりそうという印象。
View実装
<table>
<thead>
<tr>
<th>State</th>
<th>Limit</th>
<th>Task</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<% @tasks.each do |t| %>
<tr>
<td><%= t.state %></td>
<td><%= t.limit_date %></td>
<td><%= t.task %></td>
<td>
<!-- 次のタスク詳細取得画面へのリンク -->
<a href="/tasks/<%= t.id %>">edit</a>
</td>
<td>
<!-- まだ実装しない -->
<button type="submit">delete</button>
</td>
</tr>
<% end %>
</tbody>
</table>
こちらも特に難しいことはやっていません。
デザイン部分は全て削ってあります。
DELETE
部分はあとで追記します。
タスク作成(POST)
app/views/tasks/index.erb
に少し追記。
先頭にタスク作成フォームを追します。
View修正
<form method="POST" action="/tasks">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
<div>
<label for="task">Task Name</label>
<input id="task" name="task" type="text" required>
</div>
<div>
<select name="state">
<% @status.each_with_index do |s,i| %>
<option value="<%= s %>"><%= s %></option>
<% end %>
</select>
<label>Task State</label>
</div>
<div>
<input type="date" name="limit_date" required>
<label>Task Limit Day</label>
</div>
<p>
<button type="submit">create</button>
</p>
</div>
</form>
<!-- flashメッセージを表示 -->
<% flash.each do |msg| %>
<p><%= msg %></p>
<% end %>
<table>...</table>
Controller実装
class TasksController < ApplicationController
# ...
def store
task = Task.new
task.task = params[:task]
task.state = params[:state]
task.limit_date = params[:limit_date]
task.save
redirect_to '/tasks', notice: 'タスクを作成しました。'
end
# ...
end
redirect_to
でindexページに差し戻しています(参考)。
差し戻す際に notice: 'message'
として flash
に情報を残しておくと一回こっきりで表示されるメッセージ(例:ユーザーを登録しました)をViewに引き渡せます(参考)。
タスク詳細取得(GET)
index.erb
からのリンクで入ってきます。
ルーティングは /tasks/:id
、コントローラーメソッドは show
。
Controller実装
class TasksController < ApplicationController
# ...
def show
id = params[:id]
@task = Task.find(id)
@status = ['todo', 'doing', 'done']
end
# ...
end
こちらも何も難しいことはしておりません。
indexページから引き渡ってきたパラメータ(params
)を使っているくらい。
View実装
<form method="POST" action="/tasks/<%= @task.id %>">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
<div>
<label for="task">Task Name</label>
<input value="<%= @task.task %>" id="task" name="task" type="text" required>
</div>
<div>
<select name="state">
<% @status.each_with_index do |s,i| %>
<option value="<%= s %>"><%= s %></option>
<% end %>
</select>
<label>Task State</label>
</div>
<div>
<input type="date" name="limit_date" required>
<label>Task Limit Day</label>
</div>
<p>
<button type="submit">update</button>
</p>
</form>
次のこともあるので、先にフォームだけ生成しておきます。
トークンとメソッドの上書きを忘れないこと。
タスク更新(PUT)
フォームは先に生成しているので省略。
こちらも特に難しいことはありません。
ルーティングは /tasks/:id
、 コントローラーメソッドは update
。
Controller実装
class TasksController < ApplicationController
# ...
def update
id = params[:id]
task = Task.find(1)
task.task = params[:task]
task.state = params[:state]
task.limit_date = params[:limit_date]
task.save
redirect_to '/tasks', notice: 'タスクを更新しました。'
end
# ...
end
ちなみにModelにアクセスするメソッドについては こちら を参考にしました。
タスク削除(DELETE)
はい、最後に削除処理です。
ルーティングは /tasks/:id
、 コントローラーメソッドはdestroy
。
View修正
<table>
...
<tbody>
<% @tasks.each do |t| %>
<tr>
<td><%= t.state %></td>
<td><%= t.limit_date %></td>
<td><%= t.task %></td>
<td>
<!-- 次のタスク詳細取得画面へのリンク -->
<a href="/tasks/<%= t.id %>">edit</a>
</td>
<td>
<!-- ここを修正する -->
<form method="POST" action="/tasks/<%= t.id %>">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
<button type="submit">delete</button>
</form>
</td>
</tr>
<% end %>
</tbody>
</table>
隠しメソッドを DELETE
で送るためだけのフォームを実装。
Controller実装
class TasksController < ApplicationController
# ...
def destroy
task = Task.find(params[:id])
task.destroy
redirect_to '/tasks', notice: 'タスクを削除しました。'
end
# ...
end
特に悩むことなし。
終わりに
ね?簡単でしょ?
全ソースコードは こちら に置いています。
(そういやログイン機能は未実装だった)
— ♡まおちゃ♡ (@mao_sum) 2018年10月10日
今後は これ をやっていき