Laravelでポートフォリオ作り vol.8【データ詳細表示編】

今回もポートフォリオを進めていきます。
今回はデータ詳細画面を作成します。
まだモックアップも作成していませんが、今回は総復習も兼ねて一気に作成していきます。
新しい部分もあるので、しっかりと押さえていきましょう。

前回の記事

ルーティングの定義

まずはルーティングの定義をしていきます。
今回は生徒一覧の下に追加しました。「detail」という名前で定義しています。
※順番は機能に関係ありませんが、いつも一覧の下に詳細を用意しているので、そのように定義してみました。個人の好みです。

// 以降のルーティングはこの下に書いていく
Route::get('/student', [StudentController::class, 'index'])->name('student.index'); // 生徒一覧
Route::get('/student/{id}/detail', [StudentController::class, 'detail'])->name('student.detail'); // 生徒詳細
Route::get('/student/create', [StudentController::class, 'create'])->name('student.create'); // 生徒登録
Route::post('/student/store', [StudentController::class, 'store'])->name('student.store'); // 生徒登録処理

さて、さっそく見慣れない記述がでてきましたね。ルーティングのURL部分に「’/student/{id}/detail’」といった形で「{id}」と記述されています。
これは、URLにパラメータを設定するための記述でルートパラメータと呼ばれます。。
例えばURLが「/student/1/detail」となった場合、id=1として扱うことが出来るようになります。
実際にどのように扱うかはControllerの実装で見ていきましょう。
このページの「ルートパラメータ」で詳細が確認できるので、詳細を知りたい方は読んでみましょう。
まずは動きを見てから、詳細を読んだ方が理解しやすいかもしれません。

Controllerの処理を記述

続いて、Controllerにdetailメソッドを定義して処理を記述していきます。
とりあえずこんな感じで定義しておきましょう。

<?php

namespace App\Http\Controllers;

use App\Models\Student;
use Illuminate\Http\Request;

class StudentController extends Controller
{
    /**
     * 一覧画面
     */
    public function index()
    {
    省略
    }
    
    public function detail($id){
        dd($id);
    }

    省略
}

ポイントは引数の名前ををルートパラメータの名前と合わせる部分です。
今回のdetailメソッドのURLでは「/student/{id}/detail」とidというルートパラメータを定義しているため、引数もidという名前で定義する必要があります。

ルーティングの確認

それではルーティングの確認をしていきます。
まず、一覧から詳細画面に遷移するためのURLを記述します。
前回までの実装でリンクの準備はできているのでURLの設定を行うのみとなります。
indexメソッドで作成している生徒情報でURLを設定します。
※student_detail_urlの部分で設定しています

public function index()
{
    // studentsテーブルからデータを取得
    $db_students = Student::get();

    // 画面に返却する値を定義
    $students = [];
    foreach ($db_students as $db_student){
        $students[] = [
            'student_name' => $db_student->student_sei . $db_student->student_mei,  // 生徒名
            'kikan_from' => $db_student->student_from_date,  // 期間(開始日)
            'kikan_to' => $db_student->student_to_date,  // 期間(終了日)
            'student_memo' => $db_student->student_memo,  // 生徒メモ
            'student_detail_url' => route('student.detail', ['id' => $db_student->id]),  // 生徒詳細URL
            'student_mendan_url' => '',  // 面談URL
        ];
    }

    return view('student.index', [
        'students' => $students,
    ]);
}

student_detail_urlに設定をしました。
routeメソッドを利用してURLの設定をしているのはこれまでと同じですが、今回はルートパラメータに値の設定をする必要があります。ルートパラメータの値はrouteメソッドの第二引数に配列の形で定義します。配列のキーがパラメータ名となり、対応する値を設定します。
今回のルートパラメータのidに生徒情報の主キーであるidの値を設定したいので、[‘id’ => $db_student->id]と記述します。

一覧画面を表示して、開発者モードで「生徒詳細」のリンクを確認すると
http://127.0.0.1:8000/student/1/detailのように、URLが設定されていることが分かります。
「1」の部分はDBレコードのidとなっているので、生徒ごとに異なっていることも確認しておきましょう。

生徒詳細リンクをクリックすると、遷移先の画面でidがddによって出力されることが確認できます。

このように遷移先の画面に値を渡すことができます。
今回は詳細画面で表示する生徒情報を特定するために、一覧画面から生徒のIDを渡しているということですね。

もちろん、登録処理の時に使用したRequestを使用することもできます。
必要に応じて使い分けましょう。
データのIDを渡す程度であればルートパラメータが良いですが、登録処理や検索処理のように多くの情報を渡す場合はルートパラメータは向いていませんね。

詳細画面の処理を記述する

続いて詳細画面の処理について考えていきます。
詳細画面では指定の1レコードの情報を表示するという機能が求められます。
そのため、レコードを識別するための主キーをルートパラメータで受け取れるように実装を行いました。

主キーの値が分かっているため、この主キーを使用してレコードを取得すれば必要な情報が取得できます。
Modelクラスには「find」という便利なメソッドが実装されており、主キーであるidの値を渡して実行することで、対応するレコードをModelインスタンスで返却してくれます。
つまりこんなソースになりますね。

public function detail($id){
    $student = Student::find($id);
    dd($student);
}

処理結果を見てみましょう。

単純なidのddから、取得したレコードに変更したためdd結果が複雑になりましたね。
簡単に指定したレコードが取得できるfindメソッドは是非とも覚えておきましょう。

しかし、もっと簡単に取得する方法があります。
こちらのソースを見てください。

public function detail(Student $id){
    dd($id);
}

引数の$idの前にModelクラスである「Student」を指定しています。
これにより、$idはStudentクラスと指定ができます。
この指定をするとLaravelが自動的にStudentクラスでfindしてくれます(正確には異なるかもしれませんが)
つまり、自身でfindメソッドを使用しなくても、$idにはルートパラメータから取得したidを持つレコードでStudentインスタンスが作成されます。便利ですね。
こちらのページの「ルートモデル結合」の部分に詳細が記載されているので、詳細についてはこちらを確認しましょう。

詳細画面は名前の通り、詳細情報を確認するための画面ですが、生徒情報については一覧画面以上の情報を表示できないので、一覧画面と同じような処理を書いておきます。

public function detail(Student $id){
    $student = [
        'student_name' => $id->student_sei . $id->student_mei,  // 生徒名
        'kikan_from' => $id->student_from_date,  // 期間(開始日)
        'kikan_to' => $id->student_to_date,  // 期間(終了日)
        'student_memo' => $id->student_memo,  // 生徒メモ
        'student_detail_url' => route('student.detail', ['id' => $id->id]),  // 生徒詳細URL
        'student_mendan_url' => '',  // 面談URL
    ];


    return view('student.detail', [
        'student' => $student,
    ]);
}

$idが変数名として違和感のある実装になってしまいましたね。気になる方は変数名を修正しましょう。
その際はルート定義の修正も忘れずに!

bladeの作成

Controllerが呼び出している「student.detail」の作成をしていきましょう。
ここまでbladeを作成してきたディレクトリにdetail.blade.phpを作成します。
内容はindexと大きく変わらない形にします。

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            生徒詳細
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900">
                    <div class="bg-white border shadow-sm rounded-xl p-4 mb-3">
                        <h3 class="text-lg font-bold text-gray-800">
                            {{ $student['student_name'] }}
                        </h3>
                        <p class="mt-1 text-xs font-medium text-gray-500">
                            期間({{ $student['kikan_from'] }}~{{ $student['kikan_to'] }})
                        </p>
                        <p class="mt-2 text-gray-800">
                            {{ $student['student_memo'] }}
                        </p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

今回は一覧画面と同じようなレイアウトにしてしまいましたが、実際にはレイアウトを変えましょう。
一覧画面は多くのレコードを出力するために1件1件は簡易的な表示で複数レコードを表示、詳細画面では1件のレコードを詳細表示と役割が異なります。
項目が多い場合は一覧画面に一部の情報を表示して、詳細画面にすべての情報を表示するような構成にしておくと良いと思います。
例>生徒情報に生年月日や性別などの個人情報を追加。一覧画面は現状のまま、詳細画面には生年月日などの個人情報を表示

今回はここまで。
詳細画面も一覧画面とは大きく異なりません。
役割が「情報の表示」だからです。
システムからのDBの操作は「登録」「読み取り」「更新」「削除」です。
画面の役割が同じだと処理も似てきますね。

今回のポイントは「ルートモデル結合」ですね。
便利ではありますが、何が起きているかを忘れがちな部分です。しっかりと要点を抑えて使いこなしましょう。