【Laravel】Breeze導入後のテンプレートの書き方(サイト構築のための備忘録)

ユーザー認証を行うためにBreezeを導入したものの,そのあとページをどう書けばいいのか迷子になったので,備忘録として。

Breeze導入までの手順はこちらから

トップページの差し替え

Breezeを導入して https://localhost を開くと上の画面が表示される。このページはresources/views/welcome.blade.phpである。

とりあえず,トップページをhome/indexに変えてみる。

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

を

Route::get('/', function () {
    return view('home.index');
});

に書き換える。

bladeテンプレートのファイルはコマンドで生成できないので,フォルダとファイルを自分で作る。welcome.blade.phpを踏襲してトップページを書いてみた。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>myapp</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="antialiased">
<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
    @if (Route::has('login'))
        <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
            @auth
                <a href="{{ url('/dashboard') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">ダッシュボード</a>
            @else
                <a href="{{ route('login') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">ログイン</a>

                @if (Route::has('register'))
                    <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 dark:text-gray-500 underline">新規登録</a>
                @endif
            @endauth
        </div>
    @endif

    <h1>トップページです。</h1>

</div>
</body>
</html>

再び localhost にアクセスすると。

トップページが変更された。

言語とタイムゾーンの設定

上のコードの

<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

は言語を指定している。ここで,言語とタイムゾーンを変更しておく。

'timezone' => 'UTC',
を
'timezone' => 'Asia/Tokyo',
に変更。

'locale' => 'en',
を
'locale' => 'ja',
に変更。

TailwindCSS

Breezeを入れるとTailwindCSSも使えるようになる。classの中に色々書かれている部分がそれ。TilwinsCSSの実際の書き方は以下を参考に。

Tailwind CHEAT SHEET

TailwindCSSはコードを書き換えただけでは画面に反映されず,いちいちnpm run devでコンパイルする必要がある。上のコードのようにCDNを入れておくと便利。

<script src="https://cdn.tailwindcss.com"></script>

本番環境に移すときにこの部分は削除する。ブラウザでF12を押してコンソールを見ると cdn.tailwindcss.com should not be used in production. と警告が出ていることを確認しておくと良い。

TailwindCSSはCSSフレームワークの一つ。cssファイルを書かなくてもhtml上でレイアウトの設定が直接書けるので便利。

Bladeテンプレート

Bladeテンプレートについては以下を参照。

Laravel 9.x Blade テンプレート

これをもとに,上で書いたindex.blade.phpの解説。

Route::has()

@if (Route::has('login'))@endif は「ルーティングにloginが含まれるなら」という意味。

ルーティングを確認してみる。web.phpにrequire __DIR__.'/auth.php';とあり,同じフォルダのauth.phpを見ると

Route::get('login', [AuthenticatedSessionController::class, 'create'])
            ->name('login');

とある。auth.phpを見るとその他にもユーザー認証に関わる様々なページが作られていることが分かる。

つまりルーティングにloginが含まれているので,ログインや新規登録のリンクが表示されることになる。ちなみにこれを@if (Route::has('hoge'))などに書き換えてみるとリンクが表示されないことが確認できると思う。

実際のところこの@if文は無くても動くが,「誤って消してしまった」など何らかの原因でloginページへのルーティングが見つからないときに例外処理でエラーになるのを防ぐことができる。

@auth ~ @endauth

@auth@endauth はログインしているときだけ表示される。

@auth
    ログインしているとき
@else
    ログインしていないとき
@endauth

上のコードではログインしているときはダッシュボードのリンクが表示されるようになっている。実際にユーザー登録&ログインして確かめると良い。

レイアウトと部分テンプレートの構造を理解する

ログインしてダッシュボードのページに移動すると,上部にナビゲーションメニューが表示されている。

<div class="min-h-screen bg-gray-100">
    @include('layouts.navigation')

    <!-- Page Heading -->
    <header class="bg-white shadow">
        <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
            {{ $header }}
        </div>
    </header>

    <!-- Page Content -->
    <main>
        {{ $slot }}
    </main>
</div>
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Dashboard') }}
        </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 bg-white border-b border-gray-200">
                    You're logged in!
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

ダッシュボードのbladeテンプレートファイルを見ると,<x-app-layout></x-app-layout>で囲まれている。このように書いておくとapp.blade.phpが呼び出され,dashboard.blade.phpがレイアウトの一部として表示されるようになる。

個人的にはこの部分の理解に少し苦労した。普通はルーティングから<head></head><body></body>を持つ本体ファイルのビューを呼び出してその中に部分テンプレートが入っている構造だと思うのだが,Bladeの場合は話が逆でルーティングで部分テンプレートのビューを呼び出して,そこから本体を呼び出すようになっている。慣れるとこっちの方が合理的だと思えてくる。

<x-slot name="header"></x-slot>で囲まれた部分が{{ $header }}に入り,それ以外は{{ $slot }}に入る。

ダッシュボードの画面を作り変えてみる

構造を理解したところでダッシュボードの画面を作り変えてみる。ヘッダーの表示はいらないので削除し,メッセージを変える。

<x-app-layout>
    <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 bg-white border-b border-gray-200">
                    ダッシュボードの画面です。
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
<div class="min-h-screen bg-gray-100">
    @include('layouts.navigation')

    <!-- Page Content -->
    <main>
        {{ $slot }}
    </main>
</div>

{{ $header }}<x-slot name="header"></x-slot>を削除すると,ヘッダーが削除された。

ナビゲーションメニューに項目を追加してみる

ナビゲーションメニューはnavigation.blade.phpに書かれている。

ファイルの中身を見ると1行目からx-data="{ open: false }"など書かれていて意味不明かもしれない。ナビゲーションメニューはAlpine.jsを使うことでドロップダウンメニューなどを動かしている。しかもレスポンシブ対応のために表示が切り替わるように書かれているのでなおさら複雑なことになっている。

Alpine.jsはVueやReactのような動的なページを作るための軽量フレームワーク。

Alpine.jsによるリッチなアニメーションを放棄すればナビゲーションメニューをもっとシンプルに書くこともできるが,ここでは元のコードをなるべくそのままにしておく。

<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
        {{ __('Dashboard') }}
    </x-nav-link>
</div>

元のコードで表示されているダッシュボードのリンクは↑の部分。{{ __('Dashboard') }}{{ __('ダッシュボード') }}とすれば日本語表示になる。

:active="request()->routeIs('dashboard')"は項目の下の青い線の表示に関係する部分。「HTTPリクエストの中にdashboardページのアドレスが存在していたらアクティブ状態にしてね」という意味。現在のページがdashboardなら下線が表示され,それ以外のページのときには表示されない。

さらに,別ページのリンクを表示させるために,コードを追加する。

<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
        {{ __('ダッシュボード') }}
    </x-nav-link>
</div>
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
    <x-nav-link :href="route('customer')" :active="request()->routeIs('customer')">
        {{ __('顧客管理') }}
    </x-nav-link>
</div>

web.phpに以下を追加。

Route::get('/customer', function () {
    return view('customer.index');
})->name('customer');

bladeテンプレートを新規作成しておく。

<x-app-layout>
    <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 bg-white border-b border-gray-200">
                    顧客管理のindexです。
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

これで新しいページが追加された。

ログイン状態のときだけ表示するページ

実は上のコードには問題がある。一度ログアウトして,アドレスバーにlocalhost/customerと入力して直接アクセスすると例外処理が発生してエラーとなるのが確認できる。

このように<x-app-layout></x-app-layout>を用いたビューはログイン状態のときしか表示できない。

この問題に対処するためにweb.phpを修正する。

Route::get('/customer', function () {
    return view('customer.index');
})->middleware(['auth'])->name('customer');

ルーティングにミドルウェアを挿入した。

再びlocalhost/customerにアクセスすると,ログイン画面に飛ばされることが確認できるだろう。このように->middleware(['auth'])を挿入することで,未ログイン状態でこのページにアクセスしようとすると,自動的にログインページに移動させることができる。

スマホ向けレイアウトの編集

navigation.blade.phpの後半にはスマホ向けのナビゲーションメニューのコードがあるので,こちらも修正しておく。

<!-- Responsive Navigation Menu -->
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
    <div class="pt-2 pb-3 space-y-1">
        <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
            {{ __('ダッシュボード') }}
        </x-responsive-nav-link>
    </div>
    <div class="pt-2 pb-3 space-y-1">
        <x-responsive-nav-link :href="route('customer')" :active="request()->routeIs('customer')">
            {{ __('顧客管理') }}
        </x-responsive-nav-link>
    </div>

ブラウザのウィンドウの横幅を最小にするとナビゲーションメニューの表示が切り替わるのが確認できる。

未ログイン時のページの共通レイアウト

未ログイン時のページの共通レイアウトはguest.blade.phpになる。中身を見ると分かるがこちらにはナビゲーションメニューは含まれない。このレイアウトはログインページなどに用いられている。

少し手を加えて動作を確認しておく。

<body>
    <div class="font-sans text-gray-900 antialiased">
        {{ $slot }}
    </div>
    <div>
        追加の情報
    </div>
</body>

画面の下に追加の情報が表示された。

未ログイン状態でアクセスできるページを作ってみる。まずはweb.phpにルーティングを追加。

Route::get('/inquiry', function () {
    return view('home.inquiry');
});

bladeテンプレートを新規作成する。

<x-guest-layout>
    <div>お問い合わせのページ</div>
</x-guest-layout>

画面に「お問い合わせのページ」と「追加の情報」が表示されることが確認できる。

このように<x-guest-layout></x-guest-layout>で囲むとguest.blade.phpのレイアウトが読み込まれる。ちなみにこのページはログイン状態でもアクセスすることができる。

ログイン画面の日本語化

こちらのページに飛んで,zipファイルをダウンロード。

解凍したファイルの,lang-main>locales>jaフォルダの中身をプロジェクトののlang/jaにコピーする。ただし,ja.jsonはlang直下に配置する。

これで日本語化できるが,完全に日本語化できるわけではない。

cssの置き場所

cssファイルはpublic/css以下に置く(resource/cssではない)。たとえばsub.cssなどを作った場合,テンプレートから呼び出すのを忘れないように。

<head>
    <link rel="stylesheet" href="{{ asset('css/sub.css') }}">
</head>

まとめ

本稿ではBreezeをインストールした状態から実際にwebサイトを構築していくための手掛かりを示した。構造の理解が進めばレイアウトを大幅に変更できるようになるだろう。