【速習】LaravelとPHPブートキャンプ Part3 - QuickNetwork開発部

本コンテンツはクイックネットワークでエンジニアとして開発のお仕事する人が、ベーシックであるPHPとフレームワークのLaravelを容易に学べるよう作成いたしました。外でも使ってもらえるように公開しております。学生インターンとか新卒とか中途で他の言語経験がある方とかを対象にしている感じです。

Part2は[こちら](https://qiita.com/qn-takami/items/68a1d467e115f4974639)から。
Part3は[こちら](https://qiita.com/qn-takami/items/305fe6065ff824bb85d1)からご覧になれます。よろしければぜひご確認ください。

### 通知とイベント

新しいChirpが作成されたときにメール通知を送信して、Chirperを次のレベルに引き上げましょう。

```
php artisan make:notification NewChirp
```

メール送信のサポートに加え、Laravelは、メール、SMS、Slackを含む様々な配信チャネルでの通知送信をサポートしています。さらに、コミュニティが構築した様々な通知チャネルが作成されており、数十の異なるチャネルで通知を送信することができます！通知はデータベースに保存され、Webインターフェースに表示されます。

### 通知の作成

Artisanが用意した以下のコマンドで、通知に関するすべての大変な作業を実行してくれます。

`php artisan make:notification NewChirp`

これで、app/Notifications/NewChirp.phpに新しいnotificationが作成され、カスタマイズできるようになります。

NewChirpクラスを開き、作成されたばかりのChirpを受け取れるようにし、メッセージをカスタマイズして、作者の名前とメッセージのスニペットを含めるようにしましょう。

```app/Notifications/NewChirp.php
<?php
namespace App\Notifications;
+use App\Models\Chirp;
use Illuminate\Bus\Queueable;
+use Illuminate\Support\Str;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class NewChirp extends Notification
{
    use Queueable;
    /**
     * Create a new notification instance.
     */
-    public function __construct()
+    public function __construct(public Chirp $chirp)
    {
        //
    }
 ...
    /**
     * Get the mail representation of the notification.
     */
    public function toMail(object $notifiable): MailMessage
    {
        return (new MailMessage)
-                    ->line('The introduction to the notification.')
-                    ->action('Notification Action', url('/'))
+                    ->subject("New Chirp from {$this->chirp->user->name}")
+                    ->greeting("New Chirp from {$this->chirp->user->name}")
+                    ->line(Str::limit($this->chirp->message, 50))
+                    ->action('Go to Chirper', url('/'))
                    ->line('Thank you for using our application!');
    }
}
```

ChirpControllerクラスのstoreメソッドから直接通知を送ることもできますが、そうするとコントローラの作業が増えてしまい、特にデータベースへの問い合わせやメールの送信を行うことになるため、リクエストが遅くなる可能性があります。

その代わりに、バックグラウンドのキューで待ち受けることができるイベントを発行して、アプリケーションの動作速度を向上させましょう。

### イベントの作成

イベントは、アプリケーションの様々な機能を切り離すための素晴らしい方法です。

次のコマンドで新しいイベントを作ってみましょう。

```
php artisan make:event ChirpCreated
```

これで app/Events/ChirpCreated.php に新しいイベントクラスが作成されます。

新しいChirpが作成されるたびにイベントを発行するので、ChirpCreatedイベントを更新して、新しく作成されたChirpを受け取り、それをnotificationに渡せるようにしましょう。

```app/Events/ChirpCreated.php
<?php
namespace App\Events;
+use App\Models\Chirp;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ChirpCreated
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
    /**
     * Create a new event instance.
     */
-    public function __construct()
+    public function __construct(public Chirp $chirp)
    {
        //
    }
 ...
}
```

### イベントの発行

イベントクラスができたので、Chirpが作成されたときにいつでもイベントを発行できるようになりました。アプリケーションのライフサイクルのどこでイベントを発行してもかまいませんが、今回のイベントはEloquentモデルの作成に関連しているので、Chirpモデルがイベントを発行するように設定できます。

```app/Models/Chirp.php
<?php
namespace App\Models;
+use App\Events\ChirpCreated;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Chirp extends Model
{
    use HasFactory;
    protected $fillable = [
        'message',
    ];
+    protected $dispatchesEvents = [
+        'created' => ChirpCreated::class,
+    ];
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }
}
```

これで、新しいChirpが作成されるたびに、ChirpCreatedイベントが発行されるようになりました。

### イベントのリスナー

イベントを発行したので、そのイベントをリスニングして通知を送信する準備ができました。

ChirpCreatedイベントを受信するリスナーを作成しましょう。

```
php artisan make:listener SendChirpCreatedNotifications --event=ChirpCreated
```

新しいリスナーは、app/Listeners/SendChirpCreatedNotifications.phpに配置されます。通知を送信するためにリスナーを更新しましょう。

```app/Listeners/SendChirpCreatedNotifications.php
<?php
namespace App\Listeners;
use App\Events\ChirpCreated;
+use App\Models\User;
+use App\Notifications\NewChirp;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
-class SendChirpCreatedNotifications
+class SendChirpCreatedNotifications implements ShouldQueue
{
 ...
    /**
     * Handle the event.
     */
    public function handle(ChirpCreated $event): void
    {
-        //
+        foreach (User::whereNot('id', $event->chirp->user_id)->cursor() as $user) {
+            $user->notify(new NewChirp($event->chirp));
+        }
    }
}
```

リスナーにShouldQueueインターフェースを設定しました。これは、リスナーがキューで実行されるべきことをLaravelに伝えるものです。デフォルトでは、"sync "キューが同期的にジョブを処理するために使われますが、バックグラウンドでジョブを処理するキューワーカーを設定することもできます。

また、Chirpの発信者を除くプラットフォーム内のすべてのユーザーに通知を送るようにリスナーを設定しました。現実的には、これはユーザには迷惑なものでしょう。そのため、ユーザがフォローしているアカウントの通知だけを受け取るように、「フォローする」機能を実装するとよいでしょう。

すべてのユーザーを一度にメモリに読み込むことを避ける（パフォーマンスに影響する）ために、データベースカーソル(cursor)を使用しました。

:::note warn
本番アプリケーションでは、ユーザーがこのような通知の配信を停止する機能を追加する必要があります。
:::

### テスト

MailpitやHELOのようなローカルのメールテストツールを利用して、アプリケーションから送られてくるメールをキャッチし、閲覧することができます。DockerとLaravel Sailを使って開発している場合、Mailpitが中に含まれています。

あるいは、プロジェクトの.envファイルを編集し、MAIL\_MAILER環境変数をlogに変更することで、Laravelがログファイルにメールを書き込むように設定することもできます（注：Laravel11ではlogがデフォルトになりました）。デフォルトでは、メールはstorage/logs/laravel.logにあるログファイルに書き込まれます。

Chirpの発信者には通知を送らないように設定しましたので、少なくとも2つのユーザーアカウントを登録してください。そして、新しいChirpを投稿して通知をトリガーしてください。

Mailpitを使用している場合は、[http://localhost:8025/](http://localhost:8025/ "http://localhost:8025/") ここに、先ほどChirpしたメッセージの通知があります！

### 本番でのメールの送信

本番（production)で実際のメールを送信するには、SMTPサーバー、またはMailgun、Postmark、Amazon SESなどのトランザクションメールプロバイダーが必要です。Laravelは、これらの全てをすぐにサポートします。詳しくは[Mailのドキュメント](https://laravel.com/docs/11.x/mail#introduction "https://laravel.com/docs/11.x/mail#introduction")をご覧ください。

## デプロイ

話を変えて、新しいLaravelアプリケーションをデプロイする方法を見てみましょう。

#### プロバイダーの選択

Laravelは、Laravelの控えめなサーバー要件を満たす最新のPHPホスティング環境であれば、どこでもデプロイできます。しかし、ウェブサーバーやデータベースサーバーの設定や管理は、アプリケーションの構築やユーザーへの価値提供から注意を奪います。そこで、Laravel ForgeとLaravel Vaporを開発しました。

### Laravel Forge

Laravel Forgeは、DigitalOcean、Linode、AWSなどの様々なインフラプロバイダーにサーバーを作成できます。さらに、ForgeはNginx、MySQL、Redis、Memcached、Beanstalkなど、堅牢なLaravelアプリケーションの構築に必要なすべてのツールをインストールし、管理します。

### Laravel Vapor

Laravel Vaporは、AWSが提供するLaravelのサーバーレスデプロイメントプラットフォームです。あなたのLaravelインフラをVapor上で立ち上げ、サーバーレスのスケーラブルでシンプルな使い心地に感動するでしょう。

どちらも素晴らしい選択肢ですが、今回はシンプルさ、プロバイダーの選択肢、小規模なアプリケーションで予算に優しいForgeを紹介します。サーバーレスのスケーラビリティを選択するのであれば、後でいつでもVaporに移行できます。

Laravel Forgeの無料トライアルにサインアップしてから、サーバープロバイダーを選んでください：

[DigitalOcean](https://try.digitalocean.com/freetrialoffer/ "https://try.digitalocean.com/freetrialoffer/")（100ドルの無料クレジットあり）  
[Linode](https://www.linode.com/ "https://www.linode.com/")（$50の無料クレジットあり）  
[AWS](https://aws.amazon.com/free/ "https://aws.amazon.com/free/") (無料ティアあり)  
[Vultr](https://www.vultr.com/promo/try50/ "https://www.vultr.com/promo/try50/") ($50 無料クレジットあり)  
[Hetzner](https://www.hetzner.com/ "https://www.hetzner.com/")  
カスタムVPSサーバー  
どちらを選ぶか迷った場合は、寛大なクレジット、優れたユーザーインターフェイス、優れた機能を持つ[DigitalOcean](https://try.digitalocean.com/freetrialoffer/ "https://try.digitalocean.com/freetrialoffer/")をお勧めします。

### ソース管理システムへの接続

Forge はアプリケーションのコードがどこにあるかを知る必要があるため、[GitHub](https://github.com/ "https://github.com/")、[GitLab](https://gitlab.com/ "https://gitlab.com/")、[Bitbucket](https://bitbucket.com/ "https://bitbucket.com/") などのソース管理プロバイダーのアカウントが必要です。

Forge のウェルカム画面でプロバイダーを選択するか、Forge アカウントのソース管理セクションにアクセスして Forge をプロバイダーに接続します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/581066e8-fd85-8563-2677-4d43c047825a.png)


### サーバープロバイダーへの接続

Forge はサーバーを構築するために、サーバー プロバイダーの API キーを必要とします。Forge のウェルカム画面、または Forge アカウントのサーバー プロバイダー セクションからサーバー プロバイダーに接続できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/e9b33366-15cd-cd20-9627-89d236600d8f.png)

指示に従って、選択したプロバイダーで Forge 用の API 認証情報を作成し、詳細を入力して続行します。

### サーバーの構築

Forge がソース管理およびサーバー プロバイダーに接続されたので、最初のサーバーを作成する準備ができました！

\[Server\] ページで \[Create Server\] ボタンをクリックします。

次に、サーバー プロバイダーを選択します。プロバイダーによっていくつかのオプションが表示されます。デフォルトのオプションはChirperをデプロイするのに最適なものですが、あなたの要件と予算に合うように、選択したプロバイダで利用可能なオプションをすべて確認することをお勧めします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/9551c76c-b824-feee-6851-a063f6e71341.png)

:::note warn
必要なものすべてを1つのサーバーにプロビジョニングする「アプリサーバー」から始めることをお勧めします。不要なコストを避けるため、終了したらサーバーを破棄することもできます。
:::

サーバーの作成には、プロバイダーや選択したオプションにもよりますが、10分ほどかかります。

### サイトの構築（オプション）

Forge は自動的に新しいサーバー上に "default" サイトを作成します。これは、ドメイン名を購入する代わりにサーバーのパブリック IP アドレスを使用してアクセスできるため、アプリケーションの展開に最適です。

ドメイン名を使用したい場合は、サーバーの「Site」タブにアクセスして「default」サイトを削除し、新しいサイトを作成することをお勧めします。サイトはいくつでも作成できます。また、新しいサイト用にデータベースを作成するオプションもあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/7febf390-a78c-ac56-08d9-3faa86747c4a.png)

サイトを選択し、見出しにあるサイト名をクリックすると、サイトが表示され、Forge のデフォルト サイト ページが表示されます。

### データベースの作成

“default”サイトを使用している場合、アプリケーション用にデータベースを作成する必要があります。データベースはサーバーのデータベースタブで作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/876c56e0-94fc-3fe4-478d-aaade8f551cf.png)

サーバー構築時に Forge が生成した認証情報を使用するため、ユーザー名とパスワードのフィールドは空のままにしておくことができます。

### リポジトリのインストール

新しいサイトにソース管理リポジトリをインストールする準備ができました。まだの場合は、アプリケーション用の git リポジトリを作成し、ソースコードを Forge に接続したソース管理プロバイダーにプッシュしてください。

サーバーの Sites タブからサイトを選択し、Git Repository をクリックします。リポジトリ名（例：taylorotwell/chirper）を入力し、ブランチを選択します。Install Composer Dependencies」にチェックが入っていることを確認し、リポジトリをインストールして続行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/e4c43917-9051-984e-685b-99da6ee97495.png)

### 環境ファイルの設定

リポジトリをインストールするとき、Forge はリポジトリから .env.example 環境ファイルをコピーします。環境設定ファイルは、サイトの \[Environment\] メニューから確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/948b23a5-1800-5afd-94d3-fd640a48e990.png)

すべての値が本番環境に正しく設定されていることを確認するために、すべての値を注意深く確認する必要があります。

DB\_\*の値が、先に作成したデータベースと一致していることを確認してください。専用のデータベース・ユーザ名とパスワードを作成した場合は、それらも必ず設定してください。

アプリケーションから電子メールを送信したい場合は、MAIL\_\* 値を確認し、選択したメール・プロバイダに必要な変数を追加する必要があります。

キューワーカーを実行したい場合は、QUEUE\_CONNECTION の値が希望するキュー接続と一致していること、および、前提条件がインストールされ設定されていることを確認してください。

### デプロイスクリプトの設定

サイトの \[App\] タブで、Forge が作成したデプロイ スクリプトを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/00dd0082-b916-36bd-05e5-520a546e9881.png)

アプリケーションはNodeの依存関係とViteを使用しているので、依存関係をインストールしてアセットをビルドするステップを追加する必要があります：

```bash
cd /home/forge/default
git pull origin $FORGE_SITE_BRANCH
$FORGE_COMPOSER install --no-interaction --prefer-dist --optimize-autoloader
+npm ci
+npm run build
( flock -w 10 9 || exit 1
    echo 'Restarting FPM...'; sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock
if [ -f artisan ]; then
    $FORGE_PHP artisan migrate --force
fi
```

### キューワーカーを動かす（オプション）

キュー ワーカーを構成することを選択した場合、サイトのキュー タブで Forge にワーカーの起動と管理を指示することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/62857db7-2a17-39c3-4a81-9a726a8bdf8d.png)

タスクスケジューラーを動かす（オプション）

Laravelのタスクスケジューリングを使用する場合、サーバーのスケジューラータブでスケジューラーを実行するようにForgeを設定することができます。スケジューラーが設定したスケジュールで期限切れのタスクをチェックするように、頻度を "毎分 "に設定する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/25a90c18-f153-9e75-90a3-e9006156f63b.png)

### デプロイする

アプリケーションを初めてデプロイする準備ができました！\[Deploy Now\] ボタンを押して、Forge がデプロイ スクリプトを実行する間、しばらくお待ちください。

:::note warn
Quick Deployを有効にすると、メインブランチにプッシュするたびにアプリケーションを自動的にデプロイできます。
:::

デプロイの履歴は、\[Deploy\]タブで確認できます。

完了したら、サイトに再度アクセスして、すべてが正常にデプロイされたことを確認します。

:::note warn
サーバー・プロバイダーからの不要な請求を避けるため、サーバーを使い終わったら、Metaタブからサーバーを破棄することを忘れないようにしましょう。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/7f010000-775c-54c0-8dce-3bf769fe22e9.png)

PublicIPでアプリが開きました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/18dc6762-a2e6-5b57-cd87-cfe5fd8efeac.png)

### Vite manifest not foundが出たら

上記のデプロイ後、PublicIPで開いたアプリからLoginやRegisterを押すと500エラーになってしまいます。

そこでログを開いてみると `Vite manifest not found`と出てきます。これはViteが正しく動作しないので認証機能が動かないことによるものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/0a6a31ff-42c5-6fd4-9c08-e2414cafaf95.png)

解決策は `.gitignore` を以下のように変更してコミット、プッシュした後に再度デプロイを行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/2922257c-b902-64ec-2051-24c0bc2803b6.png)

ユーザー登録、ログイン後投稿が可能になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821455/6a804d73-9b52-4ee7-f4db-1287588961a7.png)

### 最後に＆次のステップ

これで、Laravelを使って完全に機能するアプリケーションを構築できました。楽しんでいただけたでしょうか？

私たちは、Laravelで可能なことのほんの表面をなぞっただけです。Chirperの機能をどんどん追加してください！Laravelには、アプリケーションのユニークな側面に集中しながらモダンなアプリケーションを構築するのに役立つ機能がまだまだたくさんあります。Laravelが提供する全ての機能を理解するために、ドキュメントを読むことをお勧めします。

ここでは、私たちのお気に入りの機能をいくつか紹介します（英語記事）

-   [<u data-renderer-mark="true">自動テスト</u>](https://laravel.com/docs/testing "https://laravel.com/docs/testing")
    
-   [<u data-renderer-mark="true">Web Socketsによる一斉配信</u>](https://laravel.com/docs/broadcasting "https://laravel.com/docs/broadcasting")
    
-   [<u data-renderer-mark="true">Formのバリデーション</u>](https://laravel.com/docs/validation#form-request-validation "https://laravel.com/docs/validation#form-request-validation")
    
-   [<u data-renderer-mark="true">キュー</u>](https://laravel.com/docs/queues "https://laravel.com/docs/queues")
    
-   [<u data-renderer-mark="true">タスクスケジュール</u>](https://laravel.com/docs/scheduling "https://laravel.com/docs/scheduling")
    
-   [<u data-renderer-mark="true">ファイルストレージ</u>](https://laravel.com/docs/filesystem "https://laravel.com/docs/filesystem")
    
-   [<u data-renderer-mark="true">HTTP Client</u>](https://laravel.com/docs/http-client "https://laravel.com/docs/http-client")
    
-   [<u data-renderer-mark="true">データベースのデータ投入</u>](https://laravel.com/docs/seeding "https://laravel.com/docs/seeding")
    
-   [<u data-renderer-mark="true">コレクション</u>](https://laravel.com/docs/collections "https://laravel.com/docs/collections")
    
-   [<u data-renderer-mark="true">サブスク課金</u>](https://spark.laravel.com/?ref=bootcamp.laravel.com "https://spark.laravel.com/?ref=bootcamp.laravel.com")
    
-   [<u data-renderer-mark="true">管理者画面</u>](https://nova.laravel.com/?ref=bootcamp.laravel.com "https://nova.laravel.com/?ref=bootcamp.laravel.com")
    
-   [<u data-renderer-mark="true">サーバー管理</u>](https://forge.laravel.com/?ref=bootcamp.laravel.com "https://forge.laravel.com/?ref=bootcamp.laravel.com")
    
-   [<u data-renderer-mark="true">サーバーレス デプロイ</u>](https://vapor.laravel.com/?ref=bootcamp.laravel.com "https://vapor.laravel.com/?ref=bootcamp.laravel.com")
    

Laravelについてのご意見をお聞かせください！XでLaravelをフォローして、あなたの感想をお聞かせください。何か素晴らしいものを作りましょう！