aksafan/yii2-fcm-both-api

Yii2 Extension for sending push notification with both Firebase Cloud Messaging (FCM) HTTP Server Protocols (APIs).

Installs: 34 041

Dependents: 1

Suggesters: 0

Security: 0

Stars: 12

Watchers: 3

Forks: 12

Open Issues: 0

Type:yii2-extension

0.10.0 2020-09-16 12:25 UTC

This package is auto-updated.

Last update: 2024-04-16 20:27:22 UTC


README

Yii2 Extension for sending push notification with both Firebase Cloud Messaging (FCM) HTTP Server Protocols (APIs).

Latest Stable Version Total Downloads Build Status

This extension supports sending push notification through both currently supported FCM API versions:

Note: The XMPP protocol is not currently supported.

Installation

The preferred way to install this extension is through composer. Check the composer.json for this extension's requirements and dependencies.

To install, either run

$ php composer.phar require aksafan/yii2-fcm-both-api

or add

"aksafan/yii2-fcm-both-api": "*"

to the require section of your composer.json file.

Configuration

In order to use this library, you have to configure the Fcm class in your application configuration.

For ApiV1:

return [
    //....
    'components' => [
        'fcm' => [
             'class' => 'aksafan\fcm\source\components\Fcm',
             'apiVersion' => \aksafan\fcm\source\requests\StaticRequestFactory::API_V1,
             'apiParams' => [
                 'privateKey' => '/path/to/your/file/privateKeyFile.json',
             ],
        ],
    ]
];

privateKey - used to authenticate the service account and authorize it to access Firebase services. You must generate a private key file in JSON format and use this key to retrieve a short-lived OAuth 2.0 token. privateKey can be set with json-file '/path/to/your/file/privateKeyFile.json' or simply with json-string '{"type":"service_account"}'

For Legacy API:

return [
    //....
    'components' => [
        'fcm' => [
             'class' => 'aksafan\fcm\source\components\Fcm',
             'apiVersion' => \aksafan\fcm\source\requests\StaticRequestFactory::LEGACY_API,
             'apiParams' => [
                 'serverKey' => 'aef',
                 'senderId' => 'fwef',
             ],
        ],
    ]
];

serverKey - a server key that authorizes your app server for access to Google services, including sending messages via the Firebase Cloud Messaging legacy protocols. You obtain the server key when you create your Firebase project. You can view it in the Cloud Messaging tab of the Firebase console Settings pane.

senderId - a unique numerical value created when you create your Firebase project, available in the Cloud Messaging tab of the Firebase console Settings pane. The sender ID is used to identify each sender that can send messages to the client app.

Also add this to your Yii.php file in the root directory of the project for IDE code autocompletion.
/**
 * Class WebApplication
 * Include only Web application related components here.
 *
 * @property \aksafan\fcm\source\components\Fcm $fcm
 */
class WebApplication extends yii\web\Application
{
}
Now you can get access to extension's methods through:
Yii::$app->fcm
In order to use both HTTP v1 and legacy API at the same time, you need to register them separately:
return [
    //....
    'components' => [
        'fcmApiV1' => [
            'class' => 'aksafan\fcm\source\components\Fcm',
            'apiVersion' => \aksafan\fcm\source\requests\StaticRequestFactory::API_V1,
            'apiParams' => [
                'privateKey' => '/path/to/your/file/privateKeyFile.json',
            ],
        ],
        'fcmLegacyApi' => [
            'class' => 'aksafan\fcm\source\components\Fcm',
            'apiVersion' => \aksafan\fcm\source\requests\StaticRequestFactory::LEGACY_API,
            'apiParams' => [
                'serverKey' => 'aef',
                'senderId' => 'fwef',
            ],
        ],
    ]
];

Now you can use Yii::$app->fcmApiV1 when need APIV1 and Yii::$app->fcmLegacyApi for legacy one.

Basic Usage

N.B. The main thing about this library is to be used in queues, so we tried to balance between OOP and low amount of objects used.

APIv1

The APIv1 part of extension can send:

  1. A message to a specific token (device).
  2. A message to a given topic.
  3. A message to several topics by condition.

A message can contain:

  1. Two types of messages: Notification messages and Data messages (both are optional).
  2. Their combination.
  3. Target platform specific configuration.
Send push-notification to a single token (device)

You need to have a registration token for the target device. Registration tokens are strings generated by the client FCM SDKs. Each of the Firebase client SDKs are able to generate these registration tokens: iOS, Android, Web. In order to sent push to the single token you need to use setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token') method with \aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN constant:

/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->send();
Send push-notification to a topic

Based on the publish/subscribe model, FCM topic messaging allows you to send a message to multiple devices that have opted in to a particular topic. You compose topic messages as needed, and FCM handles routing and delivering the message reliably to the right devices.

To (un)subscribe devices to a topic look for Topic management section below

For example, users of a local weather forecasting app could opt in to a “severe weather alerts” topic and receive notifications of storms threatening specified areas. Users of a sports app could subscribe to automatic updates in live game scores for their favorite teams.

The main things to keep in mind about topics:

  • Developers can choose any topic name that matches the regular expression: [a-zA-Z0-9-_.~%]+.
  • Topic messaging supports unlimited topics and subscriptions for each app.
  • Topic messaging is best suited for content such as news, weather, or other publicly available information.
  • Topic messages are optimized for throughput rather than latency. For fast, secure delivery to single devices or small groups of devices, target messages to registration tokens, not topics.

In order to sent push to the topic you need to use setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOPIC, 'your_token') method with \aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOPIC constant:

/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOPIC, 'some-topic')
    ->send();
Send push-notification to a combination of topics by condition

The condition is a boolean expression that specifies the target topics. For example, the following condition will send messages to devices that are subscribed to 'TopicA' and either 'TopicB' or 'TopicC':

"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"

FCM first evaluates any conditions in parentheses, and then evaluates the expression from left to right. In the above expression, a user subscribed to any single topic does not receive the message. Likewise, a user who does not subscribe to TopicA does not receive the message. These combinations do receive it:

  • TopicA and TopicB
  • TopicA and TopicC

In order to sent push to the several topics by condition you need to use setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOPIC_CONDITION, 'your_token') method with \aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOPIC_CONDITION constant:

$condition = "'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)";
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOPIC_CONDITION, $condition)
    ->send();
Send push-notification with only 'Data' message type.
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->setData(['a' => '1', 'b' => 'test'])
    ->send();
Send push-notification with only 'Notification' message type.
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->setNotification('Test Title', 'Test Description')
    ->send();
Send push-notification with both 'Notification' and 'Data' message type.
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->setData(['a' => '1', 'b' => 'test'])
    ->setNotification('Test Title', 'Test Description')
    ->send();
Send push-notification without 'Notification' and 'Data' message type.
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->send();
Send push-notification with platform specific configuration.
Android config:
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->setAndroidConfig([
        'ttl' => '3600s',
        'priority' => 'normal',
        'notification' => [
            'title' => 'Android Title',
            'body' => 'Android Description.',
            'icon' => 'stock_ticker_update',
            'color' => '#ff0000',
        ],
    ])
    ->send();
APNs config:
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->setApnsConfig([
        'headers' => [
            'apns-priority' => '10',
        ],
        'payload' => [
            'aps' => [
                'alert' => [
                    'title' => 'iOS Title',
                    'body' => 'iOS Description.',
                ],
                'badge' => 42,
            ],
        ],
    ])
    ->send();
Web-push config:
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\apiV1\MessageOptionsBuilder::TOKEN, 'your_token')
    ->setWebPushConfig([
        'notification' => [
            'title' => 'Web push Title',
            'body' => 'Web push Description.',
            'icon' => 'https://my-server/icon.png',
        ],
    ])
    ->send();
All together config:
/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => 'test'])
    ->setNotification('Test Title', 'Test Description')
    ->setAndroidConfig([
        'ttl' => '3600s',
        'priority' => 'normal',
        'notification' => [
            'title' => 'Android Title',
            'body' => 'Andorid Description.',
            'icon' => 'push_icon',
            'color' => '#ff0000',
        ],
    ])
    ->setApnsConfig([
        'headers' => [
            'apns-priority' => '10',
        ],
        'payload' => [
            'aps' => [
                'alert' => [
                    'title' => 'iOS Title',
                    'body' => 'iOS Description.',
                ],
                'badge' => 42,
            ],
        ],
    ])
    ->setWebPushConfig([
        'notification' => [
            'title' => 'Web push Title',
            'body' => 'Web push Description.',
            'icon' => 'https://my-server/icon.png',
        ],
    ])
    ->send();

N.B. Pay attention that platform specific config will replace the general one.

In example above Android client will receive this notification info:

'title' => 'Android Title',
'body' => 'Android Description.'

and NOT this:

'title' => 'Test Title',
'body' => 'Test Description.'
Handling response.

After sending the request to FCM you will get an instance of \aksafan\fcm\source\responses\apiV1\TokenResponse.

/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => 'test'])
    ->setNotification('Test Title', 'Test Description')
    ->send();
    
if ($result->isResultOk()) {
    echo $result->getRawMessageId();
    echo $result->getMessageId();
} else {
    $tokensToDelete = $result->getTokensToDelete();
    $errorDetails = $result->getErrorDetails();
    echo $result->getErrorStatusDescription();
}

If the result is OK you can get raw message from FCM in format - projects/your_project_id/messages/message_id:

$result->getRawMessageId();

or only the message ID in format - message_id:

$result->getMessageId();

If something has happened, you can get error description with the information about the problem:

$result->getErrorStatusDescription();

Also tokens, that should be deleted from your DB, if the problem was with invalid tokens:

$result->getTokensToDelete();

As well as the technical information from FCM:

$result->getError();
$result->getErrorStatus();
$result->getErrorCode();
$result->getErrorMessage();
$result->getErrorDetails();
Validating messages (dry run mode).

You can validate a message by sending a validation-only request to the Firebase REST API.

/** @var \aksafan\fcm\source\responses\apiV1\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => 'test'])
    ->setNotification('Test Title', 'Test Description')
    ->validateOnly()
    ->send();
    
if ($result->isResultOk()) {
    echo $result->getRawMessageId();
} else {
    echo $result->getErrorStatusDescription();
}

If the message is invalid, you will receive info in error description:

$result->getErrorStatusDescription()

If it is valid, you will get fake_message - projects/your_project_id/messages/fake_message_id:

$result->getMessageId();

Legacy API

The Legacy API part of extension can send:

  1. A message to a specific token (device).
  2. A message to a several tokens (devices).
  3. A message to device group.
  4. A message to a given topic.
  5. A message to several topics by condition.

A message can contain:

  1. Two types of messages: Notification messages and Data messages (both are optional).
  2. Their combination.
  3. Target platform specific configuration.

Note: Legacy HTTP Server Protocol is still under support, is used by many people and is not in trouble of being deprecated, but Google aimed us to use HTTP v1 API.

Send push-notification to a single token (device)

You need to have a registration token for the target device. Registration tokens are strings generated by the client FCM SDKs. Each of the Firebase client SDKs are able to generate these registration tokens: iOS, Android, Web.

In order to sent push to the single token you need to use setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, 'your_token') method with \aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN constant:

/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => '2'])
    ->setNotification('Send push-notification to a single token (device)', 'Test description')
    ->send();
Send push-notification to multiple tokens (devices)

Max amount of tokens in one request is 1000. In order to sent push to the single token you need to use setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKENS, ['your_token_1', 'your_token_2', 'your_token_3']) method with \aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKENS constant:

$tokens = [
    'your_token_1',
    'your_token_2',
    'your_token_3',
];
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKENS, $tokens)
    ->setData(['a' => '1', 'b' => '2'])
    ->setNotification('Send push-notification to multiple tokens (devices)', 'Test description')
    ->send();
Send push-notification to a topic

Based on the publish/subscribe model, FCM topic messaging allows you to send a message to multiple devices that have opted in to a particular topic. You compose topic messages as needed, and FCM handles routing and delivering the message reliably to the right devices.

For example, users of a local weather forecasting app could opt in to a “severe weather alerts” topic and receive notifications of storms threatening specified areas. Users of a sports app could subscribe to automatic updates in live game scores for their favorite teams.

The main things to keep in mind about topics:

  • Developers can choose any topic name that matches the regular expression: [a-zA-Z0-9-_.~%]+.
  • Topic messaging supports unlimited topics and subscriptions for each app.
  • Topic messaging is best suited for content such as news, weather, or other publicly available information.
  • Topic messages are optimized for throughput rather than latency. For fast, secure delivery to single devices or small groups of devices, target messages to registration tokens, not topics.

In order to sent push to the topic you need to use setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOPIC, 'your_token') method with \aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOPIC constant and createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_SENDING) with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_SENDING constant:

/** @var \aksafan\fcm\source\responses\legacyApi\TopicResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_SENDING)
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOPIC, 'a-topic')
    ->setNotification('Test title', 'Test description')
    ->send();
Send push-notification to a combination of topics by condition

The condition is a boolean expression that specifies the target topics. For example, the following condition will send messages to devices that are subscribed to 'TopicA' and either 'TopicB' or 'TopicC':

"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"

FCM first evaluates any conditions in parentheses, and then evaluates the expression from left to right. In the above expression, a user subscribed to any single topic does not receive the message. Likewise, a user who does not subscribe to TopicA does not receive the message. These combinations do receive it:

  • TopicA and TopicB
  • TopicA and TopicC

In order to sent push to the several topics by condition you need to use setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOPIC_CONDITION, 'your_token') method with \aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOPIC_CONDITION constant: and createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_SENDING) with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_SENDING constant:

$condition = "'a-topic' in topics && ('b-topic' in topics || 'b-topic' in topics)";
/** @var \aksafan\fcm\source\responses\legacyApi\TopicResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_SENDING)
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOPIC_CONDITION, $condition)
    ->setNotification('Test title', 'Test description')
    ->send();
Send push-notification to a group of tokens (devices)

FCM device groups allows you to send a message to multiple devices that have opted in to a particular group. You compose group of devices as needed, and FCM handles routing and delivering the message reliably to the right devices.

In order to sent push to the topic you need to use setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::GROUP, 'your_notification_key') method with \aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::GROUP constant: and createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_SENDING) with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_SENDING constant:

$notificationKey = 'your_notification_key';
/** @var \aksafan\fcm\source\responses\legacyApi\GroupResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_SENDING)
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::GROUP, $notificationKey)
    ->setData(['a' => '1', 'b' => '2'])
    ->setNotification('Test title', 'Test description')
    ->send();
Send push-notification with only 'Data' message type.
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => 'test'])
    ->send();
Send push-notification with only 'Notification' message type.
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setNotification('Test title', 'Test Description')
    ->send();
Send push-notification with both 'Notification' and 'Data' message type.
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => 'test'])
    ->setNotification('Send push-notification with both \'Notification\' and \'Data\' message type.', 'Test Description')
    ->send();
Send push-notification without 'Notification' and 'Data' message type.
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = Yii::$app
    ->fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, 'your_token')
    ->send();
Send push-notification with platform specific configuration.
Android config:
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setAndroidConfig([
        'title' => 'Android Title',
        'body' => 'Android Description.',
        'icon' => 'stock_ticker_update',
        'color' => '#ff0000',
    ])
    ->setPriority(\aksafan\fcm\source\helpers\OptionsHelper::HIGH)
    ->send();
APNs config:
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setApnsConfig([
        'title' => 'iOS Title',
        'body' => 'iOS Description.',
        'title_loc_key' => 'iOS Title loc key.',
        'badge' => '42',
        'sound' => 'bingbong.aiff',
    ])
    ->send();
Web-push config:
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setWebPushConfig([
        'title' => 'Web push Title',
        'body' => 'Web push Description.',
        'icon' => 'https://my-server/icon.png',
        'click_action' => 'click-action',
    ])
    ->send();
All together config.

You can set additional configurations with these methods:

    ->setCollapseKey(')
    ->setPriority()
    ->setContentAvailable()
    ->setMutableContent()
    ->setTimeToLive()
    ->setRestrictedPackageName()
    ->validateOnly()

All together can be:

/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => '2'])
    ->setNotification('Test title', 'Test description')
    ->setAndroidConfig([
        'title' => 'Android Title',
        'body' => 'Android Description.',
        'icon' => 'stock_ticker_update',
        'color' => '#ff0000',
    ])
    ->setApnsConfig([
        'title' => 'iOS Title',
        'body' => 'iOS Description.',
        'title_loc_key' => 'iOS Title loc key.',
        'badge' => '42',
        'sound' => 'bingbong.aiff',
    ])
    ->setWebPushConfig([
        'title' => 'Web push Title',
        'body' => 'Web push Description.',
        'icon' => 'https://my-server/icon.png',
        'click_action' => 'click-action',
    ])
    ->setCollapseKey('collapse_key')
    ->setPriority(\aksafan\fcm\source\helpers\OptionsHelper::NORMAL)
    ->setContentAvailable(true)
    ->setMutableContent(false)
    ->setTimeToLive(300)
    ->setRestrictedPackageName('restricted_package_mame')
    ->validateOnly(false)
    ->send();

N.B. Pay attention that platform specific configuration will replace the general one and repeating from other platform configurations (shortage in legacy API version), one by one in order:

    GeneralNotificationConfig
         ->
    AndroidConfig
         ->
    ApnsConfig
         ->
    WebPushConfig

In example above any client will receive this notification info:

'title' => 'Web push Title',
'body' => 'Web push Description.',
'icon' => 'https://my-server/icon.png',
'color' => '#ff0000',
'title_loc_key' => 'iOS Title loc key.',
'badge' => '42',
'sound' => 'bingbong.aiff',
'click_action' => 'click-action',

and NOT all mentioned.

Handling response.

After sending the request to FCM you will get an instance of these:

  • \aksafan\fcm\source\responses\legacyApi\TokenResponse
  • \aksafan\fcm\source\responses\legacyApi\TopicResponse
  • \aksafan\fcm\source\responses\legacyApi\GroupResponse
For sending push-messages to single (multiply) token(s):
/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => '2'])
    ->setNotification('Test title', 'Test description')
    ->send();
    
if ($result->isResultOk()) {
    echo 'MessageId '.$result->getMessageId();
    echo 'NumberSuccess '.$result->getNumberSuccess();
    echo 'NumberFailure '.$result->getNumberFailure();
    echo 'NumberModification '.$result->getNumberModification();
} else {
    echo 'numberSuccess '.$result->getNumberSuccess();
    echo 'numberFailure '.$result->getNumberFailure();
    echo 'numberModification '.$result->getNumberModification();
    echo 'TokensToDelete '.$result->getTokensToDelete();
    echo 'TokensToModify '.$result->getTokensToModify();
    echo 'TokensToRetry '.$result->getTokensToRetry();
    echo 'RetryAfter '.$result->getRetryAfter();
    echo 'TokensWithError '.$result->getTokensWithError();
    echo 'ErrorStatusDescription '.$result->getErrorStatusDescription();
}

If the result is OK you can get the message ID in format - message_id:

$result->getMessageId();

You can see the number of successfully sent messages:

$result->getNumberSuccess();

The number of failed attempts:

$result->getNumberFailure();

The number of device that you need to modify their token:

$result->getNumberModification();

If something has happened, you can get error description with the information about the problem:

$result->getErrorStatusDescription();

List of common errors and info about handling them for legacy API is here

Also tokens, that should be deleted from your DB as invalid ones:

$result->getTokensToDelete();

Tokens, that should be changed in your storage - ['oldToken' => 'newToken']:

$result->getTokensToModify();

Tokens, that should be resend. You should to use exponential backoff to retry sending:

$result->getTokensToRetry();

Time to retry after if it was in response headers:

$result->getRetryAfter();

Tokens, that have errors. You should check these tokens in order to delete broken ones:

$result->getTokensWithError();
For sending push-messages to topic or topics by condition:
/** @var \aksafan\fcm\source\responses\legacyApi\TopicResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_SENDING)
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOPIC, 'a-topic')
    ->setNotification('Test title', 'Test description')
    ->send();
    
if ($result->isResultOk()) {
    echo 'MessageId '.$result->getMessageId();
} else {
    echo 'ErrorMessage '.$result->getErrorMessage();
    echo 'ErrorStatusDescription '.$result->getErrorStatusDescription();
}

If the result is OK you can get the message ID in format - message_id:

$result->getMessageId();

If something has happened, you can get error description with the information about the problem:

$result->getErrorStatusDescription();

And error message:

$result->getErrorMessage();
For sending push-messages to device group:
$notificationKey = 'your_notification_key';
/** @var \aksafan\fcm\source\responses\legacyApi\GroupResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_SENDING)
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::GROUP, $notificationKey)
    ->setData(['a' => '1', 'b' => '2'])
    ->setNotification('Test title', 'Test description')
    ->send();
    
if ($result->isResultOk()) {
    echo 'numberSuccess ' . $result->getNumberSuccess();
    echo 'numberFailure ' . $result->getNumberFailure();
} else {
    echo 'numberSuccess ' . $result->getNumberSuccess();
    echo 'numberFailure ' . $result->getNumberFailure();
    echo 'tokensFailed ' . print_r($tokensFailed = $result->getTokensFailed());
    echo 'ErrorStatusDescription '.$result->getErrorStatusDescription();
}

If the result is OK you can get the number of tokens successfully sent messages to:

$result->getNumberSuccess();

The number of tokens unsuccessfully sent messages to:

$result->getNumberFailure();

If the server attempts to send a message to a device group that has no members, the response will be with 0 success and 0 failure.

If something has happened, you can get error description with the information about the problem:

$result->getErrorStatusDescription();

And an array of tokens failed:

$result->getTokensFailed();

When a message fails to be delivered to one or more of the registration tokens associated with a notification_key, the app server should retry with backoff between retries.

Validating messages (dry run mode).

You can validate a message by sending a validation-only request to the Firebase REST API.

/** @var \aksafan\fcm\source\responses\legacyApi\TokenResponse $result */
$result = $fcm
    ->createRequest()
    ->setTarget(\aksafan\fcm\source\builders\legacyApi\MessageOptionsBuilder::TOKEN, $token)
    ->setData(['a' => '1', 'b' => 'test'])
    ->setNotification('Validating messages', 'Test Description')
    ->validateOnly()
    ->send();
    
if ($result->isResultOk()) {
    echo $result->getMessageId();
} else {
    echo $result->getErrorStatusDescription();
}

If the message is invalid, you will receive info in error description:

$result->getErrorStatusDescription()

If it is valid, you will get fake_message - -1:

$result->getMessageId();
Device group management.

There are three main definitions here:

  • groupName - is a name or identifier (e.g., it can be a username) that is unique to a given group;
  • notificationKey - identifies the device group by mapping a particular group (typically a user) to all of the group's associated registration tokens;
  • tokens - an array of registration tokens for each device you want to add to the group.

The groupName and notificationKey are unique to a group of registration tokens. It is important that groupName is unique per client app if you have multiple client apps for the same sender ID. This ensures that messages only go to the intended target app.

Optionally, Android client apps can manage device groups from the client side.

Create group:

To create a device group, you need to use createGroup(string $groupName, array $tokens) method with a name for the group and a list of registration tokens for the devices. Also creating request with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT argument.

$groupName = 'test-group';
$tokens = [
    'your_token_1',
    'your_token_2',
    'your_token_3',
    'your_token_4',
    'your_token_5',
];
/** @var \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT)
    ->createGroup($groupName, $tokens)
    ->send();
if ($result->isResultOk()) {
    echo 'NotificationKey '.$notificationKey = $result->getNotificationKey();
} else {
    echo 'getErrorStatusDescription '. $result->getErrorStatusDescription();
}

After sending the request to FCM you will get an instance of \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse.

FCM returns a new notificationKey that represents the device group. Save it and the corresponding groupName to use in subsequent operations.

If there was an error, you will receive info in error description:

$result->getErrorStatusDescription()

N.B. You can add to group up to 1,000 devices in a single request. If you provide an array with over 1,000 registration tokens, the request will fail with an InvalidArgumentException.

Retrieve notificationKey from a group:

If you need to retrieve an existing notificationKey, you need to use getNotificationKey(string $groupName) method with a name for the group. Also creating request with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT argument.

$groupName = 'test-group';
/** @var \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT)
    ->getNotificationKey($groupName)
    ->sendGET();
if ($result->isResultOk()) {
    echo 'NotificationKey '.$notificationKey = $result->getNotificationKey();
    echo '<br>';
} else {
    echo 'getErrorStatusDescription '. $result->getErrorStatusDescription();
    echo '<br>';
}

After sending the request to FCM you will get an instance of \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse.

FCM returns the notificationKey that represents the device group.

If there was an error, you will receive info in error description:

$result->getErrorStatusDescription()

Note: Notification_key_name is not required for adding/removing registration tokens, but including it protects you against accidentally using the incorrect notification_key. In order to be safe, current extension make you use groupName always.

Add token(s) to group:

You can add tokens to device group passing registration tokens to the addToGroup(string $groupName, string $notificationKey, array $tokens) method and creating request with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT argument:

$groupName = 'test-group';
$tokens = [
    'your_token_6',
    'your_token_7',
    'your_token_8',
    'your_token_9',
    'your_token_10',
];
/** @var \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT)
    ->addToGroup($groupName, $notificationKey, $tokens)
    ->send();
if ($result->isResultOk()) {
    echo 'NotificationKey '.$result->getNotificationKey();
} else {
    echo 'getErrorStatusDescription '. $result->getErrorStatusDescription();
}

After sending the request to FCM you will get an instance of \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse.

FCM returns a new notificationKey that represents the device group.

If there was an error, you will receive info in error description:

$result->getErrorStatusDescription()
Remove token(s) from group:

You can add tokens to device group passing registration tokens to the removeFromGroup(string $groupName, string $notificationKey, array $tokens) method and creating request with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT argument:

$groupName = 'test-group';
$tokens = [
    'your_token_6',
    'your_token_7',
];
/** @var \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_GROUP_MANAGEMENT)
    ->removeFromGroup($groupName, $notificationKey, $tokens)
    ->send();
if ($result->isResultOk()) {
    echo 'NotificationKey '.$result->getNotificationKey();
    echo '<br>';
} else {
    echo 'getErrorStatusDescription '. $result->getErrorStatusDescription();
    echo '<br>';
}

After sending the request to FCM you will get an instance of \aksafan\fcm\source\responses\legacyApi\GroupManagementResponse.

FCM returns a new notificationKey that represents the device group.

If there was an error, you will receive info in error description:

$result->getErrorStatusDescription()

If you remove all existing registration tokens from a device group, FCM deletes the device group.

Topic management.

Note: You can (un)subscribe to (from) topic through both API versions, just chose your favorite and configure it properly.

Subscribe to a topic:

You can subscribe one or multiple devices to a topic by passing registration tokens to the subscribeToTopic(string $topic, array $tokens) method and creating request with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_MANAGEMENT argument:

$topic = 'test-topic';
$tokens = [
    'your_token_1',
    'your_token_2',
    'your_token_3',
    'your_token_4',
    'your_token_5',
];
/** @var \aksafan\fcm\source\responses\TopicSubscribeResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_MANAGEMENT)
    ->subscribeToTopic($topic, $tokens)
    ->send();

N.B. You can subscribe up to 1,000 devices in a single request. If you provide an array with over 1,000 registration tokens, the request will fail with an InvalidArgumentException.

After sending the request to FCM you will get an instance of \aksafan\fcm\source\responses\TopicSubscribeResponse. Now you can grab tokens with errors:

$tokensWithError = $result->getTopicTokensWithError();

This is an array of tokens with their correspondents errors in format:

$tokensWithError = [
    [
        'token' => 'your_token_2',
        'error' => 'INVALID_ARGUMENT',
    ],
    [
        'token' => 'your_token_4',
        'error' => 'INVALID_ARGUMENT',
    ],
    [
        'token' => 'your_token_5',
        'error' => 'INVALID_ARGUMENT',
    ],
];

All other tokens were subscribed correctly.

Unsubscribe from a topic:

You can unubscribe one or multiple devices to a topic by passing registration tokens to the unsubscribeFromTopic(string $topic, array $tokens) method and creating request with \aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_MANAGEMENT argument:

$topic = 'test-topic';
$tokens = [
    'your_token_1',
    'your_token_2',
    'your_token_3',
    'your_token_4',
    'your_token_5',
];
/** @var \aksafan\fcm\source\responses\TopicSubscribeResponse $result */
$result = $fcm
    ->createRequest(\aksafan\fcm\source\builders\StaticBuilderFactory::FOR_TOPIC_MANAGEMENT)
    ->unsubscribeFromTopic($topic, $tokens)
    ->send();

N.B. You can unsubscribe up to 1,000 devices in a single request. If you provide an array with over 1,000 registration tokens, the request will fail with a messaging/invalid-argument error.

After sending the request to FCM you will get an instance of \aksafan\fcm\source\responses\TopicSubscribeResponse. Now you can grab tokens with errors:

$tokensWithError = $result->getTopicTokensWithError();

This is an array of tokens with their correspondents errors in format:

$tokensWithError = [
    [
        'token' => 'your_token_2',
        'error' => 'INVALID_ARGUMENT',
    ],
    [
        'token' => 'your_token_4',
        'error' => 'INVALID_ARGUMENT',
    ],
    [
        'token' => 'your_token_5',
        'error' => 'INVALID_ARGUMENT',
    ],
];

All other tokens were unsubscribed correctly.

Logging

This extension uses native Yii2 error logger through \Yii::error(). List of used error categories can be found here aksafan\fcm\source\helpers\ErrorsHelper::LOGS_ERRORS.

License

Copyright 2018 by Anton Khainak.

Available under the MIT license.

Complete FCM documentation can be found here Firebase Cloud Messaging.