Laravel 通知系統玩膩了?資深工程師帶你手刻 Custom Channel,從簡訊到 LINE Notify 全搞定!

2025/08/22 | Laravel技術分享

Laravel 通知系統玩膩了?資深工程師帶你手刻 Custom Channel,從簡訊到 LINE Notify 全搞定!

嗨,我是浪花科技的資深工程師 Eric。在 Laravel 的世界裡,`Notifications`(通知系統)大概是每個開發者最愛的功能之一。設定好 Mail Driver,一行 `$user->notify(new InvoicePaid($invoice));` 就能優雅地寄出帳單通知,實在是太方便了。但隨著專案越長越大、需求越來越刁鑽,你會發現內建的 Mail、Database、Slack 這些管道,漸漸地開始不夠用了。

「如果我想串接台灣本土的簡訊服務商怎麼辦?」「客戶希望訂單成立時,通知能直接發到他們內部的企業通訊軟體上。」「我們有個 IoT 裝置,需要在特定事件發生時閃爍燈號,這也能用 Laravel 通知系統做嗎?」

當這些問題出現時,很多開發者可能會選擇在 Controller 或 Service 裡面直接用 Guzzle 或 Laravel 的 HTTP Client 硬幹 API。但相信我,身為一個有點龜毛的工程師,我會告訴你:這不是最優雅的解法。今天,就讓我帶你深入 Laravel 通知系統的核心,教你如何打造自己的「Custom Channel」,讓你的 Laravel 應用程式真正學會「說各種語言」,無論是 LINE Notify、台廠簡訊,還是你家的智慧音箱,通通難不倒它!

為何內建的通知管道(Channel)還不夠?

Laravel 本身提供了相當豐富的通知管道,像是郵件 (Mail)、資料庫 (Database)、廣播 (Broadcast)、簡訊 (Vonage) 和 Slack。對於許多標準應用場景來說,這些已經綽綽有餘。然而,真實世界的商業邏輯遠比想像中複雜。硬幹 API 的問題在於,你的程式碼會變得很零散、難以維護和測試。想像一下,到處都是 `Http::post(…)` 的呼叫,如果哪天 API 規格改了,你得改多少地方?

這就是 Custom Channel 派上用場的時候了。我們需要一個統一的、可重複使用的介面來處理這些「非標準」的通知。以下是一些典型的場景:

  • 串接特定地區的服務商:例如台灣常見的三竹簡訊、every8d,或是東南亞流行的通訊軟體。
  • 整合企業內部系統:發送通知到企業微信、Jandi、Microsoft Teams 等內部協作平台。
  • 與物聯網 (IoT) 設備互動:當伺服器發生錯誤時,觸發辦公室的警報紅燈。
  • 串接任何第三方 API:只要對方提供 API,你就能把它包裝成一個 Laravel Channel,例如發送到 Discord、Telegram,甚至是寫入到 Trello 卡片中。

透過建立自訂管道,我們可以將所有通知邏輯封裝起來,讓業務邏輯層的程式碼保持乾淨,只需專注在「何時」發送「什麼」通知,而不用管「如何」發送。這就是軟體工程中常說的「關注點分離 (Separation of Concerns)」,也是一個資深工程師的基本素養啦。

實戰:從零到一打造 LINE Notify 自訂管道

講了這麼多理論,不如直接動手做。我們來實作一個非常實用的範例:建立一個可以發送通知到 LINE Notify 的 Custom Channel。LINE Notify 的 API 非常簡單,只需要一個 POST 請求,很適合當作我們的第一個自訂管道。

步驟一:建立 Channel 類別

首先,我們使用 Artisan 指令來建立一個新的 Channel 類別。打開你的終端機,輸入:

php artisan make:channel LineNotifyChannel

這個指令會在 `app/Channels` 目錄下建立一個 `LineNotifyChannel.php` 檔案。打開它,你會看到一個基本的模板:

<?php

namespace App\Channels;

use Illuminate\Notifications\Notification;

class LineNotifyChannel
{
    /**
     * Send the given notification.
     *
     * @param  mixed  $notifiable
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return void
     */
    public function send($notifiable, Notification $notification)
    {
        // ...
    }
}

核心就在這個 `send` 方法。它接收兩個參數:`$notifiable` (通常是 User 模型,也就是接收通知的對象) 和 `$notification` (我們建立的通知內容本身)。我們的任務就是在這裡面把通知發出去。

步驟二:實現 `send` 方法與路由邏輯

要發送 LINE Notify,我們需要兩樣東西:接收者的 Access Token 和要發送的訊息。訊息可以從 `$notification` 物件中取得,而 Access Token 則需要從 `$notifiable` (User) 取得。

Laravel 有個很漂亮的慣例:當一個通知要透過某個 Channel 發送時,它會在 Notifiable 模型上尋找一個名為 `routeNotificationFor{ChannelName}` 的方法。所以,我們需要在 `User` 模型中加入一個 `routeNotificationForLineNotify` 方法:

// In app/Models/User.php

public function routeNotificationForLineNotify()
{
    // 這個 line_notify_token 欄位是你需要自己加到 users 資料表中的
    // 用來儲存每個使用者的 LINE Notify Access Token
    return $this->line_notify_token;
}

接著,我們來完成 `LineNotifyChannel` 的 `send` 方法。我們會使用 Laravel 的 HTTP Client 來發送請求。

<?php

namespace App\Channels;

use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Http;

class LineNotifyChannel
{
    const API_ENDPOINT = 'https://notify-api.line.me/api/notify';

    public function send($notifiable, Notification $notification)
    {
        // 1. 從 User Model 取得 Access Token
        if (! $token = $notifiable->routeNotificationFor('LineNotify', $notification)) {
            return; // 如果沒有 token,就直接返回不發送
        }

        // 2. 從 Notification 類別取得要發送的訊息內容
        // 慣例是呼叫 to{ChannelName} 方法
        $message = $notification->toLineNotify($notifiable);

        // 3. 使用 HTTP Client 發送請求
        $response = Http::withToken($token)
            ->asForm()
            ->post(self::API_ENDPOINT, [
                'message' => $message,
            ]);

        // 這裡可以加上錯誤處理,例如記錄 log 或拋出 exception
        if ($response->failed()) {
            // Log::error('LINE Notify failed: ' . $response->body());
        }
    }
}

你看,所有跟 LINE Notify API 溝通的髒活累活都封裝在這個類別裡了。超乾淨!

步驟三:建立專屬的 Notification

現在我們有了快遞員 (Channel),還需要信件內容 (Notification)。再次使用 Artisan:

php artisan make:notification OrderShipped

這會在 `app/Notifications` 目錄下建立 `OrderShipped.php`。我們需要修改它,讓它知道要使用 `LineNotifyChannel`,並且提供給這個 Channel 需要的訊息格式。

<?php

namespace App\Notifications;

use App\Channels\LineNotifyChannel; // 記得引入我們的自訂 Channel
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

class OrderShipped extends Notification implements ShouldQueue
{
    use Queueable;

    public $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    // 決定這個通知要透過哪些管道發送
    public function via($notifiable)
    {
        return [LineNotifyChannel::class];
    }

    // 準備要給 LineNotifyChannel 的資料
    public function toLineNotify($notifiable)
    {
        return "親愛的用戶,您的訂單 #{$this->order->id} 已經出貨囉!";
    }
}

注意到 `via` 方法回傳了我們的 `LineNotifyChannel::class`,而 `toLineNotify` 方法則回傳了要發送的字串。這樣一來,這個通知就和我們的自訂管道完美地結合了。

步驟四:觸發通知!

萬事俱備,只欠東風。現在,在你應用程式的任何地方,只要你想發送這個通知,只需一行程式碼:

use App\Models\User;
use App\Notifications\OrderShipped;

$user = User::find(1);
$order = Order::find(123);

$user->notify(new OrderShipped($order));

執行下去,你的 LINE 就會收到通知了!是不是很神奇?我們把複雜的 API 串接流程,變成了一個可高度重用、語意清晰的系統。

進階技巧與工程師的囉嗦

搞定 Custom Channel 只是第一步,要在產品環境中穩健地運行,還有幾個坑你得注意:

  • 非同步處理:外部 API 呼叫可能會很慢,甚至超時。你絕對不想讓使用者在網頁上空等 API 回應。記得在你的 Notification 類別上實現 `ShouldQueue` 介面,這樣 Laravel 就會自動把通知丟到背景佇列去處理,大幅提升使用者體驗。這也是為什麼我在上面的範例偷偷加了 `implements ShouldQueue` 的原因,好習慣要養成!
  • On-Demand Notifications:有時候你需要通知的對象並不是你系統裡的使用者,可能只是一個 Email 地址或是一個 Webhook URL。這時可以用 On-Demand Notifications:
    Notification::route('mail', 'taylor@example.com')
                ->route('slack', '#slack-channel-name')
                ->notify(new InvoicePaid($invoice));

    這對於臨時性的通知非常有用。

  • 設定檔管理:千萬別把 API Endpoint 或金鑰寫死在 Channel 類別裡。你應該把它們放在 `config/services.php` 檔案中,然後在 Channel 裡用 `config(‘services.line.endpoint’)` 的方式讀取。這樣做方便你在不同環境(開發、測試、正式)使用不同的設定。
  • 健壯的錯誤處理:API 不可能永遠 100% 可用。在 `send` 方法中,務必檢查 API 回應,並做好日誌紀錄。搭配佇列的重試機制 (`$tries` / `$backoff`),可以讓你的通知系統在面對暫時性網路問題時更加穩固。

結論:打開 Laravel 通知系統的無限可能

今天我們從為什麼需要自訂通知管道,一路聊到如何從零開始打造一個功能完整的 LINE Notify Channel。掌握了這個技巧,你就等於解鎖了 Laravel 通知系統的「超能力」。

重點在於那個設計模式:將「如何發送」的邏輯(Channel)與「發送什麼內容」的邏輯(Notification)徹底分開。這讓你的程式碼不僅更乾淨、更好維護,也具備了極高的擴充性。未來無論是串接 Facebook Messenger、Telegram Bot,還是某個新潮的 IoT 裝置,你都可以用同樣的模式,快速打造出對應的 Channel,而不需要動到任何核心的業務邏輯程式碼。

希望這篇文章能幫助你打開新的思路。別再把 API 呼叫寫得到處都是了,試著用 Laravel 更優雅的方式來解決問題吧!

如果你對 Laravel 系統開發、API 串接,或是企業級的 WordPress 解決方案有更深入的需求,或是遇到了難以解決的技術瓶頸,別害羞,歡迎隨時與浪花科技的團隊聊聊。我們很樂意用我們的技術經驗,為你的專案帶來價值。

延伸閱讀

常見問題 (FAQ)

Q1: 我需要為每個不同的第三方服務都建立一個 Custom Channel 嗎?

是的,這是最佳實踐。為每個服務(例如:三竹簡訊、企業微信)建立一個專屬的 Channel,可以讓你的程式碼保持高度模組化和關注點分離。這樣做的好處是,當某個服務的 API 變更時,你只需要修改對應的 Channel 檔案,而不會影響到其他程式碼。這也讓你的 Channel 更容易被團隊其他成員或在其他專案中重複使用。

Q2: 如何在 Custom Channel 中取得使用者的設定,例如 LINE Notify 的 Access Token?

最佳方式是遵循 Laravel 的慣例,在你的 Notifiable 模型(通常是 `App\Models\User`)中定義一個 `routeNotificationFor{ChannelName}` 的方法。例如,對於 `LineNotifyChannel`,你就在 User 模型裡建立一個 `routeNotificationForLineNotify()` 方法,並讓它回傳該使用者的 Access Token。Laravel 在執行 `send` 方法前會自動呼叫這個方法來取得「投遞地址」。

Q3: 如果我的通知需要同時發送到多個自訂管道,該怎麼做?

非常簡單!在你的 Notification 類別中,`via($notifiable)` 方法負責決定通知要走哪些管道。你只需要讓它回傳一個包含所有目標 Channel 類別名稱的陣列即可。例如:`return [LineNotifyChannel::class, SmsChannel::class, ‘database’];`。Laravel 會依序遍歷這個陣列,並為每個 Channel 執行其對應的 `send` 或 `to{ChannelName}` 邏輯。

 
立即諮詢,索取免費1年網站保固