Railsでタスク管理アプリを作成_7

# タスク管理アプリを作ってみよう(続き)

## 1. 前置き

### 1. 前提・事前作業

 * [こっちの投稿](http://qiita.com/fujimohige/items/4c5602b36dc706e2b6ee)
 * 例によって[ドットインストール](http://dotinstall.com/lessons/basic_rails_v2/24901)の写経
 * [#19 before_actionを使ってみよう](http://dotinstall.com/lessons/basic_rails_v2/24919)から実施



## 2. before_actionを使ってみよう

* Controllerの整備
* `/home/vagrant/taskApp/app/Constollers` の `projects_constroller.rb` を弄る。
* 下記コードが重複しているので整備する

```rb:ruby
    @project = Project.find(params[:id])
```

* `before_action`を活用する
* Actionで必ず実行されるべきことを`before_action`に定義することで、コードの重複を防ぐ

```rb:ruby
class ProjectsController < ApplicationController

    # 共通処理を定義（各Actionでは`set_project` が呼ばれますよ、という宣言）
    # 別途 `set_project` という関数を定義する
    # 全Actionに適用するわけではないので、 `only` で実行するActionを限定する
    before_action :set_project, only: [:show, :edit, :update, :destroy]

# 処理いろいろ・・・

  private
    # project_params を定義
    def project_params
      # projectでわたってきたもののうち、titleだけ使用
      params[:project].permit(:title)
  end
    # before_actionで定義した処理の実装
    def set_project
      @project = Project.find(params[:id])
    end

end

```

## 3. Tasksを設定していこう

* 機能を拡張する
* projectに対してtaskを追加できるようにする

* 一旦railsをとめて、Modelを作成する
* 下記コマンドは以下の意味を持つ
 * `Task` というモデルを作る
 * `title` というフィールドを持つ（String型は型の明記不要）
 * `done` というフィールドを `boolean` 型で持つ
 * `project` というフィールドを参照する

```
rails g model Task title done:boolean project:references
```
* でけた

```
    invoke    active_record
    create      db/migrate/20140426085349_create_tasks.rb
    create      app/models/task.rb
    invoke      test_unit
    create        test/models/task_test.rb
    create        test/fixtures/tasks.yml

```

* 生成されたマイグレーションファイルをチェック
* `/home/vagrant/taskApp/db/migrate` の `～～～_create_tasks.rb` を開く
* `t.boolean :done` はデフォルトでfalseにしておく

```rb:ruby
class CreateTasks < ActiveRecord::Migration
  def change
    create_table :tasks do |t|
      t.string :title
      t.boolean :done, default: false
      t.references :project, index: true

      t.timestamps
    end
  end
end
```
* マイグレーションファイルを読み込んで反映させる

```
rake db:migrate
```

* Controlloerをジェネレートする

```
rails g controller Tasks
```

## Associationの設定をしよう

* ModelとかControllerとかできたので、ルーティングとか引き続きやっていく
* その前にModelの関連付けを行う
* `/home/vagrant/taskApp/app/models` の `task.rb` を見てみると、「projectに属してるよ」という明記がある。
* `/home/vagrant/taskApp/app/models` の `project.rb` も同様に、関連付け情報を記述する。

```rb:ruby(task.rb)
class Task < ActiveRecord::Base
  belongs_to :project
end
```

```rb:ruby(project.rb)
class Project < ActiveRecord::Base
    # taskと1対多で紐付く
    has_many :tasks
    # 必須制御
    # 「project」を未入力の状態では保存できなくなる
    # validates :title, presence: true
    # エラーメッセージを定義（メッセージは独自定義）
    validates :title,
    presence: { message: "入力してください"},
    length:   {minimum: 3, message: "短すぎます"}
end
```

* ルーティングを行う
* `/home/vagrant/taskApp/config` の `routes.rb` を弄る

```rb:ruby(before)
  resources :projects   # Projectに関するルーティングを自動生成
```

```rb:ruby(after)
  resources :projects do
    resources :tasks, only: [:create, :destroy]
  end
```
* `rake` で変更を反映

```
rake routes
```

* こんな感じになった
* `only: [:create, :destroy]` とすることで生成範囲を限定

```
       Prefix Verb   URI Pattern                               Controller#Action
project_tasks POST   /projects/:project_id/tasks(.:format)     tasks#create
 project_task DELETE /projects/:project_id/tasks/:id(.:format) tasks#destroy
     projects GET    /projects(.:format)                       projects#index
              POST   /projects(.:format)                       projects#create
  new_project GET    /projects/new(.:format)                   projects#new
 edit_project GET    /projects/:id/edit(.:format)              projects#edit
      project GET    /projects/:id(.:format)                   projects#show
              PATCH  /projects/:id(.:format)                   projects#update
              PUT    /projects/:id(.:format)                   projects#update
              DELETE /projects/:id(.:format)                   projects#destroy
         root GET    /                                         projects#index
```


## 4. Tasksの新規作成フォームを作ろう

* とりあえずさっき止めたサーバを実行

```
rails s
```

* ロジックを作成する
* `/home/vagrant/taskApp/app/views/projects` の `show.html.erb` を弄る。

```erb:ERB(show.html.erb)
<h1><%= @project.title %></h1>

<ul>
    <!-- projectに紐付くtaskがあるだけループする -->
    <% @project.tasks.each do |task| %>
    <li>
        <!-- taskのtitleを表示 -->
        <%= task.title %>
    </li>
    <% end %>

    <!-- 新規タスク登録フォーム -->
    <li>
        <%= form_for [@project, @project.tasks.build] do |f| %>
        <%= f.text_field :title %>
        <%= f.submit %>
        <% end %>
    </li>

</ul>
```

* 下記の通りタスク登録用のフォームが表示された（まだタスクは登録してないので登録フォームの1行だけ表示される）


![無題.png](https://qiita-image-store.s3.amazonaws.com/0/40160/c312c7a9-1c63-73f8-0928-add032fe263d.png)



## 5. Tasksを保存していこう

* `create` Actionを作っていく
* ルーティングを確認


```
project_tasks POST   /projects/:project_id/tasks(.:format)     tasks#create
```

* フォームからわたってくるのはproject_idなので、それを使ってControllerを色々やっていく
* `/home/vagrant/taskApp/app/controllers` の `tasks_controller.rb` を弄る

```rb:ruby

class TasksController < ApplicationController


  # createアクションを追加
  def create
    # 画面からわたってきたIDからprojectをfindする
    @project = Project.find(params[:project_id])
    # taskを登録
    @task    = @project.tasks.create(tasks_params)
    # 遷移先はプロジェクト詳細画面
    redirect_to project_path(@project.id)
  end


  private
    # task_params を定義
    def tasks_params
      # taskでわたってきたもののうち、titleだけ使用
      params[:task].permit(:title)
    end

end
```

* 動作確認。入力して、サブミットすると...


![無題.png](https://qiita-image-store.s3.amazonaws.com/0/40160/817f400b-0857-fc63-d611-0a1dab4a93c8.png)

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/40160/54371e45-725a-2627-30d4-6a74a6191df6.png)

* 追加された
 　
* さらに、バリデーションを追加する
* 必須入力チェックを追加
* `/home/vagrant/taskApp/app/models` の `task.rb` を弄る

```rb:ruby

class Task < ActiveRecord::Base
    belongs_to :project # projectに属してるよ。という情報
    # 必須チェック メッセージの設定はなし
    validates :title,
        presence: true
end


```

* 未入力状態でサブミットしても、データは登録されないでリダイレクトされる。


## 6. Tasksの削除をしてみよう

* 登録したタスクを削除する
* `/home/vagrant/taskApp/app/views/projects` の `show.html.erb` を弄る
* の前に、 `rake routes` でルーティングを確認

```
 project_task DELETE /projects/:project_id/tasks/:id(.:format) tasks#destroy
```

* `project_id` と `task_id` が必要らしいので取得するようにする


```erb:ERB(show.html.erb)
<h1><%= @project.title %></h1>

<ul>
    <!-- projectに紐付くtaskがあるだけループする -->
    <% @project.tasks.each do |task| %>
    <li>
        <!-- taskのtitleを表示 -->
        <%= task.title %>
        <!-- 削除リンク(taskのプロジェクトIDとtaskIDを取得) -->
        <%= link_to "[Delete]", project_task_path(task.project_id, task.id), method: :delete, data: {confirm: "are you sure?"} %>
    </li>
    <% end %>

    <!-- 新規タスク登録フォーム -->
    <li>
        <%= form_for [@project, @project.tasks.build] do |f| %>
        <%= f.text_field :title %>
        <%= f.submit %>
        <% end %>
    </li>

</ul>
```

* これでとりあえず削除リンクが追加される。

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/40160/00cd8c05-e7ee-a7c4-e860-70f74677190e.png)


* さらにControllerにActionを追加する。

* `/home/vagrant/taskApp/app/views/controllers` の `tasks_controller.rb` を弄る
* ルーティング情報によると

```
 project_task DELETE /projects/:project_id/tasks/:id(.:format) tasks#destroy
```

* task_idがわたってきているので、これを使えばOK


```rb:ruby(tasks_controller.rb)
class TasksController < ApplicationController


  # createアクションを追加
  def create
    # 画面からわたってきたIDからprojectをfindする
    @project = Project.find(params[:project_id])
    # taskを登録
    @task    = @project.tasks.create(tasks_params)
    # 遷移先はプロジェクト詳細画面
    redirect_to project_path(@project.id)
  end
  # destroyアクションを追加
  def destroy
      @task = Task.find(params[:id])
      @task.destroy
      redirect_to project_path(params[:project_id])
  end

  private
    # task_params を定義
    def tasks_params
      # taskでわたってきたもののうち、titleだけ使用
      params[:task].permit(:title)
    end

end

```

* Deleteリンクを押下するとメッセージが上がるので、

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/40160/9d15e03c-1946-5bf7-18ba-72554d42f607.png)

* OKすると...

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/40160/6c54f8a9-34d1-1c92-d0f5-42c619b24f1b.png)

* 消せた