*️⃣

Laravel Sail + Vite + Tailwind CSS + Laravel Breeze(認証機能)を使った開発環境の構築

2023/07/07に公開

はじめに

今回はLaravel Sailを使用して、Dockerを使った開発環境を構築を行います。

Laravelとは

PHPで開発されたオープンソースのWebアプリケーションフレームワークです。

Laravelは、ルーティング、MVCアーキテクチャ、認証、データベースマイグレーション、テンプレートエンジン、セッション管理などの多くの機能を提供しています。
https://laravel.com/

MVCとは

下記の頭文字を取ったものです。

モデル(Model)はデータを管理する部分、ビュー(View)はユーザーに表示されるインターフェース部分、コントローラー(Controller)はユーザーからの要求を受け取り、モデルとビューの間で情報をやり取りする役割を担います。

Laravel Sailとは

Laravel SailはCLIを通じてLaravelの開発環境を構築するためのDockerコンテナを提供するツールです。

Sailには、Laravelアプリケーションに必要なPHP、MySQL、Node.jsなどのコンテナが含まれており、簡単な設定とコマンド一つで環境をセットアップできます。
https://laravel.com/docs/11.x/sail

Sailコマンド

Laravel Sailは、./vendor/bin/sailスクリプトを通じてコンテナ内部のコマンドを呼び出す仕組みを提供しています。これにより、直接コンテナ内に入ることなくコマンドを実行できます。

sailコマンドはdocker-compose execコマンドを使用してDockerコンテナ内でArtisanコマンドを実行します。このため、sailはdocker-composeのラッパーとして機能し、Docker Composeで定義されたサービスに対してコマンドを実行します。

この仕組みにより、ユーザーは複雑なDockerコマンドを覚える必要がなく、簡単にLaravelのArtisanコマンドを実行することが可能です。

エイリアス作成

Laravelでは、通常コマンドを入力する場合にはphp artisanを使用します。

しかし、Laravel Sailを使用する場合は、./vendor/bin/sailと入力する必要があります。毎回./vendor/bin/sailと入力するのは面倒なのでエイリアスを設定します。

zshシェルを使用している場合はホームディレクトリにある.zshrcファイルを編集してエイリアスを設定することができます。もし、.zshrcファイルが存在しない場合は新規に作成してください。

touch .zshrc

.zshrcファイルを開き、以下のコードを追加してください。
sailと入力するだけで./vendor/bin/sailを実行できるようにするエイリアスを定義しています。

alias sail="./vendor/bin/sail"

設定を有効にするために、以下のコマンドを実行して設定変更を反映させてください。
これにより、Laravel Sail コマンドを sail という短縮形で簡単に使用できるようになります

source ~/.zshrc

Laravelプロジェクトの作成

Dockerを用いた開発環境を含んだLaravelプロジェクトの作成してください。

curl -s https://laravel.build/<プロジェクト名> | bash

https://laravel.com/docs/11.x/installation#sail-on-macos

利用するサービスをオプションの追加することで指定することもできます。URLクエリパラメータを使用して、withの後に使用したいDockerサービスのリストをカンマ区切りで指定してください。

指定しない場合は、mysql、redis、meilisearch、mailhog、seleniumが設定されます。

curl -s "https://laravel.build/<プロジェクト名> ?with=mysql,redis,meilisearch" | bash

最後にパスワードの入力が求められます。

無事にプロジェクトが作成できると下記のコマンドが表示されますので、表示通りに実行してコンテナを起動してください。

 cd <作成されたプロジェクト名> && ./vendor/bin/sail up

sail upのあとに-dをつけると、バックグラウンドでコンテナを立ち上げることができます。

ブラウザに http://localhost/ と入力すると、下記のいずれか表示されます。

下記の場合は、RUN MIGRATIONSを選択してください。

下記の場合は、sail artisan migrateでマイギュレーションを実行してください。

データベースのマイグレーションを実行してLaravelアプリケーションで定義されたすべてのマイグレーションがデータベースに適用されます。

Refresh nowを選択してください。

http://localhost/ にアクセスすると、デフォルトのトップページ(welcome.blade.php)が表示されることを確認してください。

認証機能の追加

認証機能の追加することによって、ログインした人のみが観覧できるようにすることができます。

今回はLaravel Breezeを使用して認証機能の追加します。
これは必須ではないのでプロジェクトに応じて追加してください。

また後からaravel Breezeを使用して認証機能を追加する場合は、既存のものが上書きされるなどして組み込みが大変なので最初に追加しておくことをおすすめします。

Laravel Breezeとは

Laravelアプリケーションに認証機能を追加するための軽量なパッケージです。
https://laravel.com/docs/11.x/starter-kits

Laravel Breezeのインストール

composer require laravel/breeze --dev

認証機能の作成

下記を実行すると、認証機能に必要なコントローラー、モデル、ビューが自動生成されます。

sail artisan breeze:install

Blade with Alpineを選択してください。

 ┌ Which Breeze stack would you like to install? ───────────────┐
 │ › ● Blade with Alpine                                        │
 │   ○ Livewire (Volt Class API) with Alpine                    │
 │   ○ Livewire (Volt Functional API) with Alpine               │
 │   ○ React with Inertia                                       │
 │   ○ Vue with Inertia                                         │
 │   ○ API only                                                 │
 └──────────────────────────────────────────────────────────────┘

または核を実行すると、オプションの中でBlade with Alpineを選択していることになります。

sail artisan breeze:install blade

生成が完了したら、http://localhost/ にアクセスして右上にLog in Registerが追加されているか確認してください。

認証機能を確認するために、登録してください。

登録/ログインが完了すると、右上に名前が表示されると思います。

パスワードリセットの設定

現在のままでは下記のように表示はされますが、実際にパスワードリセットを行うことができません。

mailpitサービスを使用して、実際のメールサーバーではなく、ローカルの開発環境内でメールを受信できるようにします。

Notificationクラスの作成

php artisan make:notification ResetPasswordNotification

Notification

https://readouble.com/laravel/11.x/ja/notifications.html

モデルの修正

app/Models/User.php
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;

use App\Notifications\ResetPasswordNotification;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    // 追記
    public function sendPasswordResetNotification($token): void
    {
        $url = route('password.reset', ['token' => $token]);  // 名前付きルートを利用

        $this->notify(new ResetPasswordNotification($url));
    }
}

http://localhost:8025/ にアクセスしてメールが受信できているか確認してください。

ResetPasswordNotification.phpの内容を変更すると、届くメールの内容も変更することができます。

app/Notifications/ResetPasswordNotification.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class ResetPasswordNotification extends Notification
{
    use Queueable;

    protected $url; // プロパティの宣言を追加

    /**
     * Create a new notification instance.
     */
    public function __construct($url)
    {
        $this->url = $url;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @return array<int, string>
     */
    public function via(object $notifiable): array
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     */
    public function toMail(object $notifiable): MailMessage
    {
        return (new MailMessage)
            ->subject(config('app.name') . ' パスワードリセットURLの送付')
            ->greeting('いつもご利用頂きありがとうございます。')
            ->action('パスワードリセット', $this->url)
            ->line('こちらからパスワードリセットを行ってください。');
    }

    /**
     * Get the array representation of the notification.
     *
     * @return array<string, mixed>
     */
    public function toArray(object $notifiable): array
    {
        return [
            //
        ];
    }
}

次のように変更されます。

リンクからパスワードリセットの画面が表示されるか確認してください。

Tailwind CSSの導入

Laravel Breezeのインストールしている場合はTailwind CSSがすでに含まれているため、追加の手順や初期設定は不要です。
https://tailwindcss.com/docs/guides/laravel

Tailwindで機能するパッケージをインストール

npm install -D tailwindcss postcss autoprefixer

Tailwind CSSの初期設定を行います。
tailwind.config.jspostcss.config.jsの2つのファイルがプロジェクトのルートに作成されているか確認してください。

npx tailwindcss init -p

テンプレートパスの設定

以下を記述してください。
LaravelはデフォルトでpublicディレクトリにあるCSSファイルを探します。テンプレートパスは、アプリケーションのCSSファイルがどこにあるかをLaravelに伝えるものです。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./resources/**/*.blade.php",
    "./resources/**/*.js",
    "./resources/**/*.vue",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

@tailwindディレクティブの追加

Tailwind CSSを利用してプロジェクトにスタイルを追加するために@tailwindディレクティブの記述を追加してください。

resources/css/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;

スタイルの適用

スタイルを適用するには、以下を適用したいbladeファイル(レイアウトを共通化するためのファイル)に記述してください。

@vite('resources/css/app.css')

今回は試しに以下のように変更して、確認してみます。

resources/views/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.0">
</head>

<body>
    <h1 class="text-3xl bg-orange-300 font-bold ">
        Hello world!
    </h1>
</body>

</html>

スタイルが適用されているか確認するために、コンテナは起動したままViteを使用してフロントエンドの開発サーバーを起動します。

npm run dev

既にデフォルトページが表示されているにも関わらず、Viteを使用してフロントエンドの開発サーバーを起動する理由

Laravelプロジェクトには、通常、フロントエンドのJavaScriptやCSSなどが含まれています。

これらのファイルを編集した場合にnpm run devを実行し、Viteを起動していると、Viteが変更を検知し、自動的にビルドを実行してブラウザをリロードしてくれるので、変更を即座に反映させる(ホットリロード)ことができます。

下記が表示され、Tailwind CSSが効いていないことがわかると思います。

次に@vite('resources/css/app.css')を追記してください。

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

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    @vite('resources/css/app.css')
</head>

<body>
    <h1 class="text-3xl bg-orange-300 font-bold ">
        Hello world!
    </h1>
</body>

</html>

Tailwind CSSが効いていることがわかると思います。

Viteを起動している時の違いがわかるように、h1のユーザー一覧ユーザーに変更して比較しました。

Viteの起動なし(手動更新&tailwind cssが効いていません)

Viteの起動あり(手動更新不要&tailwind cssが効いています)

Viteとは

Vue.jsのための新しいビルドツールおよび開発サーバーです。
https://ja.vitejs.dev/guide/
https://zenn.dev/comm_vue_nuxt/articles/what-is-vite

Laravelのデフォルトビルドツールがバージョン9.18以降はLaravel MixからViteに変わっています。
https://laravel.com/docs/11.x/vite#main-content

Viteを使用してフロントエンドの開発サーバーを起動することができます。

npm run dev

ホットリロード機能

Viteのデフォルト設定にはホットリロード機能が含まれており、これによりコードの変更が即座に適用されるため、ページをリフレッシュする必要がありません。デフォルトで、localhostが指定されています。

記述されていませんが、コメントアウト部分の記述が適用されています。

vite.config.js
import laravel from "laravel-vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
    // server: {
    //     hmr: {
    //         host: "localhost",
    //     },
    // },
    plugins: [
        laravel({
            input: ["resources/css/app.css", "resources/js/app.js"],
            refresh: true,
        }),
    ],
});

言語とタイムゾーンの変更

timezone、localeの項目を以下のように変更します。

config/app.php
'timezone' => 'Asia/Tokyo'
'locale' => 'ja',

データベースのGUIツール

phpMyAdminのインストール方法などを記述しております!
https://zenn.dev/nenenemo/articles/5222abfa1f6736

本番環境用

本番環境用にビルドする場合は下記を実行してください。

sail npm run build

これにより、publicディレクトリにファイルが出力され、本番環境で参照するjs、cssファイルなどが生成され、デプロイできる状態になります。

Laravel SailのDockerfileを表示する

下記コマンドでDocker関連のファイルをルートディレクトリにコピーすることで確認できます。
また、コピーしたファイルはGit管理の対象となります。

sail artisan sail:publish

docker以下にvendor内にあるファイルがコピー表示されます。

また、docker-compose.yml context:の内容も自動でコピーして作成されたDockerfileに変更になっています。

laravel.testのデフォルトのコンテナ名を変更する

.env > $APP_SERVICEは、Laravelアプリケーションを実行しているコンテナ名を指定しています。

デフォルトのlaravel.testから、`docker-compose.yml`でコンテナ名を変更する場合は必ず、.envAPP_SERVICEを定義してください。

.env
APP_SERVICE="app"

artisanコマンドの実行について

ローカルでartisanコマンドを使う場合は

php artisan

で実行していましたが、Sail上でartisanコマンドを使う場合には

sail artisan

で実行します。

アプリケーションが表示されない場合

デベロッパーツールのConsoleで以下のような表示がされている場合には、フロントエンドの開発サーバー(Vite)が立ち上がっていません。

開発中は、両方のサーバーを起動しておいてください。

両方のサーバーを起動していることで、バックエンドのサーバーはフロントエンドからのリクエストの処理などを行ったりすることができます。

サーバー コマンド 説明
Laravelのバックエンドサーバー(コンテナ) ./vendor/bin/sail up API の提供やデータベースとのやり取りなど、アプリケーションのバックエンド処理を担当します。
Viteを使用したフロントエンドの開発サーバー npm run dev フロントエンドの開発に特化した機能を提供し、ビルドやモジュールのホットリロードを可能にします。

エラーログの確認

Laravelは詳細なエラーログをstorage/logs/laravel.logに保存します。
このログファイルを確認して、具体的なエラーメッセージを確認してください。

また、下記を記述することでデバックできます。

use Illuminate\Support\Facades\Log;

Log::info('ユーザーID: ' . user()->id);

ポート番号を変更する

"${APP_PORT:-80}:80""8080:80"に変更してコンテナを再起動してください。

docker-compose.yml
services:
    laravel.test:
        build:
            context: ./docker/8.3
            dockerfile: Dockerfile
            args:
                WWWGROUP: "${WWWGROUP}"
        image: sail-8.3/app
        extra_hosts:
            - "host.docker.internal:host-gateway"
        ports:
            - "8080:80"
            # - "${APP_PORT:-80}:80"
            - "${VITE_PORT:-5173}:${VITE_PORT:-5173}"

http://localhost/ ではなく、http://localhost:8080/ で画面が表示されるように変更になっているのがわかると思います。

ビュー(Bladeテンプレート内)でデバッグする

dd関数

dd(Dump and Die)関数はデバッグヘルパー関数の1つで、変数や式の値を出力してから、その後の処理が停止します。

blade.php
{{ dd(session('message')) }}

SQLSTATE[42S02]: Base table or view not found: 1146 Table '<データベース名>.<テーブル名>' doesn't exist (Connection: mysql, SQL: select * from <テーブル名> where id = <ID> limit 1)

マイグレーションを実行してテーブルの作成/変更を行うと解決するか確認してください。

sail artisan migrate

file_put_contents(/.../storage/framework/views/<>.php): Failed to open stream: No such file or directory

Laravelがビューのキャッシュファイルを作成しようとした際にディレクトリが存在しないか、書き込み権限がないためにファイルを作成できないことを示しています。

ディレクトリが存在しない場合は作成してください。
/.../は省略して表示しています。

mkdir -p /.../storage/framework/views

ストレージディレクトリに対して適切なパーミッションを設定してください。
今回はmacOSを使用しているので_wwwとしています。

chmod -R 775 /.../storage
sudo chown -R $USER:_www /.../storage

設定を変更した後、Laravelのキャッシュをクリアして設定を反映させます。

php artisan cache:clear
php artisan view:clear
php artisan config:clear

終わりに

何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉

Discussion