Try T.M Engineer Blog

多摩市で生息するエンジニアが「アウトプットする事は大事だ」と思って始めたブログ

(Laravel第2回目)MVCモデルを使った一通りの流れを理解した話

今回はMVCモデルを使った一通りの流れを学んだので、その復習(アウトプット)をしたいと思います。 参考にしたサイトは以下。

www.hypertextcandy.com

このサイトは@kuriharaさんに教えていただきました。
LaravelでToDoリストの作り方を非常に丁寧なドキュメントで書かれているサイトです。 とても分かりやすい。。。

このようなサイトを教えていただき、@kuriharaさんありがとうございますm( )m
今回は上記サイトの(4)までをやってみたので、ここまでを振り返りたいと思います。

(4)の作成時点

フォルダ毎にToDoリストがある感じです。

f:id:special-moucom:20190709233215p:plain

LaravelでのMVCの流れ

前回、MVCのデータの流れは、Controller→Model→Viewの順になると学びました。 それをLaravelで表現すると以下のようになります。

f:id:special-moucom:20190709233353p:plain

ControllerとViewの背面にRoutesが入ります。 このRoutesを使って、HTTPリクエストを特定のControllerへ渡したり、直接Viewを呼び出したりすることができます。
Routesをコードで書くと以下の通り。

■ Routes
// HTTPリクエストをTaskControllerへ渡す
// ->name('tasks.index')は、このURLの名前
Route::get('/folders/{id}/tasks', 'TaskController@index')->name('tasks.index');

// 直接Viewを呼び出す
Route::get('/', function () {
    return view('welcome');
});

上記のRouteのgetは、以下のTaskControllerのindexを呼び出しています。

■ TaskController
namespace App\Http\Controllers;

use App\Folder;
use App\Task;

class TaskController extends Controller
{
    public function index(int $id)
    {
        // ① (DB)Folderテーブルから全件取得
        $folders = Folder::all();

        // ② HTTPリクエストから取得した$idをキーに(DB)Folderテーブルから検索
        $current_folder = Folder::find($id);
        
        // ③ (DB)Taskテーブルから②と一致するデータを取得
        $tasks = Task::where('folder_id', $current_folder->id)->get();

        // ④ ①と③の結果と$idをViewへ渡す
        return view('tasks/index', [
            'folders' => $folders,
            'current_folder_id' => $current_folder->id,
            'tasks' => $tasks,
        ]);
    }
}

①〜③で、DBへのアクセスと④でデータをViewへ渡しているのがわかります。 Modelを呼び出してないように見えますが、「Folder::all」「Task::where」でちゃんと呼び出しています。

■ Model(Task)
namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    const STATUS = [
        1 => [ 'label' => '未着手', 'class' => 'label-danger' ],
        2 => [ 'label' => '着手中', 'class' => 'label-info' ],
        3 => [ 'label' => '完了', 'class' => '' ],
    ];

    // アクセサ
    public function getStatusLabelAttribute()
    {

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

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

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

    public function getStatusClassAttribute()
    {
        $status = $this->attributes['status'];

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

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

Modelを見て、Folder::all(allメソッド)Task::where(whereメソッド)が見つからない事に気づくと思います。
これは実はModel(Task)の継承先のIlluminate\Database\Eloquent\Modelから呼んでいるIlluminate\Database\Query\Builderのallとwhereメソッドを呼んでいます。(ふ、深い・・・)

つまり、Modelに処理を何も書かなくても、Modelを呼ぶだけでQueryBuilder(allとかwhereとかDB検索に使用できるメソッド)が使用できます。これは便利!
Modelのアクセサの説明はViewでします。

■ View一部抜粋
<div class="list-group">
    @foreach($folders as $folder)
        <a href="{{ route('tasks.index', ['id' => $folder->id]) }}" 
        class="list-group-item {{ $current_folder_id === $folder->id ? 'active' : '' }}">
        {{ $folder->title }}
        </a>
    @endforeach
</div>


@foreach($tasks as $task)
    <tr>
    <td>{{ $task->title }}</td>
    <td>
    <span class="label {{ $task->status_class }}">{{ $task->status_label }}</span>
    </td>
    <td>{{ $task->due_date }}</td>
    <td><a href="#">編集</a></td>
    </tr>
@endforeach

データを表示するところのViewです。
Laravelは、Bladeと呼ばれるテンプレートエンジンを採用しています。@foreachでforeachが使えたり、phpを{{ hoge }}で出力できたり等、とても便利です。

基本的にControllerを使って取得したデータを表示するだけですが、ポイントが2つあります。

  • URLのPathはRoutesで定義した名前を使う!
  • アクセサ

URLのPathはRoutesで定義した名前を使う!

本来、href=の後にはURLを指定するのですが、Routesでnameを指定しておくと、以下のように書くことができます。

<a href="{{ route('tasks.index', ['id' => $folder->id]) }}"

こうすることで、URLを誤って書くことが少なくなります。また、名前に誤りがあれば、PHP側でエラーがでます。

アクセサ

以下を見てください。

{{ $task->status_label }}

ControllerからViewへデータを渡す時に、$taskを渡しました。 $taskはDBから取得したデータなのですが、実はDBには「status_class」や「status_label」といったデータは入っていません。 これはModelにあるアクセサ「getStatusClassAttribute」と「getStatusLabelAttribute」を指しています。

アクセサメソッドはキャメルケース(文字の区切りが大文字)ですが、プロパティとして参照するときは(getとAttributeを除いた)スネークケース(文字の区切りがアンダースコア)で参照することができます。
DBから取得したデータを加工したいときに使えるので便利っ!!

最後に

LaravelのMVCモデルをつかって、一通りの流れを学びました。
Rails以来、久しぶりにモダンなフレームワークに触れたのですが、やっぱりバックエンドって楽しいですよね。
自分自身の理解も兼ねて引き続きアウトプットしていきたいと思います。( = = )ノ