+12

Firebase Cloud Messaging via Laravel

I. Giới thiệu

  • Sau nhiều lần mình làm về Fcm, mà chẳng thấy bài nào ns đến Laravel với FCM, có thì toàn bảo dùng package này nọ (sad).
  • Vậy nên như tiêu đề, bài viết này mình sẽ giới thiệu cách gửi notification sử dụng Firebase và Laravel, có thể subscribe topic được nhiều device token qua đó nâng cao hiệu năng gửi notification, (một vấn đề mình đã gặp trong dự án vừa qua), sử dụng queue...
  • Đã có nhiều bài viết đã hướng dẫn cách subscribe topic theo từng device token, subscribe topic khi vào một action nào đó nhưng ở bài viết này mình sẽ hướng dẫn theo từng case, từng logic trong dự án... khi muốn push vào một thời gian nhất định với lượng device token nhất định.

II. Hướng dẫn cách làm

1. Cách lấy server key

  • Có rất nhiều bài viết hướng dẫn tạo app trên firebase và bạn có thể làm theo và lấy key trong tab Cloud Messaging (Project Setting) có dạng Server key: AAA..xyz

2. Setup trong Laravel

  • Tạo một interface NotificationService
<?php

namespace App\Services\Notification;

interface NotificationService
{
    public function sendBatchNotification($deviceTokens, $data);

    public function sendNotification($data);

    public function subscribeTopic($deviceTokens, $topicName);

    public function unsubscribeTopic($deviceTokens, $topicName);
}

  • Tạo Một FcmService như sau
<?php

namespace App\Services\Notification;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use Exception;

class FcmService implements NotificationService
{
    /**
     * @param $deviceTokens
     * @param $data
     * @throws GuzzleException
     */
    public function sendBatchNotification($deviceTokens, $data = [])
    {
        self::subscribeTopic($deviceTokens, $data['topicName']);
        self::sendNotification($data, $data['topicName']);
        self::unsubscribeTopic($deviceTokens, $data['topicName']);
    }

    /**
     * @param $data
     * @param $topicName
     * @throws GuzzleException
     */
    public function sendNotification($data, $topicName = null)
    {
        $url = 'https://fcm.googleapis.com/fcm/send';
        $data = [
            'to' => '/topics/' . $topicName,
            'notification' => [
                'body' => $data['body'] ?? 'Something',
                'title' => $data['title'] ?? 'Something',
                'image' => $data['image'] ?? null,
            ],
            'data' => [
                'url' => $data['url'] ?? null,
                'redirect_to' => $data['redirect_to'] ?? null,
            ],
            'apns' => [
                'payload' => [
                    'aps' => [
                        'mutable-content' => 1,
                    ],
                ],
                'fcm_options' => [
                    'image' => $data['image'] ?? null,
                ], 
            ],
        ];

        $this->execute($url, $data);
    }

    /**
     * @param $deviceToken
     * @param $topicName
     * @throws GuzzleException
     */
    public function subscribeTopic($deviceTokens, $topicName = null)
    {
        $url = 'https://iid.googleapis.com/iid/v1:batchAdd';
        $data = [
            'to' => '/topics/' . $topicName,
            'registration_tokens' => $deviceTokens,
        ];

        $this->execute($url, $data);
    }

    /**
     * @param $deviceToken
     * @param $topicName
     * @throws GuzzleException
     */
    public function unsubscribeTopic($deviceTokens, $topicName = null)
    {
        $url = 'https://iid.googleapis.com/iid/v1:batchRemove';
        $data = [
            'to' => '/topics/' . $topicName,
            'registration_tokens' => $deviceTokens,
        ];

        $this->execute($url, $data);
    }

    /**
     * @param $url
     * @param array $dataPost
     * @param string $method
     * @return bool
     * @throws GuzzleException
     */
    private function execute($url, $dataPost = [], $method = 'POST')
    {
        $result = false;
        try {
            $client = new Client();
            $result = $client->request($method, $url, [
                'headers' => [
                    'Content-Type' => 'application/json',
                    'Authorization' => 'key=' . $keyInHere,
                ],
                'json' => $dataPost,
                'timeout' => 300,
            ]);

            $result = $result->getStatusCode() == Response::HTTP_OK;
        } catch (Exception $e) {
            Log::debug($e)
        }

        return $result;
    }
}
  • Ý nghĩa từng hàm

-subscribeTopic: Hàm này thực hiện để subscribe topic trên firebase và thực hiện được với nhiều device token.

-unsubscribeTopic: Hàm này thực hiện để unsubscribe topic (cancel topic) trên firebase và thực hiện được với nhiều device token.

-sendNotification: Dựa trên topic name và device token đã đươc đăng ký, hàm này thực hiên gửi notification dựa vào topic đã được gửi qua hàm subscribeTopic, ý nghĩa của các attrubte

+to: để biết push notification đến topic nafo

+notification: tieu đề và nội dung của notification

+apns (for IOS): các option định sẵn như image chẳng hạn.

+data: thêm các custom attribute.

-sendBatchNotification: Hàm này sẽ thực hiện tuần tự subscribeTopic -> sendNotification -> unsubscribeTopic.

  • Tạo file PushNotificationJob như sau:
<?php

namespace App\Jobs;

use App\Services\Notification\NotificationService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class PushNotificationJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $serviceMethod;

    protected $methodParams;

    /**
     * Notification constructor.
     * @param $serviceMethod
     * @param $methodParams
     * @param string $methodHttp
     */
    public function __construct($serviceMethod, $methodParams = [[]])
    {
        $this->serviceMethod = $serviceMethod;
        $this->methodParams = $methodParams;
    }

    /**
     * @param NotificationService $notificationService
     */
    public function handle(NotificationService $notificationService)
    {
        call_user_func_array(
            [
                $notificationService,
                $this->serviceMethod,
            ],
            $this->methodParams
        );
    }
}
  • Giờ thì xem cách nó hoạt động thôi !!! Giả sử có một bài toán như sau: Gửi notification đến những device có ngày sinh nhật vào ngày 25-10 (Vơi device token được lưu trong bảng User)
$deviceTokens = User::whereDay('birthday', now()->format('d'))
    ->whereMonth('birthday', now()->format('m'))
    ->pluck('device_token')
    ->toArray();
PushNotificationJob::dispatch('sendBatchNotification', [
    $deviceTokens,
    [
        'topicName' => 'birthday',
        'title' => 'Chúc mứng sinh nhật',
        'body' => 'Chúc bạn sinh nhật vui vẻ',
        'image' => 'https://picsum.photos/536/354',
    ],
]);

III. Kết thúc

Trên đây là cách gửi push notification được setup trên server sử dụng laravel, mình nghĩ n rất hiệu quả với những project có từng trừng hợp, theo từng rule rõ ràng mà k để bị phí tài nguyên của sản phẩm, Hi vọng có thể giúp ích được cho các bạn trong qua trình phát triển sản phẩm \m/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí