Skip to content

Commit 17ba23a

Browse files
committed
fix controller missing import issue
1 parent 62f16e2 commit 17ba23a

File tree

6 files changed

+178
-45
lines changed

6 files changed

+178
-45
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## v0.4 - 2025-06-09
4+
5+
- Fixed `unsubscribe` controller logic by adding missing `Controller` import
6+
- Fixed documentation about which Laravel model to declare when using model specific notifications
7+
- Added documentation about what basic functionality the controller provides
8+
- Reorganised the readme a bit so that unsubscribe is nearer the end
9+
- Added more documentation for protected and public variables in the base class
10+
311
## v0.3 - 2025-05-31
412

513
### Changed

README.md

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ In your notification class, add any or all of the following variables to do repe
7070
use Eugenefvdm\NotificationSubscriptions\Enums\RepeatFrequency;
7171
use Eugenefvdm\NotificationSubscriptions\Notifications\BaseNotification;
7272

73-
7473
class DailyReminder extends BaseNotification
7574
{
7675
use Queueable;
@@ -102,33 +101,6 @@ class DailyReminder extends BaseNotification
102101
}
103102
```
104103

105-
## Unsubscribe
106-
107-
Any new notification will be automatically subscribed when used the first time.
108-
109-
### Unsubscribe Links in Blades
110-
111-
For unsubscribe links, modify the `toMail` method in the notification class:
112-
113-
```php
114-
/**
115-
* Get the mail representation of the notification.
116-
*/
117-
public function toMail(object $notifiable): MailMessage
118-
{
119-
return (new MailMessage)
120-
->markdown('notification.reminders.max-count', [
121-
'subscription' => $this->getSubscriptionFromNotifiable($notifiable)
122-
]);
123-
}
124-
```
125-
126-
Then add this to your blade:
127-
128-
```php
129-
<x-notification-subscriptions::unsubscribe :subscription="$subscription" />
130-
```
131-
132104
## Categorization
133105

134106
All new messages without an explicit category assignment will be assigned to the `default` category in the database.
@@ -147,36 +119,68 @@ class DailyReminder extends BaseNotification
147119

148120
## Model Specific Subscriptions
149121

150-
Notifications are typically tied to a user, but at times one wants to bind a notification to both a user and another model. For example, you might have a `products` table, and you want a user to be subscribed to a price notification for the `Product` model. Here's how:
122+
Notifications are typically tied to a user, but at times one wants to associate a notification to both a user and another model. For example, you might have a `products` table, and you want a user to be subscribed to a price notification for specific `Product` models. Here's how:
151123

152124
```php
153125
use Eugenefvdm\NotificationSubscriptions\Notifications\BaseNotification;
126+
use Illuminate\Database\Eloquent\Model;
154127
use App\Models\Product;
155128

156129
class ProductPriceNotification extends BaseNotification
157130
{
158131
use Queueable;
159132

160-
public Product $customModel; // Override $customModel
133+
public ?Model $customModel; // Override $customModel
161134

162-
public function __construct(Product $product)
135+
public function __construct(Product $product) // Type-hint Product in constructor
163136
{
164137
$this->customModel = $product;
165138
}
166139

167140
public function toMail(object $notifiable): MailMessage
168141
{
142+
/** @var Product $product */
143+
$product = $this->customModel;
144+
169145
return (new MailMessage)
170-
->subject("Price Update for {$this->customModel->name}")
146+
->subject("Price Update for {$product->name}")
171147
->markdown('notification.product-price-update', [
172-
'product' => $this->customModel,
173-
// Add line below and blade component for unsubscribe
148+
'product' => $product,
174149
'subscription' => $this->getSubscriptionFromNotifiable($notifiable)
175150
]);
176151
}
177152
}
178153
```
179154

155+
## Unsubscribe
156+
157+
Any new notification will be automatically subscribed when used the first time.
158+
159+
### Unsubscribe Links in Blades
160+
161+
For unsubscribe links, modify the `toMail` method in the notification class:
162+
163+
```php
164+
/**
165+
* Get the mail representation of the notification.
166+
*/
167+
public function toMail(object $notifiable): MailMessage
168+
{
169+
return (new MailMessage)
170+
->markdown('notification.reminders.max-count', [
171+
'subscription' => $this->getSubscriptionFromNotifiable($notifiable)
172+
]);
173+
}
174+
```
175+
176+
Then add this to your blade:
177+
178+
```php
179+
<x-notification-subscriptions::unsubscribe :subscription="$subscription" />
180+
```
181+
182+
The default controller action for unsubscribe is to redirect back with a session variable value of `error` or `status`.
183+
180184
## Testing
181185

182186
```bash

src/Http/Controllers/NotificationController.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Http\Request;
66
use Illuminate\Support\Facades\Session;
77
use Eugenefvdm\NotificationSubscriptions\Models\NotificationSubscription;
8+
use Illuminate\Routing\Controller;
89

910
class NotificationController extends Controller
1011
{
@@ -16,20 +17,15 @@ public function unsubscribe(Request $request, string $uuid)
1617
$subscription = NotificationSubscription::where('uuid', $uuid)->first();
1718

1819
if (!$subscription) {
19-
abort(404, 'Subscription not found');
20+
return redirect()->back()->with('error', 'Subscription not found');
2021
}
2122

22-
// Check if this notification can be unsubscribed
2323
if (!$subscription->canBeUnsubscribed()) {
24-
return redirect()->route('home')->with('error', 'This notification cannot be unsubscribed.');
24+
return redirect()->back()->with('error', 'This notification cannot be unsubscribed');
2525
}
2626

27-
// Unsubscribe
2827
$subscription->unsubscribe();
2928

30-
Session::flash('success', 'You have been successfully unsubscribed from this notification.');
31-
32-
// Return to previous page or home
33-
return redirect()->back()->fallback(route('home'));
29+
return redirect()->back()->with('success', 'Successfully unsubscribed');
3430
}
3531
}

src/Notifications/BaseNotification.php

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,57 @@ abstract class BaseNotification extends Notification implements ShouldQueue
1616
{
1717
use Queueable;
1818

19-
protected ?NotificationSubscription $subscription = null;
19+
/**
20+
* customModel may be overridden by child classes and used for model specific notifications.
21+
*
22+
* @var null|Model
23+
*/
2024
public ?Model $customModel = null;
2125

22-
// Default repeat variables, can be overridden by child classes
26+
/**
27+
* The subscription that this notification belongs to.
28+
*
29+
* @var null|NotificationSubscription
30+
*/
31+
protected ?NotificationSubscription $subscription = null;
32+
33+
/**
34+
* The frequency at which this notification will be repeated.
35+
*
36+
* See Enum RepeatFrequency for possible values.
37+
*
38+
* @var null|RepeatFrequency
39+
*/
2340
protected static ?RepeatFrequency $repeatFrequency = null;
41+
42+
/**
43+
* The interval at which this notification will be repeated.
44+
*
45+
* @var null|int
46+
*/
2447
protected static ?int $repeatInterval = null;
48+
49+
/**
50+
* The maximum number of times this notification can be repeated.
51+
*
52+
* @var null|int
53+
*/
2554
protected static ?int $maxRepeats = null;
55+
56+
/**
57+
* The initial delay for this notification.
58+
*
59+
* @var null|Carbon
60+
*/
2661
protected static ?Carbon $initialDelay = null;
62+
63+
/**
64+
* The category that will be stored in the database.
65+
*
66+
* @var string
67+
*/
2768
protected static ?string $category = 'default';
28-
69+
2970
// Get the repeat frequency from the base class
3071
public static function getRepeatFrequency(): ?RepeatFrequency
3172
{

tests/Feature/UnsubscribeLinkTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
use Eugenefvdm\NotificationSubscriptions\Tests\TestModels\User;
4+
use Eugenefvdm\NotificationSubscriptions\Tests\TestNotifications\SystemNotification;
45
use Eugenefvdm\NotificationSubscriptions\Tests\TestNotifications\UnsubscribeLinkNotification;
56

67
it('will show an unsubscribe link if the notification is not a system notification', function () {
@@ -20,4 +21,49 @@
2021

2122
// Check the unsubscribe link is present in the mail
2223
$this->assertStringContainsString($unsubscribeUrl, $bladeView);
24+
});
25+
26+
it('will allow you to unsubscribe from a notification if you click the unsubscribe link', function () {
27+
$user = User::factory()->create();
28+
$notification = new UnsubscribeLinkNotification();
29+
30+
$user->notify($notification);
31+
32+
$subscription = $user->notificationSubscriptions()
33+
->where('notification_template_id', UnsubscribeLinkNotification::getTemplate()->id)->first();
34+
35+
// Build the unsubscribe url
36+
$unsubscribeUrl = url(route('notifications.unsubscribe', $subscription->uuid));
37+
38+
$mail = $notification->toMail($user);
39+
$bladeView = $mail->render();
40+
41+
// Visit the unsubscribe url
42+
$response = $this->get($unsubscribeUrl);
43+
expect($response->status())->toBe(302);
44+
45+
// Check the success message is present
46+
expect(session('success'))->not->toBeNull();
47+
48+
// Check if the subscription now has an unsubscribed_at timestamp
49+
expect($subscription->fresh()->unsubscribed_at)->not->toBeNull();
50+
});
51+
52+
it('a system notification cannot be unsubscribed', function () {
53+
$user = User::factory()->create();
54+
$notification = new SystemNotification();
55+
56+
$user->notify($notification);
57+
58+
$subscription = $user->notificationSubscriptions()
59+
->where('notification_template_id', SystemNotification::getTemplate()->id)->first();
60+
61+
// Build the unsubscribe url
62+
$unsubscribeUrl = url(route('notifications.unsubscribe', $subscription->uuid));
63+
64+
$response = $this->get($unsubscribeUrl);
65+
expect($response->status())->toBe(302);
66+
67+
// Check the error message is present
68+
expect(session('error'))->not->toBeNull();
2369
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Eugenefvdm\NotificationSubscriptions\Tests\TestNotifications;
4+
5+
use Eugenefvdm\NotificationSubscriptions\Notifications\BaseNotification;
6+
use Illuminate\Bus\Queueable;
7+
8+
class SystemNotification extends BaseNotification
9+
{
10+
use Queueable;
11+
12+
public static ?string $category = 'system';
13+
14+
/**
15+
* Create a new notification instance.
16+
*/
17+
public function __construct()
18+
{
19+
//
20+
}
21+
22+
/**
23+
* Get the notification's delivery channels.
24+
*
25+
* @return array<int, string>
26+
*/
27+
public function via(object $notifiable): array
28+
{
29+
return ['mail'];
30+
}
31+
32+
public function toMail(object $notifiable)
33+
{
34+
return (new \Illuminate\Notifications\Messages\MailMessage)
35+
->subject('System Category Test')
36+
->line('This is a test notification with system category.');
37+
}
38+
}

0 commit comments

Comments
 (0)