Ruby on Rails でToDoアプリを作ろう(3)
概要
この章では、前回作成したToDoアプリに新しく
- 新規登録機能
- 編集機能
- 削除機能
を実装していくことによって、さらにRailsの扱いについて理解を深めていきます。
新規投稿機能の実装
いままでは、タスクを追加するときに毎回rails console
から直接データベースにタスクを追加する必要がありましたが、ここではブラウザ上からでもデータベースにタスクを追加できるような新規投稿機能を実装しようと思います。
トップページにリンクを作成
まずは、トップページ(top.html.eerb)から新規投稿画面へ飛べるようにリンクを作成します。
以下の通りにトップページを編集してください。
<% @tasks.each do |task| %>
<% if task[:priority] == "high" %>
<span class="redbox">
<%= task[:text] %>
<span class="deadline">
<%= task[:deadline] %>
</span>
</span>
<% else %>
<span class="graybox">
<%= task[:text] %>
<span class="deadline">
<%= task[:deadline] %>
</span>
</span>
<% end %>
<% end %>
<!--新規投稿ページへのリンク-->
<span class="toplink">
<%= link_to "新規投稿ページ", "/tasks/new"%>
</span>
ここでは、トップページに新たに新規投稿ページへのリンクを作成しました。
Railsで画面遷移を行うときはこのような書き方をします。
#リンク先URLを指定する場合
<%= link_to <表示される>, <リンク先URL> }%>
#直接実行するメソッドを指定する場合
<%= link_to <表示される>, {:controller => <コントローラー名>, :action => <メソッド名> } %>
link_to
メソッドを使って記述を行うと、HTMLコードが読み込まれる際にaタグに変換されるので、サイトを表示した際にはaタグと同様に、リンクとして表示されます。
今回の場合だと、以下のようなHTMLコードになります。
<a href="/tasks/new">新規投稿ページ</a>
表示されているリンクをクリックすると、指定のURLにアクセスします。 今回の場合だと、新規投稿ページというリンクをクリックすると、localhost:3000/tasks/newにアクセスするという意味になります。
ルート、コントローラー、ビューの作成
トップ画面にリンクを作成したら、早速リンク先(tasks/new)に対応するルートとコントローラーとビューを作成しましょう。
以下のコマンドを実行してtasksコントローラーとnewメソッドを作成してください。 (同時に、対応するルートとビューも生成されます)
$ rails g controller tasks new
新しくtasksコントローラーとnewメソッドが出来ました。
tasksコントローラーは表示用のhomeコントローラーとは別に、taskを保存するためのコントローラーとして使用します。
ここまでを実装すると、 新規投稿ページのリンクをクリックし、tasksコントローラーのnewメソッドを実行し、newビューを表示するところまで実装できました。
それではいよいよタスクを追加するためのnewビューを編集していきます。
新規投稿画面の編集
今の状態でhttp://localhost/tasks/newを見ても、まだ何も編集をしていないので何も表示されないかと思います。
それでは早速新規投稿用のビューを編集していきましょう。
newビューを以下のように編集してください。
<%= form_tag('/tasks/create') do %>
<textarea name="text" rows="4" cols="40">ここにTODOを記入してください</textarea>
<input name="deadline" type="date">
<input type="checkbox" name="priority" value="high">high
<input type="submit" value="送信する">
<% end %>
ここではform_tagを使いデータを送信する処理を書きます。 form_tagは以下のように定義することができます。
<%= form_tag(<送信先URL>) do %>
<!--受け渡すデータ-->
<% end %>
今回の場合だと、タスクの内容を書くtextと期限を書くdeadline、優先度を決めるpriorityといった3つの値が、<input type="submit" value="送信する">
をクリックすると、tasks/createに向けて送信されます。
編集が終わったら、早速newビュー(http://localhost/tasks/new)にアクセスをして実際に「送信する」ボタンをクリックしてみましょう。
この時点で送信するボタンを押してもtasks/createに対応するのルートが定義されていないので、エラー画面が出てきてしまいます。
それでは対応するルートと、コントローラーを作成していきましょう。
リクエストに対応するルートの作成
まずは、tasks/createに対応するメソッドを定義するためルーターを以下のように編集してください。
Rails.application.routes.draw do
get 'tasks/new'
get 'home/top'
post 'tasks/create'
end
ここではgetではなくpostをHTTPメソッドとして使います。 なぜならcreateの処理は、ブラウザで受け取ったデータ(text、deadline、priority)をデータベースに送信するからです。
データベースを操作するような処理はgetでは無くpostを使用するので注意してください。
タスクを保存するためのメソッドを追加
ルーターの設定も終え、「送信ボタン」を押した際に、tasksコントローラーのcreateメソッドを実行させるところまで完了しました。
それでは最後にcreateメソッドの中身を定義して、ブラウザから受け取ったデータをデータベースに対して保存できるようにしていきましょう。
createメソッドを以下のように定義してください。
class TasksController < ApplicationController
def new
#htmlを返すだけので、何も書かない
end
def create
@task = Task.new(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
@task.save
redirect_to controller: 'home', action: 'top'
end
end
ここでは、ブラウザからルーターを経由して受け取ったデータをデータベースに入力しています。
新しい手法がいくつか出てきたので順を追って説明します。
まずはこちらの部分です。
@task = Task.new(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
@task.save
こちらは前回rails console
を使ってデータベースにデータを追加したように Task.new
とsave
を使って、データをデータベースのそれぞれのカラムに追加しています。
params
はブラウザから渡ってきたデータを取り出すのに使われています。 [:text]
,[:deadline]
,[:priority]
といった部分は、どれもname属性を表しています。 (ビューのファーム部分でそれぞれにname属性をつけていたのを思い出してください)
つまりここの場所は
(text:ビューから渡ってきた[:text], deadline:ビューから渡ってきた[:deadline] , priority:ビューから渡ってきた[:priority])
といった意味になり、それぞれの値をそれぞれのカラムにTask.new
で追加していることになります。
redirect_to controller: 'home', action: 'top'
次にここでは、ridirect_to
を使って指定したコントローラーのメソッドを実行させています。
今回指定した処理はhomeコントローラーのtopメソッドなので、ここではデータベースに新しい値を追加したあと、topビューへのリダイレクトとされるという意味になります。
それでは、実際に新規投稿画面からデータベースに新しいタスクを追加することができるか確認してみてください。
バリデーションをつける
ここまでで、新規投稿画面から新しいタスクをデータベースに追加してトップ画面に戻る処理までを完了しましたが、実はこのままだとフォームの値が空白でも追加されてしまいます。
そこでここではバリデーションを設けたいと思います。
バリデーションとは検証という意味で、ユーザーが予期せぬ入力をしていないかなどを検証することができ、場合に応じて処理を無効化することができます。
バリデーションは、モデルに関するファイルに以下のように定義します。
validates: <検証したいデータ>. <検証したい処理>
それでは、app/models/task.rb
を以下のように編集してください。
class Task < ApplicationRecord
validates :text, presence: true
end
今回はタスクの内容であるtextが空だと困るので、 text部分にバリデーションをかけ、presence: true
を使ってtextのデータが存在しない場合はデータベースにデータが保存されないようにしました。
presence以外にもバリデーションに使える処理はたくさんありますが、かけたい処理に対してその都度検索できれば問題ありません。
それでは実際にtextの値を空白にしてタスクを追加しようとするとデータベースにデータが追加されないか確認してみてください。
正しく実行することができれば新規投稿機能の完成です!
更新機能の作成
次は投稿の詳細画面を作成し、それぞれのタスクの内容を更新できる機能をつけていきます。
トップページに詳細ページ用のリンクを作成
まずは詳細画面へののリンク作成から始めます。
トップページを以下のように編集してください。
<% @tasks.each do |task| %>
<% if task[:priority] == "high" %>
<span class="redbox">
<%= task[:text] %>
<%= task[:deadline] %>
<%= link_to "詳細", "/tasks/#{task.id}" %>
</span>
<% else %>
<span class="graybox">
<%= task[:text] %>
<%= task[:deadline] %>
<%= link_to "詳細", "/tasks/#{task.id}" %>
</span>
<% end %>
<% end %>
<span class="toplink">
<%= link_to "新規投稿ページ", "/tasks/new"%>
</span>
ここでは#{task.id}
を使用して、そのタスクのidを取得し、リンク先URLとして使用しています。 こういった処理をすることで、それぞれのタスクのidの値をルーターやコントローラーに渡すことができ、タスクのidごとに別々の結果を表示させることがあとあと可能になります。
ルート、コントローラー、ビューの作成
それでは、tasks/task.idに対応したルート、コントローラー、ビューを順に作成していきます。
まずはルートを作成するため、以下のようにルーターを編集してください。
Rails.application.routes.draw do
get 'tasks/new'
get 'home/top'
get 'tasks/:id' => 'tasks#show'
post 'tasks/create'
end
tasks/:id
とリクエストを記述することで、それぞれのタスクごとに1や2といった別々のidがリクエストされても対応することでできます。
今回は、すべてtasksコントローラーのshowメソッドを実行するように設定しました。
こうしてルーター上で受け取ったデータ(タスクのid)をコントローラーで受け取る事が出来ます。
次はshowメソッドの作成です。
コントローラーを以下のように編集してください。
class TasksController < ApplicationController
def new
end
def show
@task = Task.find_by(id: params[:id])
end
def create
@task = Task.new(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
@task.save
redirect_to controller: 'home', action: 'top'
end
end
ここでは、find_by
を使って、データベースから指定したタスクのidを検索し、結果を@taskに格納しています。
find_by
は以下のように定義することができます。
モデル名.find_by(<検索したいカラム>:<検索したい値>)
今回は、まずparamsを使ってコントローラー上でデータを受け取り、 データベースのidカラムのうち、取得したタスクのidと一致するタスクを検索しています。
現在のままだと、まだshowビューが存在していないため、 詳細画面用のshowビューを以下の場所(app/views/tasks/show.html.erb
)に作成してください。
詳細画面の編集
最後にshowビューを以下のように編集してください。
<%= link_to "<戻る", {:controller => "home", :action => "top" } %>
<%= @task[:text] %>
<%= @task[:deadline] %>
<% if @task[:priority] == "high" %>
<span>priority : high </span>
<% end %>
<%= link_to "編集する", "/tasks/#{@task.id}/edit" %>
ここでは、取得したタスク(@task)の各値を表示し、さらに編集ページへのリンクを作成しています。
link_to
のリクエストURLを”/tasks/#{@task.id}/edit”とすることで、idの違うタスクの場合でも同じリクエストをルーターに送ることを可能にしています。
これで詳細ページが出来ました。
編集画面のルート、コントローラー、ビューの作成
まずは、/tasks/#{@task.id}/edit
に対応する編集画面へのルートを作成していきましょう。
Rails.application.routes.draw do
get 'tasks/new'
get 'home/top'
get 'tasks/:id' => 'tasks#show'
get 'tasks/:id/edit' => 'tasks#edit'
post 'tasks/create'
end
showメソッドのときと同様に、ビューからの{@task.id}
というリクエストは:id
として記述します。
次はコントローラーの編集をして、editメソッドを追加します。
class TasksController < ApplicationController
def new
end
def create
@task = Task.new(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
@task.save
redirect_to :action => "top"
end
def show
@task = Task.find_by(id: params[:id])
end
def edit
@task = Task.find_by(id: params[:id])
end
end
showメソッドと同様、find_by
とparams
を使用して、idの一致するタスクの情報取得し、@taskに格納しています。
このままだと、editメソッドに対応するビューがまだ存在しないため、 編集画面用のeditビューを以下の場所(app/views/tasks/edit.html.erb)に作成してください。
編集画面の編集
それでは、編集画面を編集していきます。
編集画面を以下のように編集してください。
<%= form_tag("/tasks/#{@task.id}/update")do %>
<input name="deadline" type="date" value="#{@task[:deadline]}" >
<% if @task[:priority] == "high" %>
<input type="checkbox" name="priority" value="high" checked="checked" >high
<% else %>
<input type="checkbox" name="priority" value="high" >high
<% end %>
<textarea name="text" rows="4" cols="40"><%= @task[:text]%></textarea>
<input type="submit" value="更新する">
<% end %>
今回は投稿画面と同様form_tag
を使い、フォームのリクエスト先を/tasks/update
に指定しました。
また、deadlineの値をvalue="#{@task[:deadline]}"
、priorityの値をif文、textの値を<%= @task[:text]%>
を使用することで取得したタスク(@task)の各データを入力しています。
これで「更新する」を押すと、フォームに入力したデータを/tasks/update
宛に送ることができます。
更新に必要なルートとコントローラーの作成
それでは、最後に/tasks/update
に対応するルートとコントローラーを作成し、フォームで送られたデータを使ってデータベースの値を更新できるようにしていきましょう。
まずルーターを編集します。
ルーターを以下のように編集してください。
Rails.application.routes.draw do
get 'tasks/new'
get 'home/top'
get 'tasks/:id' => 'tasks#show'
get 'tasks/:id/edit' => 'tasks#edit'
post 'tasks/create'
post 'tasks/:id/update' => 'tasks#update'
end
データベースにデータを送るのでここでのHTTPメソッドはgetではなくpostを使用します。
次にコントローラーを以下のように編集してupdateメソッドを追加してください。
class TasksController < ApplicationController
protect_from_forgery :except => [:sample]
def new
end
def show
@task = Task.find_by(id: params[:id])
end
def edit
@task = Task.find_by(id: params[:id])
end
def create
@task = Task.new(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
@task.save
redirect_to controller: 'home', action: 'top'
end
def update
@task = Task.find_by(id: params[:id])
@task.update(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
redirect_to controller: 'home', action: 'top'
end
end
updateメソッドの中身はタスクを追加する時に使用するcreateメソッドとほとんど変わりません。
updateメソッドの場合は、find_by
を使ってidの一致するタスク(@task)を取得し、そのタスクに対してupdate
メソッドを使用することで、タスクの内容を更新しています。
これでデータを編集する処理が完了しました。
実際にトップ画面から詳細ページにアクセスをして、タスクの内容を更新できるか確認してみましょう。
完了機能の作成
最後に完了機能をToDoアプリにつけていきます。
トップページの各タスクに「完了」というリンクを設けて、それを押したときにそのタスクがデータベースから削除されるようにしていきます。
トップページに完了リンクの作成
まずはトップページに完了リンクを作成しましょう。
トップページを以下のように編集してください。
<% @tasks.each do |task| %>
<% if task[:priority] == "high" %>
<span class="redbox">
<%= task[:text] %>
<span class="deadline">
<%= link_to "完了", "/tasks/#{task.id}/delete", {method:"post"} %>
<%= link_to "詳細", "/tasks/#{task.id}" %>
<%= task[:deadline] %>
</span>
</span>
<% else %>
<span class="graybox">
<%= task[:text] %>
<span class="deadline">
<%= link_to "完了", "/tasks/#{task.id}/delete", {method:"post"} %>
<%= link_to "詳細", "/tasks/#{task.id}" %>
<%= task[:deadline] %>
</span>
</span>
<% end %>
<% end %>
<span class="toplink">
<%= link_to "新規投稿ページ", "/tasks/new"%>
</span>
ここではlink_to
を使って、それぞれのタスクごとにリンクを作成しています。
link_to
はmethodオプションを指定することでHTTPメソッドを指定することができます。 今までのように何も指定しないとHTTPメソッドはgetになりますが、今回のようにpostでリクエストを送信したい場合はこのようにpostを指定する必要がありますので注意してください。
次は個別の詳細ページ(app/views/tasks/show.html.erb
)にも完了リンクを作成しましょう。
<%= link_to "<戻る", {:controller => "home", :action => "top" } %>
<%= @task[:text] %>
<%= @task[:deadline] %>
<% if @task[:priority] == "high" %>
<span>priority : high </span>
<% end %>
<%= link_to "編集する", "#{@task.id}/edit" %>
<%= link_to "完了", "/tasks/#{@task.id}/delete", {method:"post"} %>
それでは、/tasks/#{@task.id}/delete
に対応するルートとコントローラーも作成していきましょう。
ルートとコントローラーの作成
まずはルートの設定です。 ルーターを以下のように編集してください。
Rails.application.routes.draw do
get 'tasks/new'
get 'home/top'
get 'tasks/:id' => 'tasks#show'
get 'tasks/:id/edit' => 'tasks#edit'
post 'tasks/create'
post 'tasks/:id/update' => 'tasks#update'
post 'tasks/:id/delete' => 'tasks#delete'
end
次に、コントローラーを編集してdeleteメソッドを新しく定義してください。
class TasksController < ApplicationController
protect_from_forgery :except => [:sample]
def new
end
def show
@task = Task.find_by(id: params[:id])
end
def edit
@task = Task.find_by(id: params[:id])
end
def create
@task = Task.new(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
@task.save
redirect_to controller: 'home', action: 'top'
end
def update
@task = Task.find_by(id: params[:id])
@task.update(text:params[:text], deadline:params[:deadline] , priority:params[:priority])
redirect_to controller: 'home', action: 'top'
end
def delete
@task = Task.find_by(id: params[:id])
@task.delete
redirect_to controller: 'home', action: 'top'
end
end
ここでは、find_by
とparams
を使って、各タスクの情報を取得して@taskに格納したあと、delete
メソッドを使ってタスクのデータを削除しています。
これでデータを削除する処理の作成が完了しました。
終わりに
ここまでの作業で、
- 新規登録機能
- 編集機能
- 削除機能
の実装が完了しました。
実際に完成したToDoアプリを動かしてみて正しくそれぞれの機能が実装できているか確認してみてください。
ここまで作成してきていかがでしょうか?
全体を通してRailsでWebアプリケーションを作成する力が身についたかと思います。
Ruby on Railsは出来ることが多いため、数回やっただけでは中々覚え切れません。
何度もも繰り返す事をおすすめします。
最後までお疲れ様でした。