はじめに
railsを一通り学んだので、自分の課題として簡単なタスク管理ができるWebアプリケーションを作成しましたので、紹介して行きます。
ちなみに、Webアプリケーションとは、HTTPリクエストの中の4つのメソッドを満たす、その名もCRUDメソッド(GET, POST, PUT, DELETE)を使って、Web上のリソースを操作できるアプリケーションのことです。今回作成するWebアプリケーションもその定義通りのものを作成します。
環境
- Ruby 2.4.1
- Rails 5.2.0
プロジェクトの作成
プロジェクト名はtasklistとします。
rails new tasklist --database=mysql --skip-test
コマンドと、コマンドのオプションの説明
-
rails new プロジェクト名
:プロジェクトを作成することができます -
--database=mysql
:使用するデータベースをmysql
に指定 -
--skip-test
:自動テスト機能を使用しない
RailsでWebアプリを作る際の流れ
- 1.モデル(
Model
)の作成 - 2.ルーティングの設定
- 3.ルーティングの設定ごとに
Controller
とView
の作成
モデルの作成
RailsとDB(今回はMySQL
)の接続を行います。
データベースの作成
Railsが提供しているコマンドでデータベースを作成して行きます。
$ rails db:create
Created database 'tasklist_development'
Created database 'tasklist_test'
mysql> show databases;
+---------------------------+
| Database |
+---------------------------+
| tasklist_development |
| tasklist_test |
+---------------------------+
(結果を一部省略しています)
tasklist_test
も作成されますが、こちらは使用しません。
ここでrails s
でサーバ起動したら立ち上がっていること確認できます。
参考までに、データベースに関する接続設定を管理しているファイルはconfig/database.yml
です。
.yml
の拡張子のファイルは、YAML形式の設定ファイルで、キー: 値
という形の羅列になっています。
書き方が楽なので、設定ファイルとしてよく使われています。インデントで階層構造を表現するそうですpugみたいだね。
モデルの作成
下記コマンドで、Task
モデルを作成します。Task
モデルはcontent
というメッセージ内容(文字列)を持つようにします。
$ rails g model Task content:string
モデルをgenerate
したらマイグレーションファイルというテーブルを管理するファイルが作成されます。
class CreateTasks < ActiveRecord::Migration[5.2]
def change
create_table :tasks do |t|
t.string :content
t.timestamps
end
end
end
ファイルの中身ですが、これはtasks
というテーブルを作成する命令が書かれているマイグレーションファイルです。
このファイルの中身を実行して、テーブルを作成してみましょう。
マイグレーションの実行
$ rails db:migrate
== 20180606073308 CreateTasks: migrating ======================================
-- create_table(:tasks)
-> 0.0052s
== 20180606073308 CreateTasks: migrated (0.0060s) =============================
マイグレーションを実行したので、tasklist_development
データベースにtasks
テーブルが作成されました。
mysql> describe tasks;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| content | varchar(255) | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
指定したのは、content
カラムだけでした。主キーであるid
、あと、作成時間を記録するcreated_at
と、更新時間を記録するupdated_at
は自動で作成されると分かりますね。
rails consoleを使ってレコードを3件追加する
後々、view
側で確認するために、rails console
でレコードを3件追加します。
$ rails c
コンソール内で
- メソッド
new
を使って、モデルのインスタンスを作成 - メソッド
save
を使って、モデルのインスタンスをDBに保存
を、します。
2.4.1 :004 > task = Task.new(content: 'task01')
=> #<Task id: nil, content: "task01", created_at: nil, updated_at: nil>
2.4.1 :005 > task.save
(0.3ms) BEGIN
Task Create (0.2ms) INSERT INTO `tasks` (`content`, `created_at`, `updated_at`) VALUES ('task01', '2018-06-06 08:03:49', '2018-06-06 08:03:49')
(1.8ms) COMMIT
=> true
2.4.1 :006 > task = Task.new(content: 'task02')
=> #<Task id: nil, content: "task02", created_at: nil, updated_at: nil>
2.4.1 :007 > task.save
(0.1ms) BEGIN
Task Create (0.4ms) INSERT INTO `tasks` (`content`, `created_at`, `updated_at`) VALUES ('task02', '2018-06-06 08:03:56', '2018-06-06 08:03:56')
(1.9ms) COMMIT
=> true
2.4.1 :008 > task = Task.new(content: 'task03')
=> #<Task id: nil, content: "task03", created_at: nil, updated_at: nil>
2.4.1 :009 > task.save
(0.1ms) BEGIN
Task Create (0.2ms) INSERT INTO `tasks` (`content`, `created_at`, `updated_at`) VALUES ('task03', '2018-06-06 08:04:01', '2018-06-06 08:04:01')
(1.7ms) COMMIT
=> true
SQL内で挿入されたデータを確認しましょう。
mysql> select * from tasks;
+----+---------+---------------------+---------------------+
| id | content | created_at | updated_at |
+----+---------+---------------------+---------------------+
| 1 | task01 | 2018-06-06 08:03:49 | 2018-06-06 08:03:49 |
| 2 | task02 | 2018-06-06 08:03:56 | 2018-06-06 08:03:56 |
| 3 | task03 | 2018-06-06 08:04:01 | 2018-06-06 08:04:01 |
+----+---------+---------------------+---------------------+
これでモデルのインスタンス(レコード挿入)の作成を確認できました。タスク管理アプリの作るのに必要なモデルの作成はこれで完了です。
これから、Router と Controller と Viewを一気に作って行きますが、それらの関係を見てみましょう。
Router と Controller と View の関係を簡単に
-
Router
は、URLのルーティングを一元管理してる -
Controller
は、HTTPリクエストの処理を担っている -
View
は、HTTPレスポンスとして返されるWebページ
ルーティングの設定
CRUDのための基本の7つのルーティングを省略形で書きます。
Rails.application.routes.draw do
root to: 'tasks#index'
resources :tasks
end
root to: 'tasks#index'
は、/
(TOPページ)と/tasks
にアクセスしたときのルーティングです。
resources :tasks
は、CRUDのための基本の7つのルーティングを省略形で書いたやつです。
現状のルーティングを確認して見ましょう。
$ rails routes
Prefix Verb URI Pattern Controller#Action
root GET / tasks#index
tasks GET /tasks(.:format) tasks#index
POST /tasks(.:format) tasks#create
new_task GET /tasks/new(.:format) tasks#new
edit_task GET /tasks/:id/edit(.:format) tasks#edit
task GET /tasks/:id(.:format) tasks#show
PATCH /tasks/:id(.:format) tasks#update
PUT /tasks/:id(.:format) tasks#update
DELETE /tasks/:id(.:format) tasks#destroy
(一部省略)
root to: 'tasks#index'
とresources :tasks
が展開されていると確認できます。
このresources :tasks
で展開された7つのルーティングのことをRESTfulなルーティングと呼びます。RESTfulなルーティングは、リソースのCRUD操作のために必要な7つのアクション(index, show, new, create, edit, update, destroy)です。
Controller と View を一気に作成
Controller
以下のコマンドで、Controller
を生成して行きます。
$ rails g controller Tasks --no-helper --no-assets
create app/controllers/tasks_controller.rb
invoke erb
create app/views/tasks
コントローラ名はモデルの複数形で書きます。Task
モデルに対するコントローラ名はTasks
と書きます。
コマンドと、コマンドのオプションの説明
-
--no-helper
:Helperを自動生成しないオプションです -
--no-assets
:CSS と JavaScript を自動生成しないオプションです
Controller を RESTful なルーティングに対応させる
ルーティングに対応したアクションを コントローラーに書いて行きましょう。もう一度、現状のルーティングを確認して見ましょう。
$ rails routes
Prefix Verb URI Pattern Controller#Action
root GET / tasks#index
tasks GET /tasks(.:format) tasks#index
POST /tasks(.:format) tasks#create
new_task GET /tasks/new(.:format) tasks#new
edit_task GET /tasks/:id/edit(.:format) tasks#edit
task GET /tasks/:id(.:format) tasks#show
PATCH /tasks/:id(.:format) tasks#update
PUT /tasks/:id(.:format) tasks#update
DELETE /tasks/:id(.:format) tasks#destroy
(一部省略)
コントローラー内でルーティングと同じ名前のメソッド名として定義すれば対応させられます。
class TasksController < ApplicationController
def index
end
def show
end
def new
end
def create
end
def edit
end
def update
end
def destroy
end
end
View
Viewの構成
View
ファイルを作成していきます。ルーティングの中で必要なView
ファイルはGET
メソッドで指定されたルーティングのみです。(理由:GET
メソッド以外はページの表示ではなくリソースの操作だから)
そこで、もう一度、現状のルーティングを確認して見ましょう。
$ rails routes
Prefix Verb URI Pattern Controller#Action
root GET / tasks#index
tasks GET /tasks(.:format) tasks#index
POST /tasks(.:format) tasks#create
new_task GET /tasks/new(.:format) tasks#new
edit_task GET /tasks/:id/edit(.:format) tasks#edit
task GET /tasks/:id(.:format) tasks#show
PATCH /tasks/:id(.:format) tasks#update
PUT /tasks/:id(.:format) tasks#update
DELETE /tasks/:id(.:format) tasks#destroy
(一部省略)
GET
メソッドのみがView
ファイル作成の対象なので、 GET
メソッドのルーティングであるindex
,show
,new
,edit
に対応するView
ファイルをを作成して行きます。具体的なファイルの場所と名前は下記の通りです。
- app/views/tasks/index.html.erb
- app/views/tasks/show.html.erb
- app/views/tasks/new.html.erb
- app/views/tasks/edit.html.erb
View
ファイルはrails generate
コマンドは無いので、手作業で作成して行きます。
共通部分
View
ファイルのパスはapp/views/laytous/application.html.erb
です。
この中の、<%= yield %>
の部分が、コントローラのアクションに関連したView
ファイル毎に変わります。
tasks#index
Controller
インデックスページは、タスクを一覧で全て取得したいからコントローラは下記のようになる
def index
@tasks = Task.all
end
View
<h1>メッセージ一覧</h1>
<ul>
<% @tasks.each do |task| %>
<li><%= task.content %></li>
<% end %>
</ul>
確認
現在のtasks
テーブルの全レコードはこんな感じ
mysql> select * from tasks;
+----+---------+---------------------+---------------------+
| id | content | created_at | updated_at |
+----+---------+---------------------+---------------------+
| 1 | task01 | 2018-06-06 08:03:49 | 2018-06-06 08:03:49 |
| 2 | task02 | 2018-06-06 08:03:56 | 2018-06-06 08:03:56 |
| 3 | task03 | 2018-06-06 08:04:01 | 2018-06-06 08:04:01 |
+----+---------+---------------------+---------------------+
3 rows in set (0.00 sec)
こんな感じに出力されていればOK
OK ですね。次は、詳細ページ行きましょう。
tasks#show
こちらは、各タスクの詳細ページです。
Controller
コントローラは、ここの詳細ページのid
が取得できればokなので
def show
@task = Task.find(params[:id])
end
View
<h1>id:<%= @task.id %>のタスク詳細</h1>
<p><%= @task.content %></p>
確認
今のとこ~/tasks/1
、~/tasks/2
、~/tasks/3
でアクセスしたら詳細ページは見れると思います。
index から show へのリンクを追加する
URL
直打ちもあれなので、index
ページのタスクのidにリンクさせましょう。
<h1>メッセージ一覧</h1>
<ul>
<% @tasks.each do |task| %>
<li><%= link_to task.id, task %>:<%= task.content %></li>
<% end %>
</ul>
link_to
はRailsが提供するメソッドです。
link_to 表示文字列, リンク先
という形式で書きます。
表示文字列はidなので、task.id
です。
リンク先は、task
になっています。リンク先として、task
のようにモデルのインスタンス(レコード)を渡すと、自動的にそのインスタンスのshow
アクションへとリンクされます。
リンク生成のためのメソッド
もう一度、現状のルーティングを確認して見ましょう。
$ rails routes
Prefix Verb URI Pattern Controller#Action
root GET / tasks#index
tasks GET /tasks(.:format) tasks#index
POST /tasks(.:format) tasks#create
new_task GET /tasks/new(.:format) tasks#new
edit_task GET /tasks/:id/edit(.:format) tasks#edit
task GET /tasks/:id(.:format) tasks#show
PATCH /tasks/:id(.:format) tasks#update
PUT /tasks/:id(.:format) tasks#update
DELETE /tasks/:id(.:format) tasks#destroy
(一部省略)
Prefix
にnew_task
やedit_task
があります。Prefix
があるのは全てGET
メソッドに対してのみです。ルーティングを設定すると、自動的にリンク生成のためのメソッドも定義されます。上記のようなルーティングが生成された場合には、下記の表のようにメソッドが自動的に生成されます。
ルーティング | リンク生成のメソッドの例 | 生成されたリンク |
---|---|---|
index | tasks_path | /task |
show | tasks_path(@task) | /task/1 など |
new | new_tasks_path | /task/new |
edit | edit_tasks_path(@task) | /tasks/1/edit など |
リンク生成のメソッドが、「Prefix & _path」の形式になっています。
次は、タスク作成ページ行きましょう。
tasks#new
new
アクションはPOST
メソッドを作成するタスクを入力するフォームのあるページです。
Controller
インスタンスを作成します。
def new
@task = Task.new
end
View
フォームはform_for
を使用して設置します。
<h1>タスクの作成</h1>
<%= form_for(@task) do |f| %>
<%= f.label :content, 'タスク' %>
<%= f.text_field :content %>
<%= f.submit '投稿' %>
<% end %>
<%= link_to '一覧に戻る', tasks_path %>
一覧ページ(index)から新規タスク作成ページ(new)へのリンクを追加する
<h1>タスクの作成</h1>
<%= form_for(@task) do |f| %>
<%= f.label :content, 'タスク' %>
<%= f.text_field :content %>
<%= f.submit '投稿' %>
<% end %>
<%= link_to '一覧に戻る', tasks_path %>
indexページからのリンクができまして、クリックしたら下のようなView
が出力されると思います。
完成しました。が、投稿ボタンを押してもcreate
アクションを実装していないので何もなりません。
次は、それを実装しましょう。
tasks#create
create
アクションは、new
アクションからきたView
ページから送信されてきたフォームを処理するところです。
Controller
new
からcreate
へ送られてきたフォームの内容はparams[:task]
に入っていますが、そのまま使用するとセキュリティ上の問題があります。なので、ストロングパラメータというフィルタを使用します。
class TasksController < ApplicationController
#省略
def create
@task = Task.new(task_params)
if @task.save
flash[:success] = 'タスクが投稿されました'
redirect_to @task
else
flash[:danger] = 'タスクが投稿されません'
render :new
end
end
def edit
end
def update
end
def destroy
end
end
private
def task_params
params.require(:task).permit(:content)
end
ストロングパラメータに関して
:content
だけが必要なカラムの場合、このように記述します。
privateに関して
private
は明示するものです。何を明示するかというと、それ以降のメソッドがアクションではなく、そのクラス内だけで使用するメソッドだと明示するものです。
条件分岐に関して
なんの条件分岐かというと、
@task.save
が成功した場合、つまり、true
だった場合はif文中のコマンドが実行されます。
@task.save
が失敗した場合、つまり、false
だった場合はelse文中のコマンドが実行されます。
false
になるケースの例でいうと、255文字以上の文字列を入力した場合ですね。varchar(255)
で入力文字数の限界が255文字なので。
mysql> describe tasks;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| content | varchar(255) | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
flashに関して
flash
は投稿が正常に行われたことを知らせる機能です。後ほどview
を実装して行きます。
redirect_to と render の違い
一番の違いは、redirect_to
はHTTPリクエスト(GET
メソッド)を発生させ、renderは発生させないところです。
「GET」「/tasks/:id」というHTTPリクエストが送られるので、ルーティングに従って、Tasksコントローラーのshow
アクションの処理に移り、その結果として、詳細ページが表示される、ということですね。
参考
https://qiita.com/january108/items/54143581ab1f03deefa1
@task.save
が成功した場合、redirect_to リンク先
は、リンク先(@task
のshowルーティング)に強制的に飛ばすメソッドです。
そして、show/id
を出力して、flash
が出て保存されたことがわかります。
@task.save
が失敗した場合、render :new
なので、new.html.erb
に直接飛びます。
View
保存に成功した場合、/tasks/:id
へリダイレクトします。
<h1>id:<%= @task.id %>のタスク詳細</h1>
<p><%= @task.content %></p>
<%= link_to 一覧へ戻る,tasks_path %>
ここで、フラッシュメッセージ(flash
)を表示させるための、設定をして行きます。
<% flash.each do |task_type, task| %>
<div><%= task %></div>
<% end %>
全てのView
で表示させたいので、/views/layouts/application.html.erb
にrender
させます。
<!DOCTYPE html>
<html>
<head>
<title>Tasklist</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= render 'layouts/_flash_tasks' %>
<%= yield %>
</body>
</html>
これで、flash
に代入されるとメッセージが表示されるようになります。flash
は、Controller
からView
へ、ハッシュの形式でメッセージを渡します({ success: 'タスクが投稿されました' }
)←こんな感じ。success
とかdanger
にしているのは、bootstrap都合です。
参考
https://qiita.com/youcune/items/12b153c08db695952e47
次は、タスク編集機能へ行きましょう。
tasks#edit
Controller
編集するページのidが知りたいので
def edit
@task = Task.find(params[:id])
end
View
new
のview
ファイルとほぼ同じです。
<h1>タスクの編集</h1>
<%= form_for(@task) do |f| %>
<%= f.label :content, 'タスク' %>
<%= f.text_field :content %>
<%= f.submit '投稿' %>
<% end %>
<%= link_to '一覧に戻る', tasks_path %>
ではshow
のview
ページから、edit
のview
ページにリンクさせましょう。
<h1>id:<%= @task.id %>のタスク詳細</h1>
<p><%= @task.content %></p>
<%= link_to '一覧へ戻る', tasks_path %>
<%= link_to '編集ページへ', edit_task_path(@task) %>
次は、タスク更新の機能作成を実装しに行きましょう。
tasks#update
コントローラ部分は、create
アクションに似ています。
Controller
def update
@task = Task.find(params[:id])
if @task.update(task_params)
flash[:success] = 'タスクが編集されました'
redirect_to @task
else
flash.now[:danger] = 'タスクが編集されませんでした'
render :new
end
end
create
アクションと異なるところは、①と②の部分です。なぜ違うかというのは、手順を説明すればわかるかと思います。
-
tasks/id
(タスク詳細ページ)にある「編集ページへ」リンクをクリック -
edit
アクションが呼ばれる - 編集して、投稿ボタンを押すとおそらく
update
アクションに行く -
update
アクションとしては、どのid
を編集・更新すべきかわからないのでそれを取得しようとしてる(それが①) - 取得した
id
がストロングパラメータ(task_params
)を通過した後、:content
があればif文中が実行。なければelse(それが②)
View
不要です。
次は、タスク削除に行きましょう。
tasks#destroy
Controller
def destroy
@task = Task.find(params[:id])
@task.destroy
flash[:success] = 'タスクが削除されました'
redirect_to tasks_path
end
リダイレクトする際、今まではprefix_path
を利用してきましたが、リダイレクトの場合だけは~_url
を使用しましょう。
View
削除用のView
は不要です。
ただ、削除用のリンクは必要なので、show
ページに書きましょう。
<h1>id:<%= @task.id %>のタスク詳細</h1>
<p><%= @task.content %></p>
<%= link_to '一覧へ戻る', tasks_path %>
<%= link_to '編集ページへ', edit_task_path(@task) %>
<%= link_to '削除する', @task, method: :delete, data:{ confirm: '本当によろしいですか?'} %>
method: :delete はDELETE
メソッドを送信することを明示しています。data: { confirm: ... }
はJava Scriptの記述です。
これで、自分のRails学習用課題である、全てのCRUDメソッド(GET, POST, PUT, DELETE)を使って、Web上のリソースを操作できるアプリケーション作成が完了です。
レポジトリ