【Laravel】Web開発初心者が1週間で入門チュートリアルを実施した記録(4)


以下の続き。

https://qiita.com/yotty19911207/items/5b10aece3c01bc0e1779

フォルダ一覧までは作成できた。
■(1)〜(3)の復習：
・環境構築と設計
・開発のざっくり流れ
①ルーティング設定(web.php)
②コントローラーの作成・記載
③マイグレーションファイルの作成・記載
④マイグレーションの実行
⑤モデルクラスの作成
⑥テストデータの挿入
⑦コントローラーの追記・修正
⑧テンプレートの作成
・ルーティング設定(web.php)の記載方法

    Route::get('/folders/{id}/tasks', 'TaskController@index')->name('tasks.index');

→getで/folders/{id}/tasksにリクエストがきたら、TaskControllerのindexメソッドを呼び出す。
　　これを'tasks.index'と名づける。

・コントローラーの生成コマンド

    php artisan make:controller (コントローラーの名前)

・マイグレーションファイルの生成コマンド

    php artisan make:migration create_(テーブル名)_table --create=(テーブル名)

→database/migrations ディレクトリに YYYY_MM_DD_hhmmss_create_folders_table.phpが生成される。

・マイグレーションの実行コマンド

    php artisan migrate


・モデルクラスの生成コマンド

    php artisan make:model (モデル名)

・View関数の使い方

    public function index ()
    {
      $var = 'XXX';
    
        return view('aaa', ['varvar' => $var]);
    }

上記のコードのように書けば「resources/views/aaa.blade.php」ビューへ、'varvar'という変数名で$varを渡すことができます。第二引数は"['渡す先での変数名' => 今回渡す変数]"。

・route関数の使い方

        @foreach($folders as $folder)
          <a href="{{ route('tasks.index', ['id' => $folder->id]) }}" class="list-group-item">
            {{ $folder->title }}
          </a>
        @endforeach

route関数の第一引数はルート名、第二引数は、ルート URL のうち変数になっている部分（ここでは {id}）に実際の値を埋める）。

'tasks.index'は以下のルートで、{id}にはループで回しているfolderのidが入る。

    Route::get('/folders/{id}/tasks', 'TaskController@index')->name('tasks.index');

ここまでが(1)~(3)の復習。


<h1>入門Laravelチュートリアル (4) ToDoアプリのタスク一覧表示機能を作る</h1>
フォルダ一覧の次はタスク一覧をつくった。

①、②は済みのため、Taskテーブルのマイグレーションファイル作成からスタート。

①ルーティング設定(web.php)
→記載済み。
②コントローラーの作成・記載
→TaskController作成済み
③マイグレーションファイルの作成・記載
④マイグレーションの実行
⑤モデルクラスの作成
⑥テストデータの挿入
⑦コントローラーの追記・修正
⑧テンプレートの作成


③マイグレーションファイルの作成・記載

    php artisan make:migration create_tasks_table --create=tasks

上記コマンドで生成されたマイグレーションファイルをテーブル定義に従って修正。

    // 外部キーを設定する
    $table->foreign('folder_id')->references('id')->on('folders');

外部キー制約について。
上記では、タスクテーブルのフォルダIDカラムにフォルダテーブルのIDカラムへの外部キー制約を設定。

今回、一つのフォルダに複数のタスクが紐づく。

フォルダ1
|
|--- タスク1
|
|--- タスク2
|
|--- タスク3

データの不整合を防ぐために、タスクテーブルのフォルダIDには、勝手に値を入れるのではなく、フォルダテーブルのIDとして存在する値をいれなければいけない。

タスクテーブル
|カラム論理名	|カラム物理名|	型	|型の意味|
|:----------|--------:|:-----|-------:|
|ID         |id      |SERIAL|	連番（自動採番）|
|フォルダID  |folder_id|	INTEGER	|数値|
・・・

フォルダーテーブル
|カラム論理名	|カラム物理名|	型	|型の意味|
|:----------|--------:|:-----|-------:|
|ID         |id      |SERIAL|	連番（自動採番）|
・・・


④マイグレーションの実行

    php artisan migrate

⑤モデルクラスの作成

    php artisan make:model Task

⑥テストデータの挿入
シーダーでtasksテーブルにデータを挿入。
tinkerでちゃんとテーブルにデータが入ったか、確認。

⑦コントローラーの追記・修正

タスク取得処理を追加。

    // 選ばれたフォルダに紐づくタスクを取得する
    $tasks = Task::where('folder_id', $current_folder->id)->get();

Laravel が提供するクエリビルダの機能を使用。
SQL を書かなくても PHP 風な記述でデータ操作を表現できる。
SQL は裏側で生成されデータベースに発行。
get メソッドを忘れがちなので気をつける。

    return view('tasks/index', [
        'folders' => $folders,
        'current_folder_id' => $current_folder->id,
        'tasks' => $tasks,
    ]);

コントローラーからtaskの情報をとれるようになったので、テンプレート（ビュー）にtaskの情報を渡すように追記。




⑧テンプレートの作成

コントローラーからtaskの情報を受け取ったので、一覧画面に「resources/views/tasks/index.blade.php」フォルダ一覧だけでなくタスク一覧も表示するように記載を追加。

タスク追加ボタン、タスク一覧が追加されるが、現時点でタスク追加や編集はできないので、href="#"としている。


ここまででフォルダ一覧とタスク一覧の表示はできたが、状態や日付形式を見直しするためにLaravelのモデルに関する機能である「アクセサ」を用いてブラッシュアップ。


⑨Taskモデルにアクセサを追加

・・・ちょっと難しかったが現時点での理解を記載。

タスクテーブル
|カラム論理名	|カラム物理名|	型	|型の意味|
|:----------|--------:|:-----|-------:|
|ID         |id      |SERIAL|	連番（自動採番）|
|フォルダID  |folder_id|	INTEGER	|数値|
|タイトル	|title	|VARCHAR(100)	|100文字までの文字列|
|状態	|status	|INTEGER	|数値|
|期限日	|due_date	|DATE	|日付|
|作成日	|created_at	|TIMESTAMP	|日付と時刻|
|更新日	|updated_at	|TIMESTAMP	|日付と時刻|

アクセサとは：
ModelにgetHogeHogeAttribute()メソッドを定義することで、コントローラーやテンプレートで${{hoge_hoge}}としてDBの情報を加工して簡単に表示することができる。


    <<Task.php>>
    class Task extends Model
    {
    /**
     * 状態定義
     */
    const STATUS = [
        1 => [ 'label' => '未着手' ],
        2 => [ 'label' => '着手中' ],
        3 => [ 'label' => '完了' ],
    ];

    /**
     * 状態のラベル
     * @return string
     */
    public function getStatusLabelAttribute()
    {
        // 状態値
        $status = $this->attributes['status'];


        // 定義されていなければ空文字を返す
        if (!isset(self::STATUS[$status])) {
            return '';
        }

        return self::STATUS[$status]['label'];
        }
    }


以下でTasksテーブルのstatusカラムを取得(数値1~3が取れる想定)。

    $status = $this->attributes['status'];

取得したstatusの値が数値1~3でない場合、空文字を返す。

        // 定義されていなければ空文字を返す
        if (!isset(self::STATUS[$status])) {
            return '';
        }

取得したstatusの値が数値1~3の場合、'未着手'/'着手中'/'完了'を返却する。

        return self::STATUS[$status]['label'];
        }


Modelにて上記の設定ができたら、コントローラーやテンプレートにて、'未着手'/'着手中'/'完了'を取得できる。
Modelで定義した関数がgetStatusLabelAttribute()だったので、コントローラーやテンプレートで取得する際は、以下のようにする。

        <span class="label">{{ $task->status_label }}</span>

これで、状態を数値でなく'未着手'/'着手中'/'完了'で表示できるようになった。


ちなみに、この「self::」とはなんだろうか？？「self::」は自分のクラスを表しthisは自分のインスタンスを表す。静的なプロパティにアクセスする際は、「self::」を使用し、動的なプロパティやメソッドにアクセスする際は、thisを使用する。



https://techacademy.jp/magazine/29453


続いて、状態に応じて色を付ける。
アクセサとBootstrapをうまく活用していてすごいと思った。

http://bootstrap3.cyberlab.info/components/labels.html

上記の通り、Bootstrapでclassに定義することで、色をつけることができる。


    /**
     * 状態定義
     */
    const STATUS = [
        1 => [ 'label' => '未着手', 'class' => 'label-danger' ],
        2 => [ 'label' => '着手中', 'class' => 'label-info' ],
        3 => [ 'label' => '完了', 'class' => '' ],
    ];

    /**
     * 状態を文字で表示する
     * @return string
     */
    public function getStatusLabelAttribute()
    {
        // 状態値
        $status = $this->attributes['status'];

        // 定義されていなければ空文字を返す
        if (!isset(self::STATUS[$status])) {
            return '';
        }

        return self::STATUS[$status]['label'];
    }

     /**
     * 状態に色を付ける
     * @return string
     */
    public function getStatusClassAttribute()
    {
        // 状態値
        $status = $this->attributes['status'];

        // 定義されていなければ空文字を返す
        if (!isset(self::STATUS[$status])) {
            return '';
        }

        return self::STATUS[$status]['class'];
    }


STATUS定義にBootstrapを踏まえたclass定義を追加し、ステータスカラムに応じたclassを返すメソッドを追加。
テンプレート側で以下の通り定義することで、色づけすることができる。

    <span class="label {{ $task->status_class }}">{{ $task->status_label }}</span>



⑩モデルクラスにおけるリレーション

タスクコントローラーではタスクの一覧を以下のコードで取得した。

    $tasks = Tasks::where('folder_id', $current_folder->id)->get();

ここで紹介する機能を使うとこのように書き直すことができる。（めっちゃ便利・・・！）

    $tasks = $current_folder->tasks()->get();


上でも記載の通り、今回、一つのフォルダに複数のタスクが紐づく。
これを踏まえて、Folderクラスに以下の記載を加える。


    <?php

    namespace App\Models;

    use Illuminate\Database\Eloquent\Model;

    class Folder extends Model
    {
        public function tasks()
        {
            return $this->hasMany('App\Task');
        }
    }

実際に、タスクコントローラーの記載を以下のように修正する。

    // 選ばれたフォルダに紐づくタスクを取得する
    $tasks = Task::where('folder_id', $current_folder->id)->get();


長くなったので、(4)までで一旦、区切る。