Laravelでタスクアプリを作る

![202006091556.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643170/8a51557c-9d0e-14a0-edc0-ff9a29b0eef1.png)

下記の「Laravel 5.2 中級者向けタスクリスト」を、Laravel 7以上での作成する方法をご紹介します。ボリュームが多いので、解説は少なめです。認証機能以外はほぼ同じです。

https://readouble.com/laravel/5.2/ja/quickstart-intermediate.html

##作業環境
OS：Windows 10 HOME Edition（ver.2004）
Laravel：7.14.1
Xampp：7.4.6
Composer：1.10.7
Node.js：13.9.0

##①Laravelのインストール
###Composerをインストール
Laravelをインストールするには、Composerというソフトが必要です。
Composerはプログラムの依存関係を解消できるソフトで、簡単に説明するとLaravelを動かすために必要な部品をComposerがまとめて用意してくれる、便利なソフトです。
https://getcomposer.org/
上記のページにアクセスすると、下記のような画面が出てきます。
![202006031927.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643170/690142cf-e3eb-53b3-7de6-36da48fd05ce.png)
「Download」を選び、ダウンロードしたファイルをダブルクリックをして、全て「Next」を選んでインストールしておきます。

###Windows PowerShell（管理者）からLaravelをインストールする
PowerShellを管理者権限で実行し、以下のコマンドを打ち、Xamppのhtdocsのフォルダまで移動します。

```PowerShell
cd C:\xampp\htdocs
```

次に以下のコマンドを打ち、Laravelをインストールします。

```PowerShell
composer create-project laravel/laravel laravel_todo --prefer-dist
```

「laravel_todo」はフォルダ名ですので、好きなものを指定してください。
「--prefer-dist」と指定すると解凍したフォルダをダウンロードするので、インストールの時間が短くなります。

```PowerShell
Application key set successfully.
```

上記の表示が出てくれば、Laravelの最新版がインストールが完了です。
また、バージョンを指定した場合は以下のように入力します。

```PowerShell
composer create-project laravel/laravel laravel_todo --prefer-dist "6.0.*'
```

この場合だと、Laravelの「6.0.＊」の「＊」の部分の数字が一番最新のものがインストールされます。

###Laravelの簡易サーバーを実行する
PowerShellで先程作ったフォルダに移動します。

```PowerShell
cd laravel_todo
```

以下のコマンドを打ち、Larabelの簡易サーバーを起動します。

```PowerShell
php artisan serve
```

XamppのApecheとMySQLを起動して、http://127.0.0.1:8000/ にアクセスし、下記の画面が表示されていればOKです。
![202006090953.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643170/ecce3f78-4f7f-5322-cd06-482c824fbd1a.png)
※簡易サーバーの実行を止めるには、PowerShellで「Ctrl」＋「C」ボタンを押すと止めることができます。
**※PowerShellを2つ起動し、1つで簡易サーバーを実行し、残りでコマンドを打っていくのがおすすめです。**

##②Laravelの初期設定
Laravelの初期設定を行います。
###Laravelのタイムゾーンと言語設定
Laravelのタイムゾーンと言語設定を行います。
「config」フォルダの中の「app.php」をエディタで開き、該当箇所を下記のように変更します。

```config/app.php
70行目　'timezone' => 'Asia/Tokyo',
83行目　'locale' => 'ja',
```

###データベースの言語設定
データベースの言語設定を行います。
「config」フォルダの中の「database.php」をエディタで開き、該当箇所を下記のように変更します。

```config/database.php
55行目　'charset' => 'utf8',
56行目　'collation' => 'utf8_unicode_ci',
```

###デバックバーのインストール
デバックバーをPowerShellから下記のコマンドを実行してインストールします。

```powershell
composer require barryvdh/laravel-debugbar
```

簡易サーバーを起動して、http://127.0.0.1:8000/ にアクセスし、下記の画面が表示されていればOKです。
![202006091035.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643170/dd81c271-87c4-382f-1378-1d5686835484.png)

デバックバーを非表示するには「.env」ファイルを下記のように書き換えます。

```.env
4行目 APP_DEBUG=false
```

##③データベースの準備

###データベースの設定
xamppからphpMyAdminを起動してデータベースとユーザーを設定し、設定した情報を「.env」ファイルに書きこみます。

```.env
4行目 DB_CONNECTION=mysql
5行目 DB_HOST=127.0.0.1
6行目 DB_PORT=3306
7行目 DB_DATABASE=laravel_todo
8行目 DB_USERNAME=ユーザー名
9行目 DB_PASSWORD=パスワード
```

今回は「laravel_todo」というデータベース名にしました。ユーザーを設定しない場合は、Xamppの場合は下記のままでOKです。

```.env
4行目 DB_CONNECTION=mysql
5行目 DB_HOST=127.0.0.1
6行目 DB_PORT=3306
7行目 DB_DATABASE=laravel_todo
8行目 DB_USERNAME=root
9行目 DB_PASSWORD=
```

###データベースマイグレーション
Userテーブルとtasksテーブルを作ります。
Laravelではコマンドを実行することで、テーブルを作れる仕組みになっています。
UserテーブルはLaravelのデフォルトのファイルを使用します。tasksテーブルは、PowerShellで以下のコマンドを実行して作成します。

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

「database」フォルダ→「migration」フォルダ→「2020_06_09_111346_create_tasks_table.php」を開きます。
※ファイル名は、作成した日付が先頭に入るので、違うファイル名になりますが問題ありません。
下記のように変更して保存します。

```2020_06_09_111346_create_tasks_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTasksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('tasks', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('user_id')->unsigned()->index();
            $table->string('name');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('tasks');
    }
}
```

これでtasksテーブルを作る準備ができたので、下記のコマンドを実行し、テーブルを作成します。

```powershell
php artisan migrate
```

phpMyAdminを確認すると、下記のようなテーブルが作成されています。
![202006091126.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643170/dce597b6-4c93-c3de-8b6d-1af78119c5b2.png)

###Eloquentモデル
UserモデルとTaskモデルを作成します。
今回はUserモデルはLaravelが最初から用意してものを使用するので、Taskモデルを作ります。以下のコマンドを実行します。

```powershell
php artisan make:model Task
```

「app」フォルダに「Task.php」が作成されるので、以下のように書き換えます。

```app/Task.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    /**
     * 複数代入を行う属性
     *
     * @var array
     */
    protected $fillable = ['name'];
}
```

###Eloquentリレーション
これでModelが定義できたので、関連付けを行います。
最初にUserモデルに対するtasksリレーションを定義します。「app」フォルダに「User.php」を下記のように追記します。

```app/User.php
<?php

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    // 他のEloquentプロパティー…

    /**
     * 特定ユーザーの全タスク取得
     */
    public function tasks()
    {
        return $this->hasMany(Task::class);
    }
}
```

次に、Taskモデルに対するuserリレーションを定義します。「app」フォルダに「Task.php」を下記のように追記します。

```app/Task.php
<?php

namespace App;

use App\User;
use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    /**
     * 複数代入を行う属性
     *
     * @var array
     */
    protected $fillable = ['name'];

    /**
     * タスク所有ユーザーの取得
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
```

以上でリレーションができたので、コントローラーを作成します。


##④ルーティング

###認証
Laravel6以降は「php artisan make:auth」というコマンドが使用できないので、[公式マニュアル](https://readouble.com/laravel/7.x/ja/frontend.html)を参照して、以下の手順で認証機能を有効化します。
まずは、次のコマンドを実行し、laravel/uiパッケージをインストールします。

```powershell
composer require laravel/ui
```

laravel/uiパッケージをインストールできたら、ui Artisanコマンドを使い、ログイン／ユーザー登録スカフォールドを生成します。今回はbootstrapを使用しますが、vueとreactを使用することもできます。

```powershell
php artisan ui bootstrap --auth
```

CSSとJavaScriptをLaravelで生成するために、必要なパッケージをNodeプロジェクトマネージャー(NPM)を使用してインストールします。（Node.jsのインストールが必要です）

```powershell
npm install
```

これで準備ができたので、以下のコマンドを実行すると、CSSとJSが生成され、ログイン機能を実装することができます。

```powershell
npm run dev
```

実行が終わると、「node_modules」フォルダにたくさんのファルダやファイルが追加されます。例えば、http://127.0.0.1:8000/login にアクセスすると画面が表示されるようになります。

次に認証関連の機能を変更していきます。「app」フォルダ→「Http」フォルダ→「Controllers」フォルダ→「Auth」フォルダの中にある「LoginController.php」を以下のように修正します。


```app/Http/Controllers/Auth/LoginController.php
29行目 protected $redirectTo = '/tasks';
```

同じように、「app/Http/Middleware/RedirectIfAuthenticated.php」ファイルのリダイレクトパスも変更します。

```app/Http/Middleware/RedirectIfAuthenticated.php
22行目 return redirect('/tasks');
```

###タスクコントローラー

タスクを取得したり、保存したりする必要がありますので、次のコマンドを実行し、TaskControllerを作成します。

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

これでコントローラーが生成できました。続いてこのコントローラーを実行するルートを、次のコマンドを実行して作成します。

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

続いて、このコントローラーを実行するルートを「routes/web.php」に以下のように変更します。

```routes/web.php
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

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

作成された「app/Http/Controllers/TaskController.php」を以下のように記述します。

```app/Http/Controllers/TaskController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class TaskController extends Controller
{
    /**
     * 新しいコントローラインスタンスの生成
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }
}
```

##⑤レイアウトとビューの構築

###子ビューの定義

TaskControllerのindexメソッドに対応する、「resources/views/tasks/index.blade.php」を作成し、以下の内容を記述します。

```resources/views/tasks/index.blade.php
<!-- resources/views/tasks/index.blade.php -->

@extends('layouts.app')

@section('content')

    <!-- Bootstrapの定形コード… -->

    <div class="panel-body">
        <!-- バリデーションエラーの表示 -->
        @include('common.errors')

        <!-- 新タスクフォーム -->
        <form action="{{ url('task') }}" method="POST" class="form-horizontal">
            {{ csrf_field() }}

            <!-- タスク名 -->
            <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>

            <!-- タスク追加ボタン -->
            <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
```

次にTaskControllerのindexメソッドを作成し、このビューを返すようにします。

```app/Http/Controllers/TaskController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class TaskController extends Controller
{
    /**
     * 新しいコントローラインスタンスの生成
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }

    /**
     * ユーザーの全タスクをリスト表示
     *
     * @param  Request  $request
     * @return Response
     */
    public function index(Request $request)
    {
        return view('tasks.index');
    }
}
```

##⑥タスク追加
###バリデーション
TaskControllerにstoreメソッドを追加します。

```app/Http/Controllers/TaskController.php
/**
 * 新タスク作成
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request)
{
    $this->validate($request, [
        'name' => 'required|max:255',
    ]);

    // タスクの作成処理…
    $request->user()->tasks()->create([
        'name' => $request->name,
    ]);

    return redirect('/tasks');
}
```

###$errors変数
「resources/views/common/errors.blade.php」を作成し、以下の内容を記述します。

```resources/views/common/errors.blade.php
<!-- resources/views/common/errors.blade.php -->

@if (count($errors) > 0)
    <!-- フォームのエラーリスト -->
    <div class="alert alert-danger">
        <strong>おや？何かがおかしいようです！</strong>

        <br><br>

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

##⑦既存タスク表示

既存の全タスクをビューに渡すように、TaskController@indexメソッドを編集します。

```app/Http/Controllers/TaskController.php
/**
 * ユーザーの全タスクをリスト表示
 *
 * @param  Request  $request
 * @return Response
 */
public function index(Request $request)
{
    $tasks = $request->user()->tasks()->get();

    return view('tasks.index', [
        'tasks' => $tasks,
    ]);
}
```

###依存注入
TaskRepositoryを定義します。「app/Repositories」フォルダを作成し、TaskRepositoryクラスを追加します。

```app/Repositories/TaskRepository.php
<?php

namespace App\Repositories;

use App\User;

class TaskRepository
{
    /**
     * 指定ユーザーの全タスク取得
     *
     * @param  User  $user
     * @return Collection
     */
    public function forUser(User $user)
    {
        return $user->tasks()
                    ->orderBy('created_at', 'asc')
                    ->get();
    }
}
```

TaskControllerに下記のように編集と追記をします。

```app/Http/Controllers/TaskController.php
<?php

namespace App\Http\Controllers;

use App\Task;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Repositories\TaskRepository;

class TaskController extends Controller
{
    /**
     * タスクリポジトリーインスタンス
     *
     * @var TaskRepository
     */
    protected $tasks;

    /**
     * 新しいコントローラーインスタンスの生成
     *
     * @param  TaskRepository  $tasks
     * @return void
     */
    public function __construct(TaskRepository $tasks)
    {
        $this->middleware('auth');

        $this->tasks = $tasks;
    }

    /**
     * ユーザーの全タスクをリスト表示
     *
     * @param  Request  $request
     * @return Response
     */
    public function index(Request $request)
    {
        return view('tasks.index', [
            'tasks' => $this->tasks->forUser($request->user()),
        ]);
    }
}
```


###タスク表示
タスクを表示するため、「tasks/index.blade.php」に以下のように追記をします。

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

@section('content')
    <!-- タスクフォームの作成… -->

    <!-- 現在のタスク -->
    @if (count($tasks) > 0)
        <div class="panel panel-default">
            <div class="panel-heading">
                Current Tasks
            </div>

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

                    <!-- テーブルヘッダ -->
                    <thead>
                        <th>Task</th>
                        <th>&nbsp;</th>
                    </thead>

                    <!-- テーブル本体 -->
                    <tbody>
                        @foreach ($tasks as $task)
                            <tr>
                                <!-- タスク名 -->
                                <td class="table-text">
                                    <div>{{ $task->name }}</div>
                                </td>

                                <td>
                                    <!-- TODO: 削除ボタン -->
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>
    @endif
@endsection
```

##⑧タスク削除
###削除ボタンの追加
「tasks/index.blade.php」に以下を追記

```tasks/index.blade.php
<tr>
    <!-- タスク名 -->
    <td class="table-text">
        <div>{{ $task->name }}</div>
    </td>

    <!-- 削除ボタン -->
    <td>
        <form action="{{ url('task/'.$task->id) }}" method="POST">
            {{ csrf_field() }}
            {{ method_field('DELETE') }}

            <button type="submit" id="delete-task-{{ $task->id }}" class="btn btn-danger">
                <i class="fa fa-btn fa-trash"></i>削除
            </button>
        </form>
    </td>
</tr>
```

###ポリシーを作る

PowerShellで以下のコマンドを実行して、TaskPolicyを作成します。

```PowerShell
php artisan make:policy TaskPolicy
```

作成された「app/Policies/TaskPolicy.php」を以下のように編集します。

```app/Policies/TaskPolicy.php
<?php

namespace App\Policies;

use App\User;
use App\Task;
use Illuminate\Auth\Access\HandlesAuthorization;

class TaskPolicy
{
    use HandlesAuthorization;

    /**
     * 指定されたユーザーが指定されたタスクを削除できるか決定
     *
     * @param  User  $user
     * @param  Task  $task
     * @return bool
     */
    public function destroy(User $user, Task $task)
    {
        return $user->id === $task->user_id;
    }
}
```

最後に「app/Providers/AuthServiceProvider.php」ファイルの$policiesプロパティに一行加えることで、TaskモデルをTaskPolicyと関連付けします。

```app/Providers/AuthServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * アプリケーションにマップするポリシー
     *
     * @var array
     */
    protected $policies = [
      'App\Task' => 'App\Policies\TaskPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        //
    }
}
```

###タスク削除
タスクを削除する機能をTaskControllerに記述します。

```app/Http/Controllers/TaskController.php
/**
 * 指定タスクの削除
 *
 * @param  Request  $request
 * @param  Task  $task
 * @return Response
 */
public function destroy(Request $request, Task $task)
{
    $this->authorize('destroy', $task);

    $task->delete();

    return redirect('/tasks');
}
```

###見た目を整える
5年前のチュートリアルでBootstrapの記述が古いので、お好みで変更を適宜加えます。

![202006092052.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643170/45695ce6-da45-0341-ff90-642e73db302d.png)

以上で完成です。お疲れさまでした。

##参考ページ
https://readouble.com/laravel/5.2/ja/quickstart-intermediate.html

https://readouble.com/laravel/7.x/ja/frontend.html

https://qiita.com/daisu_yamazaki/items/a914a16ca1640334d7a5