Laravel 中級Task Listの5.4バージョン

# Laravel Practice
Laravelをざっと理解するため、公式サイトの中級TaskListをやってみたのでそのログです。
[https://laravel.com/docs/5.1/quickstart-intermediate](https://laravel.com/docs/5.1/quickstart-intermediate)

基本的なCRUDと、ログイン機能がついたものです。
なお上記URLはこれは5.1なので、5.4に合わせて各所調整しています。

**github**
[https://github.com/shnr/laravel_tasklist](https://github.com/shnr/laravel_tasklist)

**Laravel Version**
Laravel Framework 5.4.36


## インストール

### composer install
[https://getcomposer.org/download/](https://getcomposer.org/download/)

チュートリアルままですが、まずはプロジェクトディレクトリを作って、composerのダウンロード。

```
$ mkdir myproject-dir
$ cd myproject-dir
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"
```

そしてlaravel install.

```
$ php composer.phar create-project --prefer-dist laravel/laravel mytasklist
```

インストールの確認

```
$ cd mytasklist/
$ php artisan --version
Laravel Framework 5.4.36
```



## データベース

### セットアップ
データベースを作ったら、.envの編集をして接続確認をしておく。
※MAMPの場合は、/config/database.phpでunix_socket の調整が必要。

```
'unix_socket' => '/Applications/MAMP/tmp/mysql/mysql.sock',
```

### テーブル作成
今回はUsers, Tasksが必要となる。
Usersは既にデフォルトでLaravel migrationsファイルが存在するのでそれを使う。

Tasksは下記の様にmigrationする。

```
php artisan make:migration create_tasks_table --create=tasks
```

テーブルは、以下のように定義。

```php:CreateTasksTable.php
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->index();
$table->string('name');
$table->timestamps();
});
}
```

編集したら、migration.

```
php artisan migrate
```

show tablesして, Tableが出来ていることを確認。


## 認証
Laravelでの認証は超簡単

```
php artisan make:auth
```
としたあとに、

```
php artisan migrate
```
で、必要なコントローラ・ビューが生成される。
この状態で、../register/へ行くとユーザー登録からログイン・ログアウトが可能になる。
素晴らしい。
最もシンプルな形だが、今回はこれでよしとする。



## Eloquent
### Eloquentとは
Eloquentとは優れていることを意味する形容詞。
LaravelのEloquent ModelsとはLaravelがデフォルトで持つ優れたORM(Object relational mapper)のこと。
ひとまずモデルとの紐つけ方のこととざっくり理解しておく。

### Model作成
今回はUser, Taskの２つのModelを用意することになる。 
Userはデフォルトで格納されているので、それを利用。
Taskはartisanで作ってみる。

```
php artisan make:model Task
```
これでappディレクトリに２つのファイルが生成される。

#### $fillableの定義

ここで $fillable を定義しておく。
$fillable とはホワイトリストのこと。
つまりユーザーが入力可能な項目を指す。
今回はnameだけかな。

```php:Task.php
protected $fillable = ['name'];
```
ちなみに逆のブラックリストは、$guarded。
**これらは片方のみの指定となるらしいので注意**


### Relationship

cakephpでいうhasmanyやhasoneのこと。
今回はUserは複数のTaskを持ち、TaskはUserに属する。


```php:User.php
/**
 * Get all of the tasks for the user.
 */
public function tasks()
{
return $this->hasMany(Task::class);
}
```

```php:Task.php
/**
 * Get the user that owns the task.
 */
public function user()
{
return $this->belongsTo(User::class);
}
```


## Controller
Task管理のメソッド記述のための、taskControllerを作成する。
artisanで作成できる。

```
php artisan make:controller TaskController
```

またはこれだと、index, create, store, destory, update, editが自動でついてくる。

```
php artisan make:controller TaskController --resource
```

Taskは閲覧・編集制限を設けるので、以下を追記する。

```php:TaskController.php
public function __construct()
{
$this->middleware('auth');
}
```


## Routing

ルーティングの設定は、routesのweb.phpを編集する。
多分make:authをした段階で、ルートにはhomeが割り当てられていると思うので、
今回それは残し、その後ろに追記。


```:web.php
Route::get('/tasks', 'TaskController@index');
Route::post('/task', 'TaskController@store');
Route::delete('/task/{task}', 'TaskController@destroy');
```



## Repositories
データベース操作をまとめるような印象。
主に拡張性を高めることが目的の気がする。

まずはapp/Repositories ディレクトリを作成
TaskRepository.phpを作成し、user情報取得のロジックを書く。

```php:TaskRepository.php
<?php
namespace App\Repositories;
use App\User;
use App\Task;

class TaskRepository
{
/**
 * @paramUser$user
 * @return Collection
 */
public function forUser(User $user)
{
return Task::where('user_id', $user->id)
->orderBy('created_at', 'asc')
->get();
}
}
```


これをTaskControllerクラスのindexメソッドで、この様に利用する.

```php:TaskController.php
public function index(Request $request)
{
return view('tasks.index', [
'tasks' => $this->tasks->forUser($request->user()),
]);
}
```


## Policies
Policiesとは、認可ロジックをまとめたもの。
編集権限の付与などもこれにあたるようだ。

### Policyの作成
```
php artisan make:policy TaskPolicy
```

/app/Policies/TaskPolicy が生成される。
ここでユーザーIDの整合性のチェックを行う。

```php:TaskPolicy.php
public function destroy(User $user, Task $task)
{
return $user->id === $task->user_id;
}
```

### Policyの登録
AuthServiceProviderクラスの$policiesにTaskPolicyを追加する.

```php:AuthServiceProvider.php
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
Task::class => TaskPolicy::class,
];
```

そしてTaskControllerクラスのdestoryで認証を行う。　　

```php:TaskController.php
public function destroy(Request $request, Task $task)
{
$this->authorize('destroy', $task);

// Delete The Task...
$task->delete();

return redirect('./tasks');

}
```

$this->authorize()でdestoryを呼び、通ったらdelete()すると。
以上で削除実装も完了。

ここまでで、TaskControllerは以下の様になる。

```php:TaskController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Task;
use App\Repositories\TaskRepository;


class TaskController extends Controller
{

    /**
     * The task repository instance.
     *
     * @var TaskRepository
     */
    protected $tasks;


    public function __construct(TaskRepository $tasks)
    {   
        // Taskは閲覧・編集制限を設けるので、以下を追記
        $this->middleware('auth');

        $this->tasks = $tasks;
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {   
      // RepositoriesのforUserメソッドをここで利用
        return view('tasks.index', [
            'tasks' => $this->tasks->forUser($request->user()),
        ]);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required|max:255',
        ]);

        $request->user()->tasks()->create([
            'name' => $request->name,
        ]);

        return redirect('/tasks');
    }


    /**
     * Destroy the given task.
     *
     * @param  Request  $request
     * @param  Task  $task
     * @return Response
     */
    public function destroy(Request $request, Task $task)
    {
        $this->authorize('destroy', $task);

        // Delete The Task...
        $task->delete();

        return redirect('./tasks');

    }

}
```


## ビューの作成

Task一覧、詳細、削除が出来るビューの作成

格納場所は 


```/resorces/views/tasks/index.blade.php
@extends('layouts.app')

@section('content')

    <!-- Current Tasks -->
    @if (count($tasks) > 0)
    <div class="panel-body">
        <div class="panel panel-default" >
            <div class="panel-heading">
                Current Tasks
            </div>

            <div class="panel-body">
                <table class="table table-striped task-table">

                    <!-- Table Headings -->
                    <thead>
                        <th>Task</th>
                        <th>&nbsp;</th>
                    </thead>

                    <!-- Table Body -->
                    <tbody>
                        @foreach ($tasks as $task)
                            <tr>
                                <!-- Task Name -->
                                <td class="table-text">
                                    <div>{{ $task->name }}</div>
                                </td>

                                <!-- Delete Button -->
                                <td>
                                    <form action="./task/{{ $task->id }}" method="POST">
                                        {{ csrf_field() }}
                                        {{ method_field('DELETE') }}

                                        <button>Delete Task</button>
                                    </form>
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    @endif


    <!-- Bootstrap Boilerplate... -->

    <div class="panel-body">
        <!-- Display Validation Errors -->
        @include('common.errors')

        <!-- New Task Form -->
        <form action="./task" method="POST" class="form-horizontal">
            {{ csrf_field() }}

            <!-- Task Name -->
            <div class="form-group">
                <label for="task-name" class="col-sm-3 control-label">Task</label>

                <div class="col-sm-6">
                    <input type="text" name="name" id="task-name" class="form-control">
                </div>
            </div>

            <!-- Add Task Button -->
            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-6">
                    <button type="submit" class="btn btn-default">
                        <i class="fa fa-plus"></i> Add Task
                    </button>
                </div>
            </div>
        </form>
    </div>

    <!-- TODO: Current Tasks -->
@endsection

```

エラー表示用

```/resorces/views/common/errors.php
@if (count($errors) > 0)
    <!-- Form Error List -->
    <div class="alert alert-danger">
        <strong>Whoops! Something went wrong!</strong>

        <br><br>

        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
```