Rails7 で TODOアプリを作ろう ② (ScaffoldでTaskを作成・rootを設定) 

## はじめに

### 前回は

https://qiita.com/tomoaki-kimura/items/60da37868ad0c8afaa9e

#### プロジェクトの作成として

- asdf
- railsコマンド

について学びました。

### 今回は

#### ScaffoldでTaskを作成・rootを設定として

- scaffold
- MVC
- マイグレーション
- ルーティング

について学びます。

では、はじめていきましょう。

## 1. ScaffoldでCRUD操作の土台を作る

### CRUD

`CRUD` とは `Create(作成)` `Read(参照)` `Update(編集)` `Delete(削除)` の頭文字をとったもので、 
データー操作に欠かせない、4つの操作を表しています。

### Scaffold

`Scaffold` とは足場という意味です。

Rails には非常に協力な実装コマンドがいくつも存在しますが、
その中でもこの `scaffold` は有名で、CRUD操作に必要なものを、コマンド一つで全て用意してくれます。

では、実際にやってみましょう。

プロジェクトフォルダよりコマンドを実行します。

```zsh
todo_app % rails g scaffold Task title description:text start_time:datetime end_time:datetime status:integer
```

ここのコマンドの意味は、

`Task` という名前で `title` `description` `status` という項目を持った　`モデル` を作成して、 `CRUD` 操作に必要なものを全て作ってください。

その際に、

- `title` はデフォルトの `string` 型
- `description` は `text` 型
- `start_time` は `datetime` 型
- `end_time` は `datetime` 型
- `status` は `integer` 型

としてください。という命令になります。 すると、

```zsh
 todo_app % rails g scaffold Task title description:text start_time:datetime end_time:datetime status:integer
      invoke  active_record
      create    db/migrate/20231027033159_create_tasks.rb
      create    app/models/task.rb
      invoke    test_unit
      create      test/models/task_test.rb
      create      test/fixtures/tasks.yml
      invoke  resource_route
       route    resources :tasks
      invoke  scaffold_controller
      create    app/controllers/tasks_controller.rb
      invoke    erb
      create      app/views/tasks
      create      app/views/tasks/index.html.erb
      create      app/views/tasks/edit.html.erb
      create      app/views/tasks/show.html.erb
      create      app/views/tasks/new.html.erb
      create      app/views/tasks/_form.html.erb
      create      app/views/tasks/_task.html.erb
      invoke    resource_route
      invoke    test_unit
      create      test/controllers/tasks_controller_test.rb
      create      test/system/tasks_test.rb
      invoke    helper
      create      app/helpers/tasks_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/tasks/index.json.jbuilder
      create      app/views/tasks/show.json.jbuilder
      create      app/views/tasks/_task.json.jbuilder
```

このような沢山のファイルが作られたというログが流れてきます。
もちろん最初から全てはつかいませんが、主要なものを見ていきましょう。

## 2. Model View Controller の確認

### MVC

MVCとは、

- モデル ( `app/models/task.rb` )
- コントローラー ( `app/controllers/tasks_controller.rb` )
- ビュー ( `app/views/tasks` )

辺りは、頻繁に参照するはずです。では、ひとつずつファイルの確認をしておきましょう。

### Model

モデルは、テーブル操作をするためのロジックを書く場所となります。

現状は、何もありませんので、このように空になっています。

app/models/task.rb【確認】
```ruby
class Task < ApplicationRecord
end
```

### Controller

コントローラーは、リクエストされたURLに対応した動かすメソッドを書く場所となります。

リクエストがあって動き、レコードの操作を命令したり、次の画面に遷移したりという命令を下します。

app/controllers/tasks_controller.rb【確認】
```ruby
class TasksController < ApplicationController
  before_action :set_task, only: %i[ show edit update destroy ]

  # GET /tasks or /tasks.json
  def index
    @tasks = Task.all
  end

  # GET /tasks/1 or /tasks/1.json
  def show
  end

  # GET /tasks/new
  def new
    @task = Task.new
  end

  # GET /tasks/1/edit
  def edit
  end

  # POST /tasks or /tasks.json
  def create
    @task = Task.new(task_params)

    respond_to do |format|
      if @task.save
        format.html { redirect_to task_url(@task), notice: "Task was successfully created." }
        format.json { render :show, status: :created, location: @task }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @task.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /tasks/1 or /tasks/1.json
  def update
    respond_to do |format|
      if @task.update(task_params)
        format.html { redirect_to task_url(@task), notice: "Task was successfully updated." }
        format.json { render :show, status: :ok, location: @task }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @task.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /tasks/1 or /tasks/1.json
  def destroy
    @task.destroy!

    respond_to do |format|
      format.html { redirect_to tasks_url, notice: "Task was successfully destroyed." }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_task
      @task = Task.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def task_params
      params.require(:task).permit(:title, :description, :start_time, :end_time, :status)
    end
end
```

メソッドを見てみると、

`index` `show` `new` `edit` `create` `update` `destroy`

というメソッドが存在しますが、これらのメソッドは `アクション` と呼ばれており、これらがリクエスト毎の命令を持っています。

概ねの使い方は、以下のようになっています。

| アクション名  | 役割                  |
|---------|---------------------|
| index   | 登録済みレコードの一覧表示画面     |
| show    | 登録済みレコードの個別表示画面     |
| new     | 新しいレコードの作成のための入力画面  |
| edit    | 登録済みレコードの編集のための入力画面 |    
| create  | 新しいレコードを登録する        |
| update  | 登録済みレコードを編集する       |
| destroy | 登録済みレコードを削除する       |

一方で、 `private` 以下に書かれたプライベートメソッド

```ruby
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_task
      @task = Task.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def task_params
      params.require(:task).permit(:title, :description, :status)
    end
```

これらに関しては、アクションとは呼びません。

このコントローラー内の重複するような記述をすっきりさせる為に補助的に書かれています。

### View

ビューは `html` に当たるファイルを管理する場所となります。
アクションで言うところの、 `index` `show` `new` `edit` の表示部分のコードが書かれています。拡張子に `erb` が付いているのが特徴です。

それぞれ確認しておきましょう。

#### index

app/views/tasks/index.html.erb【確認】
```ruby
<p style="color: green"><%= notice %></p>

<h1>Tasks</h1>

<div id="tasks">
  <% @tasks.each do |task| %>
    <%= render task %>
    <p>
      <%= link_to "Show this task", task %>
    </p>
  <% end %>
</div>

<%= link_to "New task", new_task_path %>
```

#### show

app/views/tasks/show.html.erb【確認】
```ruby
<p style="color: green"><%= notice %></p>

<%= render @task %>

<div>
  <%= link_to "Edit this task", edit_task_path(@task) %> |
  <%= link_to "Back to tasks", tasks_path %>

  <%= button_to "Destroy this task", @task, method: :delete %>
</div>
```
#### new

app/views/tasks/new.html.erb【確認】
```ruby
<h1>New task</h1>

<%= render "form", task: @task %>

<br>

<div>
  <%= link_to "Back to tasks", tasks_path %>
</div>
```

#### edit

app/views/tasks/edit.html.erb【確認】
```ruby
<h1>Editing task</h1>

<%= render "form", task: @task %>

<br>

<div>
  <%= link_to "Show this task", @task %> |
  <%= link_to "Back to tasks", tasks_path %>
</div>
```

あとは、ファイル名が `_` から始まっている、

`app/views/tasks/_form.html.erb`

`app/views/tasks/_task.html.erb`

これらのファイルがありますが、これは上で紹介した `index` `show` `new` `edit` の中で、 `render` メソッドで呼び出されて使っていますが、これらはまた後ほど触れていきます。

これらMVC用のファイルを書き換える事によってUIやデーターベース操作に関する事を賄う事ができます。

## 3. migration を実行し、DBとモデルを連携させる

### マイグレーションファイル

CRUD操作可能という事は、データーの保存にはDB(データーベース)を使う訳ですが、DBとアプリケーションを結ぶ為の機能として `migration` があります。

その設定は、今回のような `Scaffold` でも生成されますし、 `Model` 単体で作ったり、変更を行う際に生成されます。

これを `マイグレーションファイル`　と言います。

今回は `Scaffold` によって、 `db/migrate/20231027033159_create_tasks.rb` (数字の部分は作成の日時が入るのでそれぞれ別の数値です) のようなファイルが作成されています。

db/migrate/20231027033159_create_tasks.rb【確認】
```ruby
class CreateTasks < ActiveRecord::Migration[7.1]
  def change
    create_table :tasks do |t|
      t.string :title
      t.text :description
      t.datetime :start_time
      t.datetime :end_time
      t.integer :status

      t.timestamps
    end
  end
end
```

これらがDBにテーブルとして設定する内容となります。

現在は、まだDBにこれらの情報が登録されていません。少し確認してみましょう。

```zsh
todo_app % rails s
```

と打ってみると、サーバーが起動し、 `localhost:3000` でアクセスが可能となります。

が、

[![Image from Gyazo](https://i.gyazo.com/133f11216ec32dfd0e80df874036a3cb.png)](https://gyazo.com/133f11216ec32dfd0e80df874036a3cb)

このようなエラーとなります。これは、 `Migration are pending` と書かれているように、
マイグレーションファイルとDBががまだ連携出来ていないという状況になります。

では、一旦サーバーを落として、マイグレーションファイルの状況も確認しましょう。

```zsh
todo_app % rails db:migrate status
```

というコマンドを打ってみると、以下のような結果が返ってきます。

```zsh
todo_app % rails db:migrate:status

database: storage/development.sqlite3

Status   Migration ID    Migration Name
--------------------------------------------------
down    20231027033159  Create tasks
```

現在、新しく作ったファイルは

```
down    20231027033159  Create tasks
```

`down` となっており、この状態であれば、ファイルの内容を書き換える事で、書き換えた内容をDBに書き込む事が出来ます。

### DBの制約

DBに反映させる前に、少し設定を足しておきたいので、早速ファイルの編集をしておきましょう。

同ファイルを下記のように追記してください。

db/migrate/20231027033159_create_tasks.rb【追記】
```ruby
class CreateTasks < ActiveRecord::Migration[7.1]
  def change
    create_table :tasks do |t|
      t.string :title
      t.text :description
      t.datetime :start_time, null: false #追加部分
      t.datetime :end_time
      t.integer :status, default: 0, null: false #追加部分

      t.timestamps
    end
  end
end
```

書き加えたコードは、該当カラムに

`null: false` という `絶対に入力が必要な制限` と、

`default: 0` という `何も入力がない場合、初期値を0とする制限` 

という設定を追加しています。

実際には、`title` などにも制約の設定しても良いのですが、一旦この通りに進めていただくと後々の相違点が出なくなっています。

### マイグレーション

では、DBとの同期を行います。ターミナルに

```zsh
todo_app % rails db:migrate
```

と打ってみます。 

```zsh
 todo_app % rails db:migrate
== 20231027073408 CreateTasks: migrating ======================================
-- create_table(:tasks)
   -> 0.0010s
== 20231027073408 CreateTasks: migrated (0.0010s) =============================
```

このように返ってくればOKです。

`-- create_table(:tasks)` と表示されていますね。 `tasks` というテーブルが作られました。

現在のDBの状況は、`migrate` を行うと `db/schema.rb` に自動で記載されます。

db/schema.rb【確認】
```ruby
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2023_10_27_073408) do
  create_table "tasks", force: :cascade do |t|
    t.string "title"
    t.text "description"
    t.datetime "start_time", null: false
    t.datetime "end_time"
    t.integer "status", default: 0, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end
```

自動で書き込みがされますので、基本的このファイルを私達が触ることはありません。

また、マイグレーションが通った事で、再び

```zsh
todo_app % rails db:migrate:status
```

を実行してみます。すると、

```zsh
 todo_app % rails db:migrate:status

database: storage/development.sqlite3

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20231027073408  Create tasks
```

マイグレーションファイルのステータスが、 `up` になっているのが確認できます。

`up`　になっている状態のマイグレーションファイルは編集できないので、気を付けておきましょう。

間違った場合は、 `rails db:rollback` で、
マイグレーションファイルの新しい順から、
一つずつ `down` に戻す事が可能です。

## 4．ルーティングとトップページ

### ルーティングとは

ルーティングとは リクエストされるURLに対して、どの `controller` の、どの `action` を実行するかの割り振りを行う場所です。

URLを通していないアクションは使用できない事になりますので、
極論を言えば、開発途中のコードが混入していてもルーティングさえ管理出来ていれば、実行されないので安心という事です。

では、ファイルを見てみましょう。 `config/routes.rb` を開きます。

config/routes.rb【確認】
```ruby
Rails.application.routes.draw do
  resources :tasks
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check

  # Defines the root path route ("/")
  # root "posts#index"
end
```

実はすでに `scaffold` によって書き込みが行われており、

```
resources :tasks
```

の部分がこれに当たります。たった一行ですが、ルーティングを確認するコマンド

`rails routes` でどういった結果を産んでいるかを確認できます。

では、調べてみましょう。

```zsh
 todo_app % rails routes -c tasks
```

ターミナルこのように打つと、

```zsh
   Prefix Verb   URI Pattern               Controller#Action
    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
```

このような結果が返ってきます。

これは、どのURLとアクションが繋がっているかを表示した表になります。

たったの一行の結果がCRUD操作に必要なルーティング全てを決定しているようです。

各ルートに関しては、後ほど改めて触れますので、一旦ここまでにしておきましょう。

### トップページ

ただし、この状態にはトップページがありません。 `routes.rb` 内のコメントにも

```
  # Defines the root path route ("/")
  # root "posts#index"
```

このような記載があり、`root path` を定義する記述が書いてあります。

ですので、これに従い `routes.rb` を編集します。

config/routes.rb【編集】
```ruby
Rails.application.routes.draw do
  root "tasks#index"
  resources :tasks
  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check
end
```

`root "tasks#index"` というコードが足されています。

これは、

トップページ(root) にアクセスすると、 `tasks` コントローラーの `index` アクションを実行しますよ。

という意味になります。これでトップページがデフォルト画面から変更されているはずです。

### 単数形? 複数形?

ちょっと不思議なのが、`task` なのか `tasks` なのか・・・モデル名は単数でしたが、単数と複数の名称が混在していてちょっと分からなくなりそうです。

簡単な覚え方として `モデル名` は `単数形` で、 `コントローラー名` は `複数形`、 DBの`テーブル名` も `複数形` となります。

また、度々Railsでは厳密に単複を使い分ける場面がありますので、都度覚えていきましょう。

### CRUD操作の確認

では、再び `rails s` で起動します。

今度はエラーとならず、このような画面が出てくるはずです。

[![Image from Gyazo](https://i.gyazo.com/1c5454cfbb37f648aaa7f0bb15ac4122.png)](https://gyazo.com/1c5454cfbb37f648aaa7f0bb15ac4122)

もう操作も出来ますので、

[![Image from Gyazo](https://i.gyazo.com/1ded815683103db5ff27bdfd658e948b.png)](https://gyazo.com/1ded815683103db5ff27bdfd658e948b)

リンクから、新規作成を行ったり、

[![Image from Gyazo](https://i.gyazo.com/65d8b10d6808dfbd386519dedc04270b.png)](https://gyazo.com/65d8b10d6808dfbd386519dedc04270b)

表示をしたり出来るようになっています。

この動作ができればOKです。

## 5. git

### git

ここまでを保存しておきましょう。

```zsh
todo_app % git add .
todo_app % git commit -m " ScaffoldでTaskを作成・rootを設定"
```

### GitHub

Githubの設定ができているのであれば、同名のリポジトリを作成し、pushしておきましょう。

## おわりに

本チャプターはここまでとなります。

### 次回は

https://qiita.com/tomoaki-kimura/items/76279fa062a9d0bc57a8

- Bootstrap
- assets
- View

を学びます。

ここまでお疲れ様でした。