Skip to content

Commit 61d3f23

Browse files
authored
Add Notification channel (#13)
1 parent 8951b8c commit 61d3f23

File tree

10 files changed

+187
-1
lines changed

10 files changed

+187
-1
lines changed

README.md

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,51 @@ Segment::identify([
158158
]);
159159
```
160160

161+
### Laravel Notifications
162+
This package includes an out-of-the-box notification channel, to allow you to use Laravel's built-in notification
163+
feature. To send Segment events to users as notifications, generate your notification as normal;
164+
165+
```
166+
php artisan make:notification UserSubscribed
167+
```
168+
169+
You must ensure your notification implements the `CanNotifyViaSegment` interface, and add the required `toSegment`
170+
method. Then you can configure the `via` method to include the `SegmentChannel` class.
171+
172+
You can then adjust the `toSegment` method to return the event you'd like.
173+
174+
```
175+
use Illuminate\Notifications\Notification;
176+
use SlashEquip\LaravelSegment\Contracts\CanBeIdentifiedForSegment;
177+
use SlashEquip\LaravelSegment\Contracts\CanBeSentToSegment;
178+
use SlashEquip\LaravelSegment\Contracts\CanNotifyViaSegment;
179+
use SlashEquip\LaravelSegment\Notifications\SegmentChannel;
180+
use SlashEquip\LaravelSegment\SimpleSegmentEvent;
181+
182+
class UserSubscribed extends Notification implements CanNotifyViaSegment
183+
{
184+
use Notifiable;
185+
186+
public function __construct(
187+
) {
188+
}
189+
190+
public function via(object $notifiable): array
191+
{
192+
return [SegmentChannel::class];
193+
}
194+
195+
public function toSegment(CanBeIdentifiedForSegment $notifiable): CanBeSentToSegment
196+
{
197+
return new SimpleSegmentEvent(
198+
$notifiable,
199+
'User Subscribed',
200+
['some' => 'thing'],
201+
);
202+
}
203+
}
204+
```
205+
161206
## Misc
162207

163208
### Deferring
@@ -167,7 +212,7 @@ through-out the request or process and then send them in batch after your applic
167212
happens during the Laravel termination.
168213

169214
### Safe mode
170-
By default safe-mode is turned on. When safe-mode is active it will swallow any exceptions thrown when making the HTTP
215+
By default, safe-mode is turned on. When safe-mode is active it will swallow any exceptions thrown when making the HTTP
171216
request to Segmenta and report them automatically to the exception handler, allow your app to continue running. When
172217
disabled then the exception will be thrown.
173218

phpstan.neon.dist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ parameters:
88

99
# Level 9 is the highest level
1010
level: 6
11+
12+
checkGenericClassInNonGenericObjectType: false
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace SlashEquip\LaravelSegment\Contracts;
4+
5+
interface CanNotifyViaSegment
6+
{
7+
public function toSegment(CanBeIdentifiedForSegment $notifiable): CanBeSentToSegment;
8+
}

src/Contracts/SegmentServiceContract.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@ interface SegmentServiceContract
88
{
99
public function setGlobalUser(CanBeIdentifiedForSegment $globalUser): void;
1010

11+
/**
12+
* @param array<string, mixed> $globalContext
13+
*/
1114
public function setGlobalContext(array $globalContext): void;
1215

16+
/**
17+
* @param array<string, mixed> $eventData
18+
*/
1319
public function track(string $event, array $eventData = null): void;
1420

21+
/**
22+
* @param array<string, mixed> $identifyData
23+
*/
1524
public function identify(array $identifyData = null): void;
1625

1726
public function forUser(CanBeIdentifiedForSegment $user): PendingUserSegment;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace SlashEquip\LaravelSegment\Exceptions;
4+
5+
use RuntimeException;
6+
7+
class NotifiableCannotBeIdentifiedForSegmentException extends RuntimeException
8+
{
9+
//
10+
}

src/Facades/Fakes/SegmentFake.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,39 @@ class SegmentFake implements SegmentServiceContract
1616
{
1717
private CanBeIdentifiedForSegment $user;
1818

19+
/** @var array<string, mixed> */
1920
private ?array $context = [];
2021

22+
/** @var array<int, SimpleSegmentEvent> */
2123
private array $events = [];
2224

25+
/** @var array<int, SimpleSegmentIdentify> */
2326
private array $identities = [];
2427

2528
public function setGlobalUser(CanBeIdentifiedForSegment $globalUser): void
2629
{
2730
$this->user = $globalUser;
2831
}
2932

33+
/**
34+
* @param array<string, mixed> $globalContext
35+
*/
3036
public function setGlobalContext(array $globalContext): void
3137
{
3238
$this->context = $globalContext;
3339
}
3440

41+
/**
42+
* @param array<string, mixed> $identifyData
43+
*/
3544
public function identify(?array $identifyData = []): void
3645
{
3746
$this->identities[] = new SimpleSegmentIdentify($this->user, $identifyData);
3847
}
3948

49+
/**
50+
* @param array<string, mixed> $eventData
51+
*/
4052
public function track(string $event, array $eventData = null): void
4153
{
4254
$this->events[] = new SimpleSegmentEvent($this->user, $event, $eventData);
@@ -158,6 +170,14 @@ public function assertNothingTracked(): void
158170
PHPUnit::assertEmpty($events, $events->count().' events were found unexpectedly.');
159171
}
160172

173+
/**
174+
* @return array<string, mixed>
175+
*/
176+
public function getContext(): ?array
177+
{
178+
return $this->context;
179+
}
180+
161181
private function identities(Closure $callback = null): Collection
162182
{
163183
$identities = collect($this->identities);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace SlashEquip\LaravelSegment\Notifications;
4+
5+
use SlashEquip\LaravelSegment\Contracts\CanBeIdentifiedForSegment;
6+
use SlashEquip\LaravelSegment\Contracts\CanNotifyViaSegment;
7+
use SlashEquip\LaravelSegment\Exceptions\NotifiableCannotBeIdentifiedForSegmentException;
8+
use SlashEquip\LaravelSegment\Facades\Segment;
9+
10+
class SegmentChannel
11+
{
12+
public function send(object $notifiable, CanNotifyViaSegment $notification): void
13+
{
14+
if (! $notifiable instanceof CanBeIdentifiedForSegment) {
15+
throw new NotifiableCannotBeIdentifiedForSegmentException();
16+
}
17+
18+
Segment::push($notification->toSegment($notifiable));
19+
}
20+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace SlashEquip\LaravelSegment\Tests\Stubs;
4+
5+
use Illuminate\Notifications\Notification;
6+
use Illuminate\Support\Str;
7+
use SlashEquip\LaravelSegment\Contracts\CanBeIdentifiedForSegment;
8+
use SlashEquip\LaravelSegment\Contracts\CanBeSentToSegment;
9+
use SlashEquip\LaravelSegment\Contracts\CanNotifyViaSegment;
10+
use SlashEquip\LaravelSegment\Notifications\SegmentChannel;
11+
use SlashEquip\LaravelSegment\SimpleSegmentEvent;
12+
13+
class SegmentTestNotification extends Notification implements CanNotifyViaSegment
14+
{
15+
public function __construct(
16+
private int $number
17+
) {
18+
}
19+
20+
public function via(object $notifiable): array
21+
{
22+
return [SegmentChannel::class];
23+
}
24+
25+
public function toSegment(CanBeIdentifiedForSegment $notifiable): CanBeSentToSegment
26+
{
27+
return new SimpleSegmentEvent(
28+
$notifiable,
29+
Str::of(class_basename(static::class))
30+
->snake()
31+
->replace('_', ' ')
32+
->title(),
33+
['some' => 'thing'],
34+
);
35+
}
36+
}

tests/Stubs/SegmentTestUser.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
namespace SlashEquip\LaravelSegment\Tests\Stubs;
44

5+
use Illuminate\Notifications\Notifiable;
56
use SlashEquip\LaravelSegment\Contracts\CanBeIdentifiedForSegment;
67

78
class SegmentTestUser implements CanBeIdentifiedForSegment
89
{
10+
use Notifiable;
11+
912
public function __construct(
1013
private string $id
1114
) {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
use SlashEquip\LaravelSegment\Contracts\SegmentServiceContract;
4+
use SlashEquip\LaravelSegment\SimpleSegmentEvent;
5+
use SlashEquip\LaravelSegment\Tests\Stubs\SegmentTestNotification;
6+
use SlashEquip\LaravelSegment\Tests\Stubs\SegmentTestUser;
7+
8+
it('can send a notification to a notifiable entity', function () {
9+
// Given we have a notifiable entity
10+
$notifiable = new SegmentTestUser('123456');
11+
12+
// And we are spying on the service
13+
$service = test()->spy(SegmentServiceContract::class);
14+
15+
// When we notify the entity
16+
$notifiable->notify(new SegmentTestNotification(987654));
17+
18+
// Then the service was called appropriately
19+
$service->shouldHaveReceived('push')
20+
->with(Mockery::on(function ($arg) {
21+
if (! $arg instanceof SimpleSegmentEvent) {
22+
return false;
23+
}
24+
25+
$payload = $arg->toSegment();
26+
27+
return $payload->user->getSegmentIdentifier() === '123456'
28+
&& $payload->event === 'Segment Test Notification'
29+
&& count($payload->data) === 1
30+
&& $payload->data['some'] === 'thing';
31+
}))
32+
->once();
33+
});

0 commit comments

Comments
 (0)