Completed
Pull Request — master (#85)
by
unknown
02:19 queued 01:10
created

FcmChannel::sendToFcm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace NotificationChannels\Fcm;
4
5
use Illuminate\Contracts\Container\BindingResolutionException;
6
use Illuminate\Contracts\Events\Dispatcher;
7
use Illuminate\Notifications\Events\NotificationFailed;
8
use Illuminate\Notifications\Notification;
9
use Kreait\Firebase\Exception\MessagingException;
10
use Kreait\Firebase\Messaging\CloudMessage;
11
use Kreait\Firebase\Messaging\Message;
12
use NotificationChannels\Fcm\Exceptions\CouldNotSendNotification;
13
use ReflectionException;
14
use Throwable;
15
16
class FcmChannel
17
{
18
    const MAX_TOKEN_PER_REQUEST = 500;
19
20
    /**
21
     * @var \Illuminate\Contracts\Events\Dispatcher
22
     */
23
    protected $events;
24
25
    /**
26
     * FcmChannel constructor.
27
     *
28
     * @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
29
     */
30
    public function __construct(Dispatcher $dispatcher)
31
    {
32
        $this->events = $dispatcher;
33
    }
34
35
    /**
36
     * @var string|null
37
     */
38
    protected $fcmProject = null;
39
40
    /**
41
     * Send the given notification.
42
     *
43
     * @param mixed $notifiable
44
     * @param \Illuminate\Notifications\Notification $notification
45
     *
46
     * @return array
47
     * @throws \NotificationChannels\Fcm\Exceptions\CouldNotSendNotification
48
     * @throws \Kreait\Firebase\Exception\FirebaseException
49
     */
50
    public function send($notifiable, Notification $notification)
51
    {
52
        $token = $notifiable->routeNotificationFor('fcm', $notification);
53
54
        // Get the message from the notification class
55
        $fcmMessage = $notification->toFcm($notifiable);
0 ignored issues
show
Bug introduced by
The method toFcm() does not seem to exist on object<Illuminate\Notifications\Notification>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
56
57
        if (! $fcmMessage instanceof Message) {
58
            throw CouldNotSendNotification::invalidMessage();
59
        }
60
61
        $this->fcmProject = null;
62
        if (method_exists($notification, 'fcmProject')) {
63
            $this->fcmProject = $notification->fcmProject($notifiable, $fcmMessage);
0 ignored issues
show
Bug introduced by
The method fcmProject() does not seem to exist on object<Illuminate\Notifications\Notification>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
64
        }
65
66
        $responses = [];
67
68
        try {
69
            if (empty($token) && $fcmMessage instanceof  FcmMessage) {
70
                if ($fcmMessage->getTopic() || $fcmMessage->getCondition()) {
71
                    $responses[] = $this->sendToFcm($fcmMessage);
72
                }
73
            } elseif (empty($token) && $fcmMessage instanceof  CloudMessage) {
74
                if ($fcmMessage->hasTarget()) {
75
                    $responses[] = $this->sendToFcm($fcmMessage);
76
                }
77
            } elseif (empty($token)) {
78
                $responses = [];
79
            } elseif (! is_array($token)) {
80
                if ($fcmMessage instanceof CloudMessage) {
81
                    $fcmMessage = $fcmMessage->withChangedTarget('token', $token);
82
                }
83
84
                if ($fcmMessage instanceof FcmMessage) {
85
                    $fcmMessage->setToken($token);
86
                }
87
88
                $responses[] = $this->sendToFcm($fcmMessage);
89
            } else {
90
                // Use multicast because there are multiple recipients
91
                $partialTokens = array_chunk($token, self::MAX_TOKEN_PER_REQUEST, false);
92
                foreach ($partialTokens as $tokens) {
93
                    $responses[] = $this->sendToFcmMulticast($fcmMessage, $tokens);
94
                }
95
            }
96
        } catch (MessagingException $exception) {
97
            $this->failedNotification($notifiable, $notification, $exception);
98
            throw CouldNotSendNotification::serviceRespondedWithAnError($exception);
99
        }
100
101
        return $responses;
102
    }
103
104
    /**
105
     * @return \Kreait\Firebase\Messaging
106
     */
107
    protected function messaging()
108
    {
109
        try {
110
            $messaging = app('firebase.manager')->project($this->fcmProject)->messaging();
111
        } catch (BindingResolutionException $e) {
112
            $messaging = app('firebase.messaging');
113
        } catch (ReflectionException $e) {
114
            $messaging = app('firebase.messaging');
115
        }
116
117
        return $messaging;
118
    }
119
120
    /**
121
     * @param \Kreait\Firebase\Messaging\Message $fcmMessage
122
     * @return array
123
     * @throws \Kreait\Firebase\Exception\MessagingException
124
     * @throws \Kreait\Firebase\Exception\FirebaseException
125
     */
126
    protected function sendToFcm(Message $fcmMessage)
127
    {
128
        return $this->messaging()->send($fcmMessage);
129
    }
130
131
    /**
132
     * @param $fcmMessage
133
     * @param array $tokens
134
     * @return \Kreait\Firebase\Messaging\MulticastSendReport
135
     * @throws \Kreait\Firebase\Exception\MessagingException
136
     * @throws \Kreait\Firebase\Exception\FirebaseException
137
     */
138
    protected function sendToFcmMulticast($fcmMessage, array $tokens)
139
    {
140
        return $this->messaging()->sendMulticast($fcmMessage, $tokens);
141
    }
142
143
    /**
144
     * Dispatch failed event.
145
     *
146
     * @param mixed $notifiable
147
     * @param \Illuminate\Notifications\Notification $notification
148
     * @param \Throwable $exception
149
     * @return array|null
150
     */
151
    protected function failedNotification($notifiable, Notification $notification, Throwable $exception)
152
    {
153
        return $this->events->dispatch(new NotificationFailed(
154
            $notifiable,
155
            $notification,
156
            self::class,
157
            [
158
                'message' => $exception->getMessage(),
159
                'exception' => $exception,
160
            ]
161
        ));
162
    }
163
}
164