@orieljs/framework 0.1.0-alpha.6 → 0.1.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +4 -0
- package/README.md +10 -1
- package/TRADEMARKS.md +33 -0
- package/dist/{chunk-ZOEPKBVF.js → chunk-4MWG3B7G.js} +1347 -428
- package/dist/chunk-4MWG3B7G.js.map +1 -0
- package/dist/{chunk-POEP4B2M.js → chunk-7ZQZYDOC.js} +745 -105
- package/dist/chunk-7ZQZYDOC.js.map +1 -0
- package/dist/{chunk-I63ZFMX6.js → chunk-GVNLZZTN.js} +1 -1
- package/dist/chunk-GVNLZZTN.js.map +1 -0
- package/dist/{chunk-MFTZQMZM.js → chunk-VOYW3R5J.js} +4 -4
- package/dist/chunk-VOYW3R5J.js.map +1 -0
- package/dist/contracts.d.ts +515 -17
- package/dist/contracts.js +1 -1
- package/dist/index.d-WQpfdIbG.d.ts +630 -0
- package/dist/index.d.ts +964 -152
- package/dist/index.js +4279 -866
- package/dist/index.js.map +1 -1
- package/dist/{jsx-runtime-CBZQYYDS.d.ts → jsx-runtime-6um4Mwfb.d.ts} +96 -5
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.js +3 -3
- package/dist/{testing-BM481Hdz.d.ts → testing-CKwfWJGn.d.ts} +180 -15
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +3 -3
- package/docs/authentication.md +487 -0
- package/docs/authorization.md +435 -0
- package/docs/cache.md +647 -0
- package/docs/collections.md +2270 -0
- package/docs/configuration.md +334 -0
- package/docs/console.md +651 -0
- package/docs/container.md +430 -0
- package/docs/controllers.md +406 -0
- package/docs/csrf.md +126 -0
- package/docs/database-testing.md +144 -0
- package/docs/database.md +236 -0
- package/docs/deployment.md +276 -0
- package/docs/eloquent-collections.md +218 -0
- package/docs/eloquent-factories.md +255 -0
- package/docs/eloquent-mutators.md +254 -0
- package/docs/eloquent-observers.md +145 -0
- package/docs/eloquent-relationships.md +557 -0
- package/docs/eloquent.md +494 -0
- package/docs/encryption.md +104 -0
- package/docs/errors.md +318 -0
- package/docs/events.md +336 -0
- package/docs/facades.md +278 -0
- package/docs/hashing.md +118 -0
- package/docs/helpers.md +1281 -0
- package/docs/http-tests.md +386 -0
- package/docs/inertia.md +485 -0
- package/docs/installation.md +209 -0
- package/docs/lifecycle.md +234 -0
- package/docs/live-tests.md +226 -0
- package/docs/live.md +910 -0
- package/docs/logging.md +255 -0
- package/docs/mail.md +619 -0
- package/docs/middleware.md +384 -0
- package/docs/migrations.md +416 -0
- package/docs/mocking.md +264 -0
- package/docs/navigation.json +273 -0
- package/docs/notifications.md +225 -0
- package/docs/pagination.md +247 -0
- package/docs/passwords.md +207 -0
- package/docs/providers.md +187 -0
- package/docs/queries.md +600 -0
- package/docs/queues.md +330 -0
- package/docs/requests.md +533 -0
- package/docs/responses.md +444 -0
- package/docs/routing.md +738 -0
- package/docs/scheduling.md +277 -0
- package/docs/seeding.md +122 -0
- package/docs/session.md +409 -0
- package/docs/starter-kits.md +396 -0
- package/docs/storage.md +192 -0
- package/docs/structure.md +264 -0
- package/docs/testing.md +165 -0
- package/docs/time-travel.md +138 -0
- package/docs/validation.md +1792 -0
- package/docs/verification.md +152 -0
- package/docs/views.md +566 -0
- package/docs/vite.md +322 -0
- package/package.json +34 -32
- package/dist/chunk-I63ZFMX6.js.map +0 -1
- package/dist/chunk-MFTZQMZM.js.map +0 -1
- package/dist/chunk-POEP4B2M.js.map +0 -1
- package/dist/chunk-ZOEPKBVF.js.map +0 -1
- package/dist/index.d-BJyJx6U1.d.ts +0 -319
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sections": [
|
|
3
|
+
{
|
|
4
|
+
"title": "Getting started",
|
|
5
|
+
"pages": [
|
|
6
|
+
{
|
|
7
|
+
"slug": "installation",
|
|
8
|
+
"title": "Installation"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"slug": "configuration",
|
|
12
|
+
"title": "Configuration"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"slug": "structure",
|
|
16
|
+
"title": "Directory structure"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"slug": "starter-kits",
|
|
20
|
+
"title": "Starter kits"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"slug": "deployment",
|
|
24
|
+
"title": "Deployment"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"title": "Architecture",
|
|
30
|
+
"pages": [
|
|
31
|
+
{
|
|
32
|
+
"slug": "lifecycle",
|
|
33
|
+
"title": "Request lifecycle"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"slug": "container",
|
|
37
|
+
"title": "Service container"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"slug": "providers",
|
|
41
|
+
"title": "Service providers"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"slug": "facades",
|
|
45
|
+
"title": "Facades"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"title": "The basics",
|
|
51
|
+
"pages": [
|
|
52
|
+
{
|
|
53
|
+
"slug": "routing",
|
|
54
|
+
"title": "Routing"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"slug": "middleware",
|
|
58
|
+
"title": "Middleware"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"slug": "csrf",
|
|
62
|
+
"title": "CSRF protection"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"slug": "controllers",
|
|
66
|
+
"title": "Controllers"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"slug": "requests",
|
|
70
|
+
"title": "Requests"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"slug": "session",
|
|
74
|
+
"title": "Session"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"slug": "responses",
|
|
78
|
+
"title": "Responses"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"slug": "views",
|
|
82
|
+
"title": "Views"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"slug": "validation",
|
|
86
|
+
"title": "Validation"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"slug": "errors",
|
|
90
|
+
"title": "Error handling"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"slug": "logging",
|
|
94
|
+
"title": "Logging"
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"title": "Digging deeper",
|
|
100
|
+
"pages": [
|
|
101
|
+
{
|
|
102
|
+
"slug": "console",
|
|
103
|
+
"title": "Console"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"slug": "cache",
|
|
107
|
+
"title": "Cache"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"slug": "collections",
|
|
111
|
+
"title": "Collections"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"slug": "events",
|
|
115
|
+
"title": "Events"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"slug": "storage",
|
|
119
|
+
"title": "File storage"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"slug": "helpers",
|
|
123
|
+
"title": "Helpers"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"slug": "mail",
|
|
127
|
+
"title": "Mail"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"slug": "notifications",
|
|
131
|
+
"title": "Notifications"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"slug": "queues",
|
|
135
|
+
"title": "Queues"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"slug": "scheduling",
|
|
139
|
+
"title": "Task scheduling"
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"title": "Security",
|
|
145
|
+
"pages": [
|
|
146
|
+
{
|
|
147
|
+
"slug": "authentication",
|
|
148
|
+
"title": "Authentication"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"slug": "authorization",
|
|
152
|
+
"title": "Authorization"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"slug": "verification",
|
|
156
|
+
"title": "Email verification"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"slug": "encryption",
|
|
160
|
+
"title": "Encryption"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"slug": "hashing",
|
|
164
|
+
"title": "Hashing"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"slug": "passwords",
|
|
168
|
+
"title": "Password reset"
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"title": "Database",
|
|
174
|
+
"pages": [
|
|
175
|
+
{
|
|
176
|
+
"slug": "database",
|
|
177
|
+
"title": "Getting started"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"slug": "queries",
|
|
181
|
+
"title": "Query builder"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"slug": "pagination",
|
|
185
|
+
"title": "Pagination"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"slug": "migrations",
|
|
189
|
+
"title": "Migrations"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"slug": "seeding",
|
|
193
|
+
"title": "Seeding"
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"title": "Models",
|
|
199
|
+
"pages": [
|
|
200
|
+
{
|
|
201
|
+
"slug": "eloquent",
|
|
202
|
+
"title": "Getting started"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"slug": "eloquent-relationships",
|
|
206
|
+
"title": "Relationships"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"slug": "eloquent-collections",
|
|
210
|
+
"title": "Collections"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"slug": "eloquent-mutators",
|
|
214
|
+
"title": "Accessors and casts"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"slug": "eloquent-factories",
|
|
218
|
+
"title": "Factories"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"slug": "eloquent-observers",
|
|
222
|
+
"title": "Observers"
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"title": "Frontend",
|
|
228
|
+
"pages": [
|
|
229
|
+
{
|
|
230
|
+
"slug": "inertia",
|
|
231
|
+
"title": "Inertia"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"slug": "live",
|
|
235
|
+
"title": "Live"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"slug": "vite",
|
|
239
|
+
"title": "Vite"
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"title": "Testing",
|
|
245
|
+
"pages": [
|
|
246
|
+
{
|
|
247
|
+
"slug": "testing",
|
|
248
|
+
"title": "Getting started"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"slug": "http-tests",
|
|
252
|
+
"title": "HTTP tests"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"slug": "live-tests",
|
|
256
|
+
"title": "Live tests"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"slug": "mocking",
|
|
260
|
+
"title": "Fakes"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
"slug": "database-testing",
|
|
264
|
+
"title": "Database"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"slug": "time-travel",
|
|
268
|
+
"title": "Time travel"
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
]
|
|
273
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Notifications
|
|
2
|
+
|
|
3
|
+
A notification is a short message to one of your users, such as "your order has shipped", sent over one or more
|
|
4
|
+
channels: as an email, as a row in your database for your app to show, or both.
|
|
5
|
+
|
|
6
|
+
## Introduction
|
|
7
|
+
|
|
8
|
+
Each notification is a class with a `via()` method, which names its channels, and a `toXxx()` method for each of
|
|
9
|
+
them. Oriel has two channels: `mail`, which sends an email, and `database`, which stores the notification in a
|
|
10
|
+
`notifications` table. You can add channels of your own with `Notification.extend()`.
|
|
11
|
+
|
|
12
|
+
> [!NOTE]
|
|
13
|
+
> Laravel also has broadcast, SMS (Vonage) and Slack channels. Oriel has `mail` and `database`.
|
|
14
|
+
|
|
15
|
+
## Generating notifications
|
|
16
|
+
|
|
17
|
+
`make:notification` writes a class to `app/Notifications`:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
node oriel make:notification InvoicePaid
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The class it writes is queued (it has `readonly shouldQueue = true`), so run `node oriel queue:work` while you work
|
|
24
|
+
on it, or remove the field to send it right away.
|
|
25
|
+
|
|
26
|
+
## Sending notifications
|
|
27
|
+
|
|
28
|
+
### Using the Notifiable mixin
|
|
29
|
+
|
|
30
|
+
A model sends and stores notifications through the `Notifiable` mixin, which the `User` model of a new app already
|
|
31
|
+
has:
|
|
32
|
+
|
|
33
|
+
```ts title="app/Models/User.ts"
|
|
34
|
+
import { AuthenticatableModel, Authorizable, HasApiTokens, Model, Notifiable } from '@orieljs/framework'
|
|
35
|
+
|
|
36
|
+
export class User extends HasApiTokens(Notifiable(Authorizable(AuthenticatableModel(Model)))) {}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
It gives the model `notify()`, `notifyNow()` and `routeNotificationFor()`, and the `notifications()`,
|
|
40
|
+
`readNotifications()` and `unreadNotifications()` relations of the [database channel](#database-notifications):
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const user = await User.findOrFail(1)
|
|
44
|
+
|
|
45
|
+
await user.notify(new InvoicePaid('$20.00'))
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Using the Notification facade
|
|
49
|
+
|
|
50
|
+
To notify several users at once, pass them (an array or any iterable) to `Notification.send()`:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { Notification } from '@orieljs/framework'
|
|
54
|
+
|
|
55
|
+
const users = await User.all()
|
|
56
|
+
|
|
57
|
+
await Notification.send(users, new InvoicePaid('$20.00'))
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`Notification.sendNow()` sends right away, even a queued notification, and takes the channels to use as a third
|
|
61
|
+
argument.
|
|
62
|
+
|
|
63
|
+
### Specifying delivery channels
|
|
64
|
+
|
|
65
|
+
`via()` gets the notifiable, so a notification can choose its channels for each user:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
class InvoicePaid extends Notification {
|
|
69
|
+
via(notifiable: object): string[] {
|
|
70
|
+
return notifiable instanceof User ? ['mail', 'database'] : ['mail']
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Queueing notifications
|
|
76
|
+
|
|
77
|
+
A notification with `readonly shouldQueue = true` is queued: `notify()` and `Notification.send()` push one job per
|
|
78
|
+
notifiable and per channel, and a [queue worker](queues.md#running-the-queue-worker) sends them. The fields
|
|
79
|
+
`connection`, `queue` and `delay` choose where and when, and any of them also queues the notification, as a `uniqueId()`
|
|
80
|
+
or `retryUntil()` method does. `notifyNow()` and `Notification.sendNow()` send a queued notification right away.
|
|
81
|
+
|
|
82
|
+
### Unique notifications
|
|
83
|
+
|
|
84
|
+
A queued notification with a `uniqueId()` method isn't queued again while its job is waiting or running, as a
|
|
85
|
+
[unique job](queues.md#unique-jobs). The lock is taken per notifiable and per channel, so the same notification to two
|
|
86
|
+
users, or by mail and by database, sends each of them.
|
|
87
|
+
|
|
88
|
+
### On-demand notifications
|
|
89
|
+
|
|
90
|
+
To notify someone who isn't a user of your app, give the route yourself:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
await Notification.route('mail', 'taylor@example.com').notify(new InvoicePaid('$20.00'))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The database channel is skipped for an on-demand notification, as it needs a model to store the row against.
|
|
97
|
+
|
|
98
|
+
> [!NOTE]
|
|
99
|
+
> Laravel chains `route()` for several channels (`->route('mail', ...)->route('vonage', ...)`). The value
|
|
100
|
+
> `Notification.route()` returns is typed with `notify()` only, so an on-demand notification has one channel.
|
|
101
|
+
|
|
102
|
+
## Mail notifications
|
|
103
|
+
|
|
104
|
+
### Formatting mail messages
|
|
105
|
+
|
|
106
|
+
`toMail()` returns a `MailMessage`, which Oriel renders in its own styled template, with a plain-text part beside the
|
|
107
|
+
HTML:
|
|
108
|
+
|
|
109
|
+
```ts title="app/Notifications/InvoicePaid.ts"
|
|
110
|
+
import { MailMessage, Notification } from '@orieljs/framework'
|
|
111
|
+
|
|
112
|
+
export class InvoicePaid extends Notification {
|
|
113
|
+
constructor(private readonly amount: string) {
|
|
114
|
+
super()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
via(): string[] {
|
|
118
|
+
return ['mail']
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
toMail(): MailMessage {
|
|
122
|
+
return new MailMessage()
|
|
123
|
+
.subject('Invoice paid')
|
|
124
|
+
.greeting('Hello!')
|
|
125
|
+
.line(`We received your payment of ${this.amount}.`)
|
|
126
|
+
.action('View invoice', 'https://example.com/invoices/1')
|
|
127
|
+
.line('Thank you for using our application!')
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- `greeting()` is "Hello!" when you don't set one ("Whoops!" for an error message), and `salutation()` is "Regards,"
|
|
133
|
+
and your app's name.
|
|
134
|
+
- `line()`, `lines()`, `lineIf()` and `linesIf()` add paragraphs; `action()` adds a button.
|
|
135
|
+
- `from()`, `to()`, `cc()`, `bcc()` and `replyTo()` set the addresses, `attach(path, { as, mime })` attaches a file and
|
|
136
|
+
`attachData(data, name, { mime })` attaches content you have in memory.
|
|
137
|
+
|
|
138
|
+
Each line is plain text: Oriel escapes it, and keeps it as you wrote it.
|
|
139
|
+
|
|
140
|
+
> [!NOTE]
|
|
141
|
+
> Laravel reads each line as Markdown, trims it and joins its lines into one. Oriel's lines are plain text, escaped,
|
|
142
|
+
> neither trimmed nor joined. A `MailMessage` has no `view()`, `markdown()`, `mailer()` or theme: for a mail of your
|
|
143
|
+
> own design, return a [mailable](mail.md) from `toMail()`. A mailable is sent to its own recipients, not to the
|
|
144
|
+
> notifiable's address.
|
|
145
|
+
|
|
146
|
+
### Error messages
|
|
147
|
+
|
|
148
|
+
`error()` gives the message the error level, which colours its button red; `success()` colours it green, and
|
|
149
|
+
`level('info' | 'success' | 'error')` sets it directly.
|
|
150
|
+
|
|
151
|
+
### Customizing the subject
|
|
152
|
+
|
|
153
|
+
When you don't call `subject()`, the subject is the class name in title case: `InvoicePaid` is sent as "Invoice Paid".
|
|
154
|
+
A notification written as an object literal has the name `Object`, so its subject is "Object": give it a `subject()`.
|
|
155
|
+
|
|
156
|
+
### Customizing the recipient
|
|
157
|
+
|
|
158
|
+
The mail channel sends to the notifiable's `email` attribute. To send elsewhere, add a `routeNotificationForMail()`
|
|
159
|
+
method to the model:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
class Customer extends Notifiable(Model) {
|
|
163
|
+
declare billing_email: string
|
|
164
|
+
|
|
165
|
+
routeNotificationForMail(): string {
|
|
166
|
+
return this.billing_email
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
When there is no route, as for a notifiable with no email address, nothing is sent.
|
|
172
|
+
|
|
173
|
+
## Database notifications
|
|
174
|
+
|
|
175
|
+
### Prerequisites
|
|
176
|
+
|
|
177
|
+
The database channel stores each notification in the `notifications` table, which a new app's migrations create.
|
|
178
|
+
`toDatabase()` (or `toArray()`) returns the data to store, and the notifiable must be a model:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
class InvoicePaid extends Notification {
|
|
182
|
+
constructor(readonly amount: string) {
|
|
183
|
+
super()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
via(): string[] {
|
|
187
|
+
return ['database']
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
toDatabase(): Record<string, unknown> {
|
|
191
|
+
return { amount: this.amount }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The row's `type` is the notification's class name, and its `data` is what `toDatabase()` returned.
|
|
197
|
+
|
|
198
|
+
### Accessing the notifications
|
|
199
|
+
|
|
200
|
+
`notifications()` is a relation, newest first; `unreadNotifications()` and `readNotifications()` filter it:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
const user = await User.findOrFail(1)
|
|
204
|
+
|
|
205
|
+
for (const notification of await user.unreadNotifications().get()) {
|
|
206
|
+
console.log(notification.data)
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Marking notifications as read
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
const user = await User.findOrFail(1)
|
|
214
|
+
const notification = await user.notifications().firstOrFail()
|
|
215
|
+
|
|
216
|
+
await notification.markAsRead()
|
|
217
|
+
await notification.markAsUnread()
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`read()` and `unread()` tell which it is, and a collection of notifications has its own `markAsRead()`.
|
|
221
|
+
|
|
222
|
+
## Custom channels
|
|
223
|
+
|
|
224
|
+
`Notification.extend(name, factory)` adds a channel: an object with `send(notifiable, notification)`. List its name in
|
|
225
|
+
`via()`.
|