Skip to content

Commit f32e186

Browse files
committed
Implement polymorphic relation and add report handler
Closes #76 #83 #74 #75
1 parent fe092f8 commit f32e186

10 files changed

+114
-51
lines changed

migrations/create_push_subscriptions_table.php.stub

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@ class CreatePushSubscriptionsTable extends Migration
1515
{
1616
Schema::connection(config('webpush.database_connection'))->create(config('webpush.table_name'), function (Blueprint $table) {
1717
$table->increments('id');
18-
$table->integer('user_id')->unsigned()->index();
18+
$table->morphs('subscribable');
1919
$table->string('endpoint', 500)->unique();
2020
$table->string('public_key')->nullable();
2121
$table->string('auth_token')->nullable();
2222
$table->string('content_encoding')->nullable();
2323
$table->timestamps();
24-
25-
$table->foreign('user_id')
26-
->references('id')
27-
->on('users')
28-
->onDelete('cascade');
2924
});
3025
}
3126

src/HasPushSubscriptions.php

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
trait HasPushSubscriptions
66
{
77
/**
8-
* Get the user's subscriptions.
8+
* Get all of the subscriptions.
99
*
10-
* @return \Illuminate\Database\Eloquent\Relations\HasMany
10+
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
1111
*/
1212
public function pushSubscriptions()
1313
{
14-
return $this->hasMany(config('webpush.model'));
14+
return $this->morphMany(config('webpush.model'), 'subscribable');
1515
}
1616

1717
/**
18-
* Update (or create) user subscription.
18+
* Update (or create) subscription.
1919
*
2020
* @param string $endpoint
2121
* @param string|null $key
@@ -27,7 +27,7 @@ public function updatePushSubscription($endpoint, $key = null, $token = null, $c
2727
{
2828
$subscription = app(config('webpush.model'))->findByEndpoint($endpoint);
2929

30-
if ($subscription && $this->pushSubscriptionBelongsToUser($subscription)) {
30+
if ($subscription && $this->ownsPushSubscription($subscription)) {
3131
$subscription->public_key = $key;
3232
$subscription->auth_token = $token;
3333
$subscription->content_encoding = $contentEncoding;
@@ -36,7 +36,7 @@ public function updatePushSubscription($endpoint, $key = null, $token = null, $c
3636
return $subscription;
3737
}
3838

39-
if ($subscription && ! $this->pushSubscriptionBelongsToUser($subscription)) {
39+
if ($subscription && ! $this->ownsPushSubscription($subscription)) {
4040
$subscription->delete();
4141
}
4242

@@ -49,14 +49,15 @@ public function updatePushSubscription($endpoint, $key = null, $token = null, $c
4949
}
5050

5151
/**
52-
* Determine if the given subscription belongs to this user.
52+
* Determine if the model owns the given subscription.
5353
*
5454
* @param \NotificationChannels\WebPush\PushSubscription $subscription
5555
* @return bool
5656
*/
57-
public function pushSubscriptionBelongsToUser($subscription)
57+
public function ownsPushSubscription($subscription)
5858
{
59-
return (int) $subscription->user_id === (int) $this->getAuthIdentifier();
59+
return (string) $subscription->subscribable_id === (string) $this->getKey() &&
60+
$subscription->subscribable_type === $this->getMorphClass();
6061
}
6162

6263
/**
@@ -73,7 +74,7 @@ public function deletePushSubscription($endpoint)
7374
}
7475

7576
/**
76-
* Get all subscriptions.
77+
* Get all of the subscriptions.
7778
*
7879
* @return \Illuminate\Database\Eloquent\Collection
7980
*/

src/PushSubscription.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace NotificationChannels\WebPush;
44

5-
use Illuminate\Support\Facades\Config;
65
use Illuminate\Database\Eloquent\Model;
76

87
class PushSubscription extends Model
@@ -39,13 +38,13 @@ public function __construct(array $attributes = [])
3938
}
4039

4140
/**
42-
* Get the user that owns the subscription.
41+
* Get the model related to the subscription.
4342
*
44-
* @return \Illuminate\Contracts\Auth\Authenticatable
43+
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
4544
*/
46-
public function user()
45+
public function subscribable()
4746
{
48-
return $this->belongsTo(Config::get('auth.providers.users.model'));
47+
return $this->morphTo();
4948
}
5049

5150
/**

src/ReportHandler.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace NotificationChannels\WebPush;
4+
5+
use Illuminate\Support\Facades\Log;
6+
7+
class ReportHandler implements ReportHandlerInterface
8+
{
9+
/**
10+
* Handle a message sent report.
11+
*
12+
* @param \Minishlink\WebPush\MessageSentReport $report
13+
* @param \NotificationChannels\WebPush\PushSubscription $subscription
14+
* @return void
15+
*/
16+
public function handleReport($report, $subscription)
17+
{
18+
if ($report->isSuccess()) {
19+
return;
20+
}
21+
22+
Log::warning("Notification failed to sent for subscription {$subscription->endpoint}: {$report->getReason()}");
23+
24+
if ($report->isSubscriptionExpired()) {
25+
$subscription->delete();
26+
}
27+
}
28+
}

src/ReportHandlerInterface.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace NotificationChannels\WebPush;
4+
5+
interface ReportHandlerInterface
6+
{
7+
/**
8+
* Handle a message sent report.
9+
*
10+
* @param \Minishlink\WebPush\MessageSentReport $report
11+
* @param \NotificationChannels\WebPush\PushSubscription $subscription
12+
* @return void
13+
*/
14+
public function handleReport($report, $subscription);
15+
}

src/WebPushChannel.php

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,20 @@ class WebPushChannel
1313
*/
1414
protected $webPush;
1515

16+
/**
17+
* @var \NotificationChannels\WebPush\ReportHandlerInterface
18+
*/
19+
protected $reportHandler;
20+
1621
/**
1722
* @param \Minishlink\WebPush\WebPush $webPush
23+
* @param \NotificationChannels\WebPush\ReportHandlerInterface $webPush
1824
* @return void
1925
*/
20-
public function __construct(WebPush $webPush)
26+
public function __construct(WebPush $webPush, ReportHandlerInterface $reportHandler)
2127
{
2228
$this->webPush = $webPush;
29+
$this->reportHandler = $reportHandler;
2330
}
2431

2532
/**
@@ -31,50 +38,61 @@ public function __construct(WebPush $webPush)
3138
*/
3239
public function send($notifiable, Notification $notification)
3340
{
41+
/** @var \Illuminate\Database\Eloquent\Collection $subscriptions */
3442
$subscriptions = $notifiable->routeNotificationFor('WebPush');
3543

36-
if (! $subscriptions || $subscriptions->isEmpty()) {
44+
if (empty($subscriptions)) {
3745
return;
3846
}
3947

40-
/** @var \NotificationChannels\WebPush\WebPushMessage $webPushMessage */
41-
$webPushMessage = $notification->toWebPush($notifiable, $notification);
42-
$options = $webPushMessage->getOptions();
43-
$payload = json_encode($webPushMessage->toArray());
48+
/** @var \NotificationChannels\WebPush\WebPushMessage $message */
49+
$message = $notification->toWebPush($notifiable, $notification);
50+
$payload = json_encode($message->toArray());
51+
$options = $message->getOptions();
4452

45-
$subscriptions->each(function (PushSubscription $pushSubscription) use ($payload, $options) {
53+
/** @var \NotificationChannels\WebPush\PushSubscription $subscription */
54+
foreach ($subscriptions as $subscription) {
4655
$this->webPush->sendNotification(new Subscription(
47-
$pushSubscription->endpoint,
48-
$pushSubscription->public_key,
49-
$pushSubscription->auth_token,
50-
$pushSubscription->content_encoding
56+
$subscription->endpoint,
57+
$subscription->public_key,
58+
$subscription->auth_token,
59+
$subscription->content_encoding
5160
), $payload, false, $options);
52-
});
61+
}
5362

5463
$reports = $this->webPush->flush();
5564

56-
$this->deleteInvalidSubscriptions($reports, $subscriptions);
65+
$this->handleReports($reports, $subscriptions);
5766
}
5867

5968
/**
60-
* @param \Minishlink\WebPush\MessageSentReport[] $reports
69+
* Handle the reports.
70+
*
71+
* @param \Generator $reports
6172
* @param \Illuminate\Database\Eloquent\Collection $subscriptions
6273
* @return void
6374
*/
64-
protected function deleteInvalidSubscriptions($reports, $subscriptions)
75+
protected function handleReports($reports, $subscriptions)
6576
{
77+
/** @var \Minishlink\WebPush\MessageSentReport $report */
6678
foreach ($reports as $report) {
67-
if (is_null($report) || $report->isSuccess()) {
68-
continue;
79+
if ($report && $subscription = $this->findSubscription($subscriptions, $report)) {
80+
$this->reportHandler->handleReport($report, $subscription);
6981
}
82+
}
83+
}
7084

71-
/* @var \Minishlink\WebPush\MessageSentReport $report */
72-
$subscriptions->each(function ($subscription) use ($report) {
73-
if ($subscription->endpoint === $report->getEndpoint()) {
74-
logger()->warning('deleting subscription cause of '.$report->getReason());
75-
$subscription->delete();
76-
}
77-
});
85+
/**
86+
* @param \Illuminate\Database\Eloquent\Collection $subscriptions
87+
* @param \Minishlink\WebPush\MessageSentReport $report
88+
* @return void
89+
*/
90+
protected function findSubscription($subscriptions, $report)
91+
{
92+
foreach ($subscriptions as $subscription) {
93+
if ($subscription->endpoint === $report->getEndpoint()) {
94+
return $subscription;
95+
}
7896
}
7997
}
8098
}

src/WebPushServiceProvider.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public function boot()
3636
return $webPush;
3737
});
3838

39+
$this->app->when(WebPushChannel::class)
40+
->needs(ReportHandlerInterface::class)
41+
->give(ReportHandler::class);
42+
3943
if ($this->app->runningInConsole()) {
4044
$this->definePublishing();
4145
}
@@ -92,7 +96,7 @@ protected function definePublishing()
9296
$timestamp = date('Y_m_d_His', time());
9397

9498
$this->publishes([
95-
__DIR__.'/../migrations/create_push_subscriptions_table.php.stub' => database_path("/migrations/{$timestamp}_create_push_subscriptions_table.php"),
99+
__DIR__.'/../migrations/create_push_subscriptions_table.php.stub' => database_path("migrations/{$timestamp}_create_push_subscriptions_table.php"),
96100
], 'migrations');
97101
}
98102
}

tests/ChannelTest.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Minishlink\WebPush\Subscription;
99
use Minishlink\WebPush\MessageSentReport;
1010
use NotificationChannels\WebPush\WebPushChannel;
11+
use NotificationChannels\WebPush\ReportHandler;
12+
use GuzzleHttp\Psr7\Response;
1113

1214
class ChannelTest extends TestCase
1315
{
@@ -23,7 +25,7 @@ public function setUp(): void
2325

2426
$this->webPush = Mockery::mock(WebPush::class);
2527

26-
$this->channel = new WebPushChannel($this->webPush);
28+
$this->channel = new WebPushChannel($this->webPush, new ReportHandler);
2729
}
2830

2931
public function tearDown(): void
@@ -103,8 +105,8 @@ public function it_will_delete_invalid_subscriptions()
103105
$this->webPush->shouldReceive('flush')
104106
->once()
105107
->andReturn((function () {
106-
yield new MessageSentReport(new Request('POST', 'valid_endpoint'), null, true);
107-
yield new MessageSentReport(new Request('POST', 'invalid_endpoint'), null, false);
108+
yield new MessageSentReport(new Request('POST', 'valid_endpoint'), new Response(200), true);
109+
yield new MessageSentReport(new Request('POST', 'invalid_endpoint'), new Response(404), false);
108110
})());
109111

110112
$this->testUser->updatePushSubscription('valid_endpoint');

tests/HasPushSubscriptionsTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function it_can_determinte_if_a_subscription_belongs_to_a_user()
4949
{
5050
$sub = $this->testUser->updatePushSubscription('foo');
5151

52-
$this->assertTrue($this->testUser->pushSubscriptionBelongsToUser($sub));
52+
$this->assertTrue($this->testUser->ownsPushSubscription($sub));
5353
}
5454

5555
/** @test */

tests/PushSubscriptionTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public function it_can_get_user()
3535
$this->testUser->updatePushSubscription('endpoint');
3636
$sub = PushSubscription::findByEndpoint('endpoint');
3737

38-
$this->assertEquals($this->testUser->id, $sub->user->id);
38+
$this->assertEquals($this->testUser->id, $sub->subscribable_id);
39+
$this->assertEquals(get_class($this->testUser), $sub->subscribable_type);
3940
}
4041
}

0 commit comments

Comments
 (0)