@orieljs/framework 0.1.0-alpha.6 → 0.1.0-alpha.8
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-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-ZOEPKBVF.js → chunk-SPQUX3C7.js} +1347 -428
- package/dist/chunk-SPQUX3C7.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
package/docs/helpers.md
ADDED
|
@@ -0,0 +1,1281 @@
|
|
|
1
|
+
# Helpers
|
|
2
|
+
|
|
3
|
+
Oriel includes Laravel's string and array helpers, `Str`, `Stringable` and `Arr`, and its helper functions, such as
|
|
4
|
+
`config()`, `route()` and `retry()`.
|
|
5
|
+
|
|
6
|
+
## Introduction
|
|
7
|
+
|
|
8
|
+
The framework uses these helpers itself, and you can use them in your own code. Each one is a named export of
|
|
9
|
+
`@orieljs/framework`, so you import it as you import anything else:
|
|
10
|
+
|
|
11
|
+
```ts title="app/Http/Controllers/PostController.ts"
|
|
12
|
+
import { redirect, request, Str } from '@orieljs/framework'
|
|
13
|
+
import { Post } from '../../Models/Post'
|
|
14
|
+
import { Controller } from './Controller'
|
|
15
|
+
|
|
16
|
+
export class PostController extends Controller {
|
|
17
|
+
async store() {
|
|
18
|
+
const title = request().string('title').trim().toString()
|
|
19
|
+
const post = await Post.create({ title, slug: Str.slug(title), body: '' })
|
|
20
|
+
|
|
21
|
+
return redirect().route('posts.show', post)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
> [!NOTE]
|
|
27
|
+
> Laravel's helpers are global PHP functions, written in snake case (`abort_if()`, `csrf_token()`), and its string
|
|
28
|
+
> and array classes are called with `::`. Oriel's are exports you import, in camel case (`abortIf()`, `csrfToken()`),
|
|
29
|
+
> and `Str` and `Arr` are objects: `Str.slug()` for `Str::slug()`.
|
|
30
|
+
|
|
31
|
+
## Available methods
|
|
32
|
+
|
|
33
|
+
**Arrays and objects:** [`Arr.dot()`](#arrdot), [`Arr.except()`](#arrexcept), [`Arr.first()`](#arrfirst),
|
|
34
|
+
[`Arr.forget()`](#arrforget), [`Arr.get()`](#arrget), [`Arr.has()`](#arrhas), [`Arr.last()`](#arrlast),
|
|
35
|
+
[`Arr.only()`](#arronly), [`Arr.set()`](#arrset), [`Arr.undot()`](#arrundot), [`Arr.wrap()`](#arrwrap),
|
|
36
|
+
[`classNames()`](#classnames), [`styleString()`](#stylestring)
|
|
37
|
+
|
|
38
|
+
**Paths:** [`app().basePath()` and the other paths](#paths)
|
|
39
|
+
|
|
40
|
+
**Strings:** [`__()`](#translation-strings), [`e()`](#e), [`Str.after()`](#strafter),
|
|
41
|
+
[`Str.afterLast()`](#strafterlast), [`Str.ascii()`](#strascii), [`Str.before()`](#strbefore),
|
|
42
|
+
[`Str.beforeLast()`](#strbeforelast), [`Str.between()`](#strbetween), [`Str.camel()`](#strcamel),
|
|
43
|
+
[`Str.contains()`](#strcontains), [`Str.endsWith()`](#strendswith), [`Str.finish()`](#strfinish), [`Str.is()`](#stris),
|
|
44
|
+
[`Str.isUlid()`](#strisulid), [`Str.isUuid()`](#strisuuid), [`Str.kebab()`](#strkebab), [`Str.lcfirst()`](#strlcfirst),
|
|
45
|
+
[`Str.limit()`](#strlimit), [`Str.of()`](#strof), [`Str.plural()`](#strplural), [`Str.random()`](#strrandom),
|
|
46
|
+
[`Str.singular()`](#strsingular), [`Str.slug()`](#strslug), [`Str.snake()`](#strsnake), [`Str.start()`](#strstart),
|
|
47
|
+
[`Str.startsWith()`](#strstartswith), [`Str.studly()`](#strstudly), [`Str.title()`](#strtitle),
|
|
48
|
+
[`Str.ucfirst()`](#strucfirst), [`Str.ulid()`](#strulid), [`Str.uuid()`](#struuid), [`Str.uuid7()`](#struuid7)
|
|
49
|
+
|
|
50
|
+
**Fluent strings:** the methods of a `Stringable`, listed in [Fluent strings](#fluent-strings)
|
|
51
|
+
|
|
52
|
+
**URLs:** [`route()`](#route), [`url()`](#url)
|
|
53
|
+
|
|
54
|
+
**Miscellaneous:** [`abort()`](#abort), [`abortIf()`](#abortif), [`abortUnless()`](#abortunless), [`app()`](#app),
|
|
55
|
+
[`auth()`](#auth), [`back()`](#back), [`bcrypt()`](#bcrypt), [`cache()`](#cache), [`collect()`](#collect),
|
|
56
|
+
[`config()`](#config), [`cookie()`](#cookie), [`csrfField()`](#csrffield), [`csrfToken()`](#csrftoken),
|
|
57
|
+
[`decrypt()`](#decrypt), [`dispatch()`](#dispatch), [`dispatchSync()`](#dispatchsync), [`encrypt()`](#encrypt),
|
|
58
|
+
[`env()`](#env), [`errors()`](#errors), [`event()`](#event), [`fake()`](#fake), [`inertia()`](#inertia),
|
|
59
|
+
[`inject()`](#inject), [`live()`](#live), [`logger()`](#logger), [`methodField()`](#methodfield), [`now()`](#now),
|
|
60
|
+
[`old()`](#old), [`raw()`](#raw), [`redirect()`](#redirect), [`request()`](#request), [`response()`](#response),
|
|
61
|
+
[`retry()`](#retry), [`session()`](#session), [`tap()`](#tap), [`value()`](#value), [`view()`](#view),
|
|
62
|
+
[`vite()`](#vite)
|
|
63
|
+
|
|
64
|
+
**Other utilities:** [Pipeline](#pipeline)
|
|
65
|
+
|
|
66
|
+
## Arrays and objects
|
|
67
|
+
|
|
68
|
+
The `Arr` methods work on plain objects and arrays. The ones that take a key read it with dots, so `'products.desk'`
|
|
69
|
+
is the `desk` key of the `products` object, and `'users.0.name'` the `name` of the first user.
|
|
70
|
+
|
|
71
|
+
#### `Arr.dot()`
|
|
72
|
+
|
|
73
|
+
`Arr.dot()` flattens a nested object into one level, with dotted keys:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const flattened = Arr.dot({ products: { desk: { price: 100 } } })
|
|
77
|
+
|
|
78
|
+
// { 'products.desk.price': 100 }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Arrays are flattened by index (`'tags.0'`), and an empty object or array stays a value.
|
|
82
|
+
|
|
83
|
+
#### `Arr.except()`
|
|
84
|
+
|
|
85
|
+
`Arr.except()` returns a copy of the object without the keys you list:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const filtered = Arr.except({ name: 'Desk', price: 100 }, ['price'])
|
|
89
|
+
|
|
90
|
+
// { name: 'Desk' }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The keys are top-level keys, where Laravel's `Arr::except()` also reads a dotted key as a path.
|
|
94
|
+
|
|
95
|
+
#### `Arr.first()`
|
|
96
|
+
|
|
97
|
+
`Arr.first()` returns the first item of an array that passes a test, or the default when none does:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const first = Arr.first([100, 200, 300], (value) => value >= 150)
|
|
101
|
+
|
|
102
|
+
// 200
|
|
103
|
+
|
|
104
|
+
const none = Arr.first([100, 200, 300], (value) => value >= 500, 'none')
|
|
105
|
+
|
|
106
|
+
// 'none'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The test receives the item and its index. Without a test, it returns the first item, or the default for an empty
|
|
110
|
+
array.
|
|
111
|
+
|
|
112
|
+
#### `Arr.forget()`
|
|
113
|
+
|
|
114
|
+
`Arr.forget()` deletes a key, given with dots, from the object itself:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const data = { products: { desk: { price: 100 } } }
|
|
118
|
+
|
|
119
|
+
Arr.forget(data, 'products.desk')
|
|
120
|
+
|
|
121
|
+
// { products: {} }
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### `Arr.get()`
|
|
125
|
+
|
|
126
|
+
`Arr.get()` reads a value with dots, and returns the default when a key on the way is missing:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const price = Arr.get({ products: { desk: { price: 100 } } }, 'products.desk.price')
|
|
130
|
+
|
|
131
|
+
// 100
|
|
132
|
+
|
|
133
|
+
const discount = Arr.get({ products: { desk: { price: 100 } } }, 'products.desk.discount', 0)
|
|
134
|
+
|
|
135
|
+
// 0
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
A type argument types the value, as a cast, not a check: `Arr.get<number>(data, 'products.desk.price')`.
|
|
139
|
+
|
|
140
|
+
#### `Arr.has()`
|
|
141
|
+
|
|
142
|
+
`Arr.has()` tells you whether a key, given with dots, exists:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
const has = Arr.has({ product: { name: 'Desk', price: 100 } }, 'product.name')
|
|
146
|
+
|
|
147
|
+
// true
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
A key that holds `null` or `undefined` exists.
|
|
151
|
+
|
|
152
|
+
#### `Arr.last()`
|
|
153
|
+
|
|
154
|
+
`Arr.last()` returns the last item of an array that passes a test, or the default when none does:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const last = Arr.last([100, 200, 300, 110], (value) => value >= 150)
|
|
158
|
+
|
|
159
|
+
// 300
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### `Arr.only()`
|
|
163
|
+
|
|
164
|
+
`Arr.only()` returns a copy of the object with only the keys you list:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const slice = Arr.only({ name: 'Desk', price: 100, orders: 10 }, ['name', 'price'])
|
|
168
|
+
|
|
169
|
+
// { name: 'Desk', price: 100 }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### `Arr.set()`
|
|
173
|
+
|
|
174
|
+
`Arr.set()` sets a value with dots, in the object itself, and creates the objects on the way:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
const data = { products: { desk: { price: 100 } } }
|
|
178
|
+
|
|
179
|
+
Arr.set(data, 'products.desk.price', 200)
|
|
180
|
+
|
|
181
|
+
// { products: { desk: { price: 200 } } }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
It returns the object. A key with a `__proto__`, `prototype` or `constructor` part throws, so data from a request
|
|
185
|
+
can't change an object's prototype.
|
|
186
|
+
|
|
187
|
+
#### `Arr.undot()`
|
|
188
|
+
|
|
189
|
+
`Arr.undot()` turns dotted keys back into nested objects:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const nested = Arr.undot({ 'user.name': 'Kevin Malone', 'user.occupation': 'Accountant' })
|
|
193
|
+
|
|
194
|
+
// { user: { name: 'Kevin Malone', occupation: 'Accountant' } }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### `Arr.wrap()`
|
|
198
|
+
|
|
199
|
+
`Arr.wrap()` wraps a value in an array. An array is returned as it is, and `null` or `undefined` gives an empty
|
|
200
|
+
array:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
Arr.wrap('Laravel') // ['Laravel']
|
|
204
|
+
Arr.wrap(['Laravel']) // ['Laravel']
|
|
205
|
+
Arr.wrap(null) // []
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### `classNames()`
|
|
209
|
+
|
|
210
|
+
`classNames()` builds a `class` attribute from strings, lists and `{ name: condition }` objects, as Laravel's
|
|
211
|
+
`Arr::toCssClasses()` does. The `class` attribute of a TSX view takes the same values:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const isActive = false
|
|
215
|
+
const hasError = true
|
|
216
|
+
|
|
217
|
+
const classes = classNames(['p-4', { 'font-bold': isActive, 'bg-red': hasError }])
|
|
218
|
+
|
|
219
|
+
// 'p-4 bg-red'
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### `styleString()`
|
|
223
|
+
|
|
224
|
+
`styleString()` builds a `style` attribute, as Laravel's `Arr::toCssStyles()` does, but from property and value pairs,
|
|
225
|
+
as React's `style` takes them, where Laravel's takes `property: value` strings. Property names are written in kebab
|
|
226
|
+
case, and a number gets `px` unless the property takes a plain number:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
const style = styleString({ backgroundColor: 'red', marginTop: 4, opacity: 0.5, color: false })
|
|
230
|
+
|
|
231
|
+
// 'background-color:red;margin-top:4px;opacity:0.5'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
A property whose value is `false`, `null`, `undefined` or `''` is left out.
|
|
235
|
+
|
|
236
|
+
## Paths
|
|
237
|
+
|
|
238
|
+
The application knows where its folders are. `app()` returns it, and each method takes a path to add:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
const base = app().basePath()
|
|
242
|
+
const path = app().appPath('Models/User.ts')
|
|
243
|
+
const bootstrap = app().bootstrapPath('app.ts')
|
|
244
|
+
const config = app().configPath('app.ts')
|
|
245
|
+
const migrations = app().databasePath('migrations')
|
|
246
|
+
const css = app().publicPath('css/app.css')
|
|
247
|
+
const views = app().resourcePath('views')
|
|
248
|
+
const logs = app().storagePath('logs/oriel.log')
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`basePath()` is the `basePath` your `bootstrap/app.ts` gives `Application.configure()`, your app's root folder. The
|
|
252
|
+
others are its `app`, `bootstrap`, `config`, `database`, `public`, `resources` and `storage` folders.
|
|
253
|
+
|
|
254
|
+
> [!NOTE]
|
|
255
|
+
> Laravel has a helper function for each path, such as `storage_path()`. Oriel has the application's methods only:
|
|
256
|
+
> `app().storagePath()`.
|
|
257
|
+
|
|
258
|
+
## Strings
|
|
259
|
+
|
|
260
|
+
#### Translation strings
|
|
261
|
+
|
|
262
|
+
`__()` returns the line for a translation key, with its placeholders filled:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const message = __('auth.throttle', { seconds: 30 })
|
|
266
|
+
|
|
267
|
+
// 'Too many login attempts. Please try again in 30 seconds.'
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
A key with no line comes back as it is, so a sentence can be its own key, with its placeholders. `:name` takes the
|
|
271
|
+
value as given, `:Name` with its first letter upper-cased, and `:NAME` upper-cased:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
const greeting = __('Welcome, :Name', { name: 'dayle' })
|
|
275
|
+
|
|
276
|
+
// 'Welcome, Dayle'
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> [!NOTE]
|
|
280
|
+
> Laravel reads the lines from your app's language files. Oriel has no language files: `__()` knows the English lines
|
|
281
|
+
> of Laravel's `auth` and `passwords` files (`auth.failed`, `auth.password`, `auth.throttle`, `passwords.reset`,
|
|
282
|
+
> `passwords.sent`, `passwords.throttled`, `passwords.token` and `passwords.user`), which the starter kits use, and
|
|
283
|
+
> returns any other key as it is.
|
|
284
|
+
|
|
285
|
+
#### `e()`
|
|
286
|
+
|
|
287
|
+
`e()` escapes `&`, `<`, `>`, `"` and `'` for HTML:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
const html = e('<html>foo</html>')
|
|
291
|
+
|
|
292
|
+
// '<html>foo</html>'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`null` and `undefined` give `''`, and markup marked with [`raw()`](#raw) comes back unescaped. TSX views escape what
|
|
296
|
+
they print themselves, so you need `e()` only for HTML you build in a string.
|
|
297
|
+
|
|
298
|
+
#### `Str.after()`
|
|
299
|
+
|
|
300
|
+
`Str.after()` returns everything after the first occurrence of a value, or the whole string when the value isn't in
|
|
301
|
+
it:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
const slice = Str.after('This is my name', 'This is')
|
|
305
|
+
|
|
306
|
+
// ' my name'
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### `Str.afterLast()`
|
|
310
|
+
|
|
311
|
+
`Str.afterLast()` returns everything after the last occurrence of a value, or the whole string when the value isn't
|
|
312
|
+
in it:
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
const slice = Str.afterLast('app/Http/Controllers/Controller.ts', '/')
|
|
316
|
+
|
|
317
|
+
// 'Controller.ts'
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
#### `Str.ascii()`
|
|
321
|
+
|
|
322
|
+
`Str.ascii()` transliterates a string into ASCII, as Laravel's does for English:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
const ascii = Str.ascii('Café crème')
|
|
326
|
+
|
|
327
|
+
// 'Cafe creme'
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
What has no ASCII form, such as Chinese characters or an emoji, is dropped. [`Str.slug()`](#strslug) has the
|
|
331
|
+
details.
|
|
332
|
+
|
|
333
|
+
#### `Str.before()`
|
|
334
|
+
|
|
335
|
+
`Str.before()` returns everything before the first occurrence of a value:
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
const slice = Str.before('This is my name', 'my name')
|
|
339
|
+
|
|
340
|
+
// 'This is '
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
#### `Str.beforeLast()`
|
|
344
|
+
|
|
345
|
+
`Str.beforeLast()` returns everything before the last occurrence of a value:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
const slice = Str.beforeLast('This is my name', 'is')
|
|
349
|
+
|
|
350
|
+
// 'This '
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### `Str.between()`
|
|
354
|
+
|
|
355
|
+
`Str.between()` returns the part between two values: after the first occurrence of the first, and before the last
|
|
356
|
+
occurrence of the second:
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
const slice = Str.between('This is my name', 'This', 'name')
|
|
360
|
+
|
|
361
|
+
// ' is my '
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
#### `Str.camel()`
|
|
365
|
+
|
|
366
|
+
`Str.camel()` converts a string to camel case:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
const converted = Str.camel('foo_bar')
|
|
370
|
+
|
|
371
|
+
// 'fooBar'
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
#### `Str.contains()`
|
|
375
|
+
|
|
376
|
+
`Str.contains()` tells you whether a string contains a value, or any of a list of values. It is case-sensitive:
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
const contains = Str.contains('This is my name', 'my')
|
|
380
|
+
|
|
381
|
+
// true
|
|
382
|
+
|
|
383
|
+
const containsAny = Str.contains('This is my name', ['my', 'foo'])
|
|
384
|
+
|
|
385
|
+
// true
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### `Str.endsWith()`
|
|
389
|
+
|
|
390
|
+
`Str.endsWith()` tells you whether a string ends with a value, or with any of a list of values:
|
|
391
|
+
|
|
392
|
+
```ts
|
|
393
|
+
const result = Str.endsWith('This is my name', 'name')
|
|
394
|
+
|
|
395
|
+
// true
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
#### `Str.finish()`
|
|
399
|
+
|
|
400
|
+
`Str.finish()` adds a value to the end of a string, unless it already ends with it:
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
const adjusted = Str.finish('this/string', '/')
|
|
404
|
+
|
|
405
|
+
// 'this/string/'
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
#### `Str.is()`
|
|
409
|
+
|
|
410
|
+
`Str.is()` tells you whether a string matches a pattern, in which `*` matches anything and every other character is
|
|
411
|
+
literal:
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
const matches = Str.is('foo*', 'foobar')
|
|
415
|
+
|
|
416
|
+
// true
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
#### `Str.isUlid()`
|
|
420
|
+
|
|
421
|
+
`Str.isUlid()` tells you whether a value is a ULID, in either case:
|
|
422
|
+
|
|
423
|
+
```ts
|
|
424
|
+
const isUlid = Str.isUlid('01gd6r360bp37zj17nxb55yv40')
|
|
425
|
+
|
|
426
|
+
// true
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### `Str.isUuid()`
|
|
430
|
+
|
|
431
|
+
`Str.isUuid()` tells you whether a value is a UUID, of any version:
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
const isUuid = Str.isUuid('a0a2a2d2-0b87-4a18-83f2-2529882be2de')
|
|
435
|
+
|
|
436
|
+
// true
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Both checks narrow the value's type to `string`.
|
|
440
|
+
|
|
441
|
+
#### `Str.kebab()`
|
|
442
|
+
|
|
443
|
+
`Str.kebab()` converts a string to kebab case:
|
|
444
|
+
|
|
445
|
+
```ts
|
|
446
|
+
const converted = Str.kebab('fooBar')
|
|
447
|
+
|
|
448
|
+
// 'foo-bar'
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### `Str.lcfirst()`
|
|
452
|
+
|
|
453
|
+
`Str.lcfirst()` lower-cases the first character of a string:
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
const string = Str.lcfirst('Foo Bar')
|
|
457
|
+
|
|
458
|
+
// 'foo Bar'
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
#### `Str.limit()`
|
|
462
|
+
|
|
463
|
+
`Str.limit()` cuts a string to a number of characters and adds `...` when it had more:
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
const truncated = Str.limit('The quick brown fox jumps over the lazy dog', 20)
|
|
467
|
+
|
|
468
|
+
// 'The quick brown fox...'
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
The third argument replaces the `...`:
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
const truncated = Str.limit('The quick brown fox jumps over the lazy dog', 20, ' (...)')
|
|
475
|
+
|
|
476
|
+
// 'The quick brown fox (...)'
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### `Str.of()`
|
|
480
|
+
|
|
481
|
+
`Str.of()` wraps a string in a `Stringable`, so you can chain the string methods. `null` and `undefined` give an
|
|
482
|
+
empty string, and any other value is turned into a string:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
const slug = Str.of(' Laravel ').trim().append(' Framework').slug().toString()
|
|
486
|
+
|
|
487
|
+
// 'laravel-framework'
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
[Fluent strings](#fluent-strings) lists the methods.
|
|
491
|
+
|
|
492
|
+
#### `Str.plural()`
|
|
493
|
+
|
|
494
|
+
`Str.plural()` returns the plural form of an English word:
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
const plural = Str.plural('car')
|
|
498
|
+
|
|
499
|
+
// 'cars'
|
|
500
|
+
|
|
501
|
+
const children = Str.plural('child')
|
|
502
|
+
|
|
503
|
+
// 'children'
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
A count as the second argument gives the singular for 1 and the plural for any other number:
|
|
507
|
+
|
|
508
|
+
```ts
|
|
509
|
+
const singular = Str.plural('child', 1)
|
|
510
|
+
|
|
511
|
+
// 'child'
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### `Str.random()`
|
|
515
|
+
|
|
516
|
+
`Str.random()` returns a random string of letters and digits, 16 characters long unless you give a length. It uses
|
|
517
|
+
Node's cryptographically secure random bytes:
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
const random = Str.random(40)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
#### `Str.singular()`
|
|
524
|
+
|
|
525
|
+
`Str.singular()` returns the singular form of an English word:
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
const singular = Str.singular('cars')
|
|
529
|
+
|
|
530
|
+
// 'car'
|
|
531
|
+
|
|
532
|
+
const child = Str.singular('children')
|
|
533
|
+
|
|
534
|
+
// 'child'
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
#### `Str.slug()`
|
|
538
|
+
|
|
539
|
+
`Str.slug()` makes a URL-friendly slug of a string: lower-case letters and digits joined by the separator, `-` unless
|
|
540
|
+
you give another:
|
|
541
|
+
|
|
542
|
+
```ts
|
|
543
|
+
const slug = Str.slug('Laravel 5 Framework', '-')
|
|
544
|
+
|
|
545
|
+
// 'laravel-5-framework'
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
The string is transliterated to ASCII first, as Laravel's is for English, so accented and non-Latin letters keep a
|
|
549
|
+
readable form:
|
|
550
|
+
|
|
551
|
+
```ts
|
|
552
|
+
Str.slug('Café crème brûlée') // 'cafe-creme-brulee'
|
|
553
|
+
Str.slug('Привет, мир!') // 'privet-mir'
|
|
554
|
+
Str.slug('über@home') // 'uber-at-home'
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
`@` is read as `at`, and underscores and dashes become the separator. What has no ASCII form is dropped, so
|
|
558
|
+
`Str.slug('日本語')` is `''`: give such a title a slug of your own.
|
|
559
|
+
|
|
560
|
+
The third argument is the language, `'en'`; `null` skips the transliteration, and keeps every letter and digit. The
|
|
561
|
+
fourth is the dictionary of words to put in, `{ '@': 'at' }` unless you give another:
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
Str.slug('Café crème', '-', null) // 'café-crème'
|
|
565
|
+
Str.slug('5 & 6', '-', 'en', { '&': 'and' }) // '5-and-6'
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
#### `Str.snake()`
|
|
569
|
+
|
|
570
|
+
`Str.snake()` converts a string to snake case, with `_` or the delimiter you give:
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
const converted = Str.snake('fooBar')
|
|
574
|
+
|
|
575
|
+
// 'foo_bar'
|
|
576
|
+
|
|
577
|
+
const dashed = Str.snake('fooBar', '-')
|
|
578
|
+
|
|
579
|
+
// 'foo-bar'
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### `Str.start()`
|
|
583
|
+
|
|
584
|
+
`Str.start()` adds a value to the start of a string, unless it already starts with it:
|
|
585
|
+
|
|
586
|
+
```ts
|
|
587
|
+
const adjusted = Str.start('this/string', '/')
|
|
588
|
+
|
|
589
|
+
// '/this/string'
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
#### `Str.startsWith()`
|
|
593
|
+
|
|
594
|
+
`Str.startsWith()` tells you whether a string starts with a value, or with any of a list of values:
|
|
595
|
+
|
|
596
|
+
```ts
|
|
597
|
+
const result = Str.startsWith('This is my name', 'This')
|
|
598
|
+
|
|
599
|
+
// true
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
#### `Str.studly()`
|
|
603
|
+
|
|
604
|
+
`Str.studly()` converts a string to studly case:
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
const converted = Str.studly('foo_bar')
|
|
608
|
+
|
|
609
|
+
// 'FooBar'
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
#### `Str.title()`
|
|
613
|
+
|
|
614
|
+
`Str.title()` lower-cases a string, then upper-cases the first letter of each word:
|
|
615
|
+
|
|
616
|
+
```ts
|
|
617
|
+
const converted = Str.title('a nice title uses the correct case')
|
|
618
|
+
|
|
619
|
+
// 'A Nice Title Uses The Correct Case'
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
A word starts after a space, so `'hello-world'` gives `'Hello-world'`.
|
|
623
|
+
|
|
624
|
+
#### `Str.ucfirst()`
|
|
625
|
+
|
|
626
|
+
`Str.ucfirst()` upper-cases the first character of a string:
|
|
627
|
+
|
|
628
|
+
```ts
|
|
629
|
+
const string = Str.ucfirst('foo bar')
|
|
630
|
+
|
|
631
|
+
// 'Foo bar'
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
#### `Str.ulid()`
|
|
635
|
+
|
|
636
|
+
`Str.ulid()` returns a new ULID: 26 upper-case characters, the time first, so a later ULID sorts after an earlier
|
|
637
|
+
one. It takes a `Date` to use instead of now:
|
|
638
|
+
|
|
639
|
+
```ts
|
|
640
|
+
const ulid = Str.ulid()
|
|
641
|
+
|
|
642
|
+
// '01M59V508YR4SZQ9PPYB9ZBX8P'
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
#### `Str.uuid()`
|
|
646
|
+
|
|
647
|
+
`Str.uuid()` returns a random version 4 UUID:
|
|
648
|
+
|
|
649
|
+
```ts
|
|
650
|
+
const uuid = Str.uuid()
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
#### `Str.uuid7()`
|
|
654
|
+
|
|
655
|
+
`Str.uuid7()` returns a version 7 UUID, which starts with the time, so a later one sorts after an earlier one. It
|
|
656
|
+
takes a `Date` to use instead of now:
|
|
657
|
+
|
|
658
|
+
```ts
|
|
659
|
+
const uuid = Str.uuid7()
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
`Str.ulid()` and `Str.uuid7()` throw a `TypeError` for an invalid date, or one before 1970 or after the year 10889.
|
|
663
|
+
|
|
664
|
+
## Fluent strings
|
|
665
|
+
|
|
666
|
+
A `Stringable` is a string with the string methods as chainable methods. `Str.of()` makes one, and
|
|
667
|
+
`request().string('name')` returns one:
|
|
668
|
+
|
|
669
|
+
```ts
|
|
670
|
+
const slug = Str.of(' Hello World ').trim().slug().toString()
|
|
671
|
+
|
|
672
|
+
// 'hello-world'
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
Each method returns a new `Stringable`, except those in the second table, which return an answer. A `Stringable`
|
|
676
|
+
prints as its string in a template literal and in JSON; `toString()` or `value()` gives you the string itself.
|
|
677
|
+
|
|
678
|
+
| Method | Returns |
|
|
679
|
+
| --- | --- |
|
|
680
|
+
| `after(search)`, `afterLast(search)` | As [`Str.after()`](#strafter) and [`Str.afterLast()`](#strafterlast) |
|
|
681
|
+
| `append(...values)` | The values added to the end |
|
|
682
|
+
| `ascii()` | As [`Str.ascii()`](#strascii) |
|
|
683
|
+
| `before(search)`, `beforeLast(search)` | As [`Str.before()`](#strbefore) and [`Str.beforeLast()`](#strbeforelast) |
|
|
684
|
+
| `between(from, to)` | As [`Str.between()`](#strbetween) |
|
|
685
|
+
| `camel()`, `kebab()`, `snake(delimiter)`, `studly()` | As [`Str.camel()`](#strcamel), [`Str.kebab()`](#strkebab), [`Str.snake()`](#strsnake) and [`Str.studly()`](#strstudly) |
|
|
686
|
+
| `finish(cap)`, `start(prefix)` | As [`Str.finish()`](#strfinish) and [`Str.start()`](#strstart) |
|
|
687
|
+
| `lcfirst()`, `ucfirst()` | As [`Str.lcfirst()`](#strlcfirst) and [`Str.ucfirst()`](#strucfirst) |
|
|
688
|
+
| `limit(limit, end)` | As [`Str.limit()`](#strlimit) |
|
|
689
|
+
| `lower()`, `upper()` | The string lower-cased or upper-cased |
|
|
690
|
+
| `ltrim(characters)`, `rtrim(characters)`, `trim(characters)` | The string without whitespace at the start, the end or both ends, or without the characters you give |
|
|
691
|
+
| `plural(count)`, `singular()` | As [`Str.plural()`](#strplural) and [`Str.singular()`](#strsingular) |
|
|
692
|
+
| `prepend(...values)` | The values added to the start |
|
|
693
|
+
| `replace(search, replace)` | Every occurrence of the value, or of each value of a list, replaced |
|
|
694
|
+
| `slug(separator)` | As [`Str.slug()`](#strslug) |
|
|
695
|
+
| `title()` | As [`Str.title()`](#strtitle) |
|
|
696
|
+
|
|
697
|
+
| Method | Returns |
|
|
698
|
+
| --- | --- |
|
|
699
|
+
| `contains(needles)`, `endsWith(needles)`, `startsWith(needles)` | As [`Str.contains()`](#strcontains), [`Str.endsWith()`](#strendswith) and [`Str.startsWith()`](#strstartswith) |
|
|
700
|
+
| `exactly(value)` | Whether the string is exactly the value, a string or a `Stringable` |
|
|
701
|
+
| `explode(delimiter)` | The parts between the delimiters, as a [collection](collections.md) |
|
|
702
|
+
| `is(pattern)` | As [`Str.is()`](#stris) |
|
|
703
|
+
| `isEmpty()`, `isNotEmpty()` | Whether the string is `''`, or isn't |
|
|
704
|
+
| `length()` | The number of characters, as Unicode code points |
|
|
705
|
+
| `toBoolean()` | `true` for `'1'`, `'true'`, `'on'` and `'yes'`, in any case, else `false` |
|
|
706
|
+
| `toInteger()`, `toFloat()` | The number the string starts with, or `0` |
|
|
707
|
+
| `toString()`, `value()`, `toJSON()` | The string |
|
|
708
|
+
|
|
709
|
+
`trim()` with no characters removes the invisible characters Laravel's `Str::trim()` removes too, such as a
|
|
710
|
+
zero-width space, along with the whitespace.
|
|
711
|
+
|
|
712
|
+
## URLs
|
|
713
|
+
|
|
714
|
+
#### `route()`
|
|
715
|
+
|
|
716
|
+
`route()` returns the URL of a named route, with its parameters:
|
|
717
|
+
|
|
718
|
+
```ts
|
|
719
|
+
declare const post: Post
|
|
720
|
+
// ---cut---
|
|
721
|
+
const url = route('posts.show', post)
|
|
722
|
+
|
|
723
|
+
const urlWithQuery = route('posts.show', { post, page: 2 })
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
The URL is absolute unless you pass `false` as the third argument. [Routing](routing.md#generating-urls-to-named-routes)
|
|
727
|
+
covers the parameters and the types `route:types` adds.
|
|
728
|
+
|
|
729
|
+
#### `url()`
|
|
730
|
+
|
|
731
|
+
`url()` returns an absolute URL for a path, with a query string from an object:
|
|
732
|
+
|
|
733
|
+
```ts
|
|
734
|
+
const profile = url('user/profile')
|
|
735
|
+
|
|
736
|
+
// 'http://localhost:8000/user/profile'
|
|
737
|
+
|
|
738
|
+
const withQuery = url('posts', { page: 2 })
|
|
739
|
+
|
|
740
|
+
// 'http://localhost:8000/posts?page=2'
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Pass `false` as the third argument for the path alone. With no arguments, `url()` returns the URL generator:
|
|
744
|
+
|
|
745
|
+
```ts
|
|
746
|
+
const current = url().current()
|
|
747
|
+
const previous = url().previous()
|
|
748
|
+
const asset = url().asset('img/photo.jpg')
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
`current()` is the current URL without its query string, and `previous()` the page the user came from. `asset()` is the
|
|
752
|
+
URL of a file in `public`, on `app.asset_url` when your config sets one.
|
|
753
|
+
|
|
754
|
+
## Miscellaneous
|
|
755
|
+
|
|
756
|
+
#### `abort()`
|
|
757
|
+
|
|
758
|
+
`abort()` throws an HTTP error, which Oriel renders as the response with that status:
|
|
759
|
+
|
|
760
|
+
```ts
|
|
761
|
+
abort(403)
|
|
762
|
+
abort(403, 'Unauthorized.', { 'X-Reason': 'banned' })
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
Pass a `Response` to send it as it is. [Error handling](errors.md) has the details.
|
|
766
|
+
|
|
767
|
+
#### `abortIf()`
|
|
768
|
+
|
|
769
|
+
`abortIf()` throws the HTTP error when the condition is truthy:
|
|
770
|
+
|
|
771
|
+
```ts
|
|
772
|
+
declare const user: User
|
|
773
|
+
// ---cut---
|
|
774
|
+
abortIf(user.email_verified_at === null, 403)
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
#### `abortUnless()`
|
|
778
|
+
|
|
779
|
+
`abortUnless()` throws the HTTP error when the condition is falsy:
|
|
780
|
+
|
|
781
|
+
```ts
|
|
782
|
+
declare const user: User
|
|
783
|
+
// ---cut---
|
|
784
|
+
abortUnless(user.email_verified_at !== null, 403)
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
#### `app()`
|
|
788
|
+
|
|
789
|
+
`app()` returns the application, the service container. With a token or a class, it resolves that from the
|
|
790
|
+
container:
|
|
791
|
+
|
|
792
|
+
```ts
|
|
793
|
+
import { app } from '@orieljs/framework'
|
|
794
|
+
import { Hasher } from '@orieljs/framework/contracts'
|
|
795
|
+
|
|
796
|
+
const container = app()
|
|
797
|
+
|
|
798
|
+
const hasher = app(Hasher)
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
A contract's token comes from `@orieljs/framework/contracts`. Inside a request, `app().make()` resolves from the
|
|
802
|
+
request's scope.
|
|
803
|
+
|
|
804
|
+
#### `auth()`
|
|
805
|
+
|
|
806
|
+
`auth()` returns the auth manager, and `auth('web')` a guard:
|
|
807
|
+
|
|
808
|
+
```ts
|
|
809
|
+
const signedIn = await auth().check()
|
|
810
|
+
|
|
811
|
+
const user = await auth().user()
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
#### `back()`
|
|
815
|
+
|
|
816
|
+
`back()` returns a redirect to the previous page, or to the fallback when there is none:
|
|
817
|
+
|
|
818
|
+
```ts
|
|
819
|
+
const response = back()
|
|
820
|
+
|
|
821
|
+
const withFallback = back('/dashboard')
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
#### `bcrypt()`
|
|
825
|
+
|
|
826
|
+
`bcrypt()` hashes a value with bcrypt, whatever your default hashing driver is. It returns a promise:
|
|
827
|
+
|
|
828
|
+
```ts
|
|
829
|
+
const password = await bcrypt('my-secret-password')
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
`{ rounds: 12 }` as the second argument sets the cost.
|
|
833
|
+
|
|
834
|
+
#### `cache()`
|
|
835
|
+
|
|
836
|
+
`cache()` with a key reads the value from the default cache store. With an object, it stores each key, for the
|
|
837
|
+
number of seconds you give, or forever:
|
|
838
|
+
|
|
839
|
+
```ts
|
|
840
|
+
const value = await cache('key')
|
|
841
|
+
|
|
842
|
+
await cache({ key: 'value' }, 300)
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
With no arguments, it returns the cache manager: `await cache().remember('users', 60, () => User.all())`. Every
|
|
846
|
+
call returns a promise.
|
|
847
|
+
|
|
848
|
+
#### `collect()`
|
|
849
|
+
|
|
850
|
+
`collect()` makes a collection of an array, or of any iterable, as [Collections](collections.md) describes:
|
|
851
|
+
|
|
852
|
+
```ts
|
|
853
|
+
const collection = collect(['Taylor', 'Abigail'])
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
#### `config()`
|
|
857
|
+
|
|
858
|
+
`config()` reads a configuration value, with dots, and returns the default when it isn't set:
|
|
859
|
+
|
|
860
|
+
```ts
|
|
861
|
+
const name = config('app.name')
|
|
862
|
+
|
|
863
|
+
const timezone = config('app.timezone', 'UTC')
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
An object sets values in the running application's configuration, and no arguments return the configuration
|
|
867
|
+
repository:
|
|
868
|
+
|
|
869
|
+
```ts
|
|
870
|
+
config({ 'app.debug': true })
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
A type argument types the value, as a cast: `config<string>('app.name')`.
|
|
874
|
+
|
|
875
|
+
#### `cookie()`
|
|
876
|
+
|
|
877
|
+
`cookie()` makes a cookie, for `minutes` minutes, which you queue or attach to a response:
|
|
878
|
+
|
|
879
|
+
```ts
|
|
880
|
+
const theme = cookie('theme', 'dark', 60)
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
With no arguments, it returns the cookie jar: `cookie().queue(cookie('theme', 'dark', 60))` sends the cookie with
|
|
884
|
+
the response.
|
|
885
|
+
|
|
886
|
+
#### `csrfField()`
|
|
887
|
+
|
|
888
|
+
`csrfField()` returns the hidden `_token` input of a form, with the session's CSRF token:
|
|
889
|
+
|
|
890
|
+
```tsx
|
|
891
|
+
const form = (
|
|
892
|
+
<form method="post" action="/profile">
|
|
893
|
+
{csrfField()}
|
|
894
|
+
</form>
|
|
895
|
+
)
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
#### `csrfToken()`
|
|
899
|
+
|
|
900
|
+
`csrfToken()` returns the session's CSRF token:
|
|
901
|
+
|
|
902
|
+
```ts
|
|
903
|
+
const token = csrfToken()
|
|
904
|
+
```
|
|
905
|
+
|
|
906
|
+
#### `decrypt()`
|
|
907
|
+
|
|
908
|
+
`decrypt()` decrypts a value made by [`encrypt()`](#encrypt), and throws a `DecryptError` when it can't:
|
|
909
|
+
|
|
910
|
+
```ts
|
|
911
|
+
declare const encrypted: string
|
|
912
|
+
// ---cut---
|
|
913
|
+
const value = decrypt<{ id: number }>(encrypted)
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
#### `dispatch()`
|
|
917
|
+
|
|
918
|
+
`dispatch()` sends a job to its queue, or runs it now when it isn't a queued job:
|
|
919
|
+
|
|
920
|
+
```ts
|
|
921
|
+
declare const podcast: Podcast
|
|
922
|
+
// ---cut---
|
|
923
|
+
await dispatch(new ProcessPodcast(podcast))
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
It returns the pending dispatch, so you can chain `onQueue()`, `delay()` and the other options. [Queues](queues.md)
|
|
927
|
+
covers jobs.
|
|
928
|
+
|
|
929
|
+
#### `dispatchSync()`
|
|
930
|
+
|
|
931
|
+
`dispatchSync()` runs a job now, in the current process, and returns a promise that settles when it is done:
|
|
932
|
+
|
|
933
|
+
```ts
|
|
934
|
+
declare const podcast: Podcast
|
|
935
|
+
// ---cut---
|
|
936
|
+
await dispatchSync(new ProcessPodcast(podcast))
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
#### `encrypt()`
|
|
940
|
+
|
|
941
|
+
`encrypt()` encrypts a value with your `APP_KEY`. The value is turned into JSON first, unless you pass `false` as the
|
|
942
|
+
second argument:
|
|
943
|
+
|
|
944
|
+
```ts
|
|
945
|
+
const secret = encrypt({ id: 1 })
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
#### `env()`
|
|
949
|
+
|
|
950
|
+
`env()` reads an environment variable, or returns the default when it isn't set. The default may be a function:
|
|
951
|
+
|
|
952
|
+
```ts
|
|
953
|
+
const environment = env('APP_ENV', 'production')
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
`true`, `false`, `null` and `empty`, in any case and with or without parentheses, become `true`, `false`, `null` and
|
|
957
|
+
`''`. A value in quotes loses them. Every other value stays a string: `PORT=8000` gives `'8000'`, not `8000`. Read
|
|
958
|
+
`env()` in your config files, and `config()` everywhere else.
|
|
959
|
+
|
|
960
|
+
#### `errors()`
|
|
961
|
+
|
|
962
|
+
`errors()` returns the validation errors the previous request flashed, as a message bag. It is empty when there are
|
|
963
|
+
none, or outside a request:
|
|
964
|
+
|
|
965
|
+
```ts
|
|
966
|
+
const message = errors().first('email')
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
`errors('login')` reads a named bag. [Validation](validation.md#displaying-the-validation-errors) shows it in a view.
|
|
970
|
+
|
|
971
|
+
#### `event()`
|
|
972
|
+
|
|
973
|
+
`event()` dispatches an event to its listeners, and returns a promise of their results:
|
|
974
|
+
|
|
975
|
+
```ts
|
|
976
|
+
declare const order: object
|
|
977
|
+
// ---cut---
|
|
978
|
+
await event(order)
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
#### `fake()`
|
|
982
|
+
|
|
983
|
+
`fake()` returns the fake data generator that factories use, shared by your whole process:
|
|
984
|
+
|
|
985
|
+
```ts
|
|
986
|
+
const name = fake().name()
|
|
987
|
+
|
|
988
|
+
const email = fake().unique().safeEmail()
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
#### `inertia()`
|
|
992
|
+
|
|
993
|
+
`inertia()` returns an Inertia response for a page, with its props:
|
|
994
|
+
|
|
995
|
+
```ts
|
|
996
|
+
declare const posts: Post[]
|
|
997
|
+
// ---cut---
|
|
998
|
+
const response = inertia('Posts/Index', { posts })
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
With no arguments, it returns the Inertia response factory.
|
|
1002
|
+
|
|
1003
|
+
#### `inject()`
|
|
1004
|
+
|
|
1005
|
+
`inject()` resolves a token or a class from the current container, the request's scope inside a request. It is how
|
|
1006
|
+
a class takes a service in a field:
|
|
1007
|
+
|
|
1008
|
+
```ts
|
|
1009
|
+
import { inject } from '@orieljs/framework'
|
|
1010
|
+
import { Hasher } from '@orieljs/framework/contracts'
|
|
1011
|
+
|
|
1012
|
+
class PasswordService {
|
|
1013
|
+
private readonly hasher = inject(Hasher)
|
|
1014
|
+
}
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
#### `live()`
|
|
1018
|
+
|
|
1019
|
+
`live()` returns a Live component's first render, as HTML for a view, with its parameters:
|
|
1020
|
+
|
|
1021
|
+
```ts
|
|
1022
|
+
declare const Counter: new () => Component
|
|
1023
|
+
// ---cut---
|
|
1024
|
+
const html = await live(Counter, { count: 3 })
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
In a TSX view, the `<Live component={Counter} />` element does the same.
|
|
1028
|
+
|
|
1029
|
+
#### `logger()`
|
|
1030
|
+
|
|
1031
|
+
`logger()` writes a `debug` message to the log, with its context:
|
|
1032
|
+
|
|
1033
|
+
```ts
|
|
1034
|
+
declare const user: User
|
|
1035
|
+
// ---cut---
|
|
1036
|
+
logger('User dashboard visited.', { id: user.id })
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
With no arguments, it returns the log manager: `logger().error('Something went wrong.')`.
|
|
1040
|
+
|
|
1041
|
+
#### `methodField()`
|
|
1042
|
+
|
|
1043
|
+
`methodField()` returns the hidden `_method` input that lets a form reach a `PUT`, `PATCH` or `DELETE` route:
|
|
1044
|
+
|
|
1045
|
+
```tsx
|
|
1046
|
+
const form = (
|
|
1047
|
+
<form method="post" action="/posts/1">
|
|
1048
|
+
{methodField('DELETE')}
|
|
1049
|
+
</form>
|
|
1050
|
+
)
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
#### `now()`
|
|
1054
|
+
|
|
1055
|
+
`now()` returns the current time as a `Date`:
|
|
1056
|
+
|
|
1057
|
+
```ts
|
|
1058
|
+
const time = now()
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
> [!NOTE]
|
|
1062
|
+
> Laravel's `now()` and `today()` return a Carbon instance, with its date methods. Oriel's `now()` returns a plain
|
|
1063
|
+
> `Date`, and there is no `today()`.
|
|
1064
|
+
|
|
1065
|
+
#### `old()`
|
|
1066
|
+
|
|
1067
|
+
`old()` returns the input the previous request flashed to the session, or the default:
|
|
1068
|
+
|
|
1069
|
+
```ts
|
|
1070
|
+
const title = old('title', '')
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
Outside a request, it returns the default. [Requests](requests.md#old-input) covers old input.
|
|
1074
|
+
|
|
1075
|
+
#### `raw()`
|
|
1076
|
+
|
|
1077
|
+
`raw()` marks HTML as safe, so a TSX view prints it without escaping it:
|
|
1078
|
+
|
|
1079
|
+
```tsx
|
|
1080
|
+
declare const html: string
|
|
1081
|
+
// ---cut---
|
|
1082
|
+
const body = <article>{raw(html)}</article>
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
Never pass it text a user wrote.
|
|
1086
|
+
|
|
1087
|
+
#### `redirect()`
|
|
1088
|
+
|
|
1089
|
+
`redirect()` returns a redirect to a path, with a status, 302 unless you give one. With no arguments, it returns the
|
|
1090
|
+
redirector:
|
|
1091
|
+
|
|
1092
|
+
```ts
|
|
1093
|
+
const response = redirect('/home')
|
|
1094
|
+
|
|
1095
|
+
const toRoute = redirect().route('dashboard')
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
[Responses](responses.md) covers redirects.
|
|
1099
|
+
|
|
1100
|
+
#### `request()`
|
|
1101
|
+
|
|
1102
|
+
`request()` returns the current request. With a key, it returns that input, or the default:
|
|
1103
|
+
|
|
1104
|
+
```ts
|
|
1105
|
+
const current = request()
|
|
1106
|
+
|
|
1107
|
+
const name = request('name', 'Guest')
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
[Requests](requests.md) covers the request.
|
|
1111
|
+
|
|
1112
|
+
#### `response()`
|
|
1113
|
+
|
|
1114
|
+
`response()` returns a `Response` with the content, a status and headers. With no arguments, it returns the response
|
|
1115
|
+
factory:
|
|
1116
|
+
|
|
1117
|
+
```ts
|
|
1118
|
+
const text = response('Hello World', 200, { 'Content-Type': 'text/plain' })
|
|
1119
|
+
|
|
1120
|
+
const json = response().json({ name: 'Abigail' })
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
#### `retry()`
|
|
1124
|
+
|
|
1125
|
+
`retry()` calls a function until it succeeds, up to a number of times, and returns what it returns. When the last
|
|
1126
|
+
attempt throws, it throws that error:
|
|
1127
|
+
|
|
1128
|
+
```ts
|
|
1129
|
+
declare const callService: (attempt: number) => Promise<string>
|
|
1130
|
+
// ---cut---
|
|
1131
|
+
const result = await retry(5, (attempt) => callService(attempt), 100)
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
The function receives the attempt's number, from 1. The third argument is the number of milliseconds to wait
|
|
1135
|
+
between attempts, or a function of the attempt's number that returns them:
|
|
1136
|
+
|
|
1137
|
+
```ts
|
|
1138
|
+
declare const callService: () => Promise<string>
|
|
1139
|
+
// ---cut---
|
|
1140
|
+
const result = await retry(3, () => callService(), (attempt) => attempt * 100)
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
The fourth argument decides which errors to retry: when it returns `false` for an error, `retry()` throws it at once:
|
|
1144
|
+
|
|
1145
|
+
```ts
|
|
1146
|
+
declare const callService: () => Promise<string>
|
|
1147
|
+
// ---cut---
|
|
1148
|
+
const result = await retry(5, () => callService(), 100, (error) => error instanceof TypeError)
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
#### `session()`
|
|
1152
|
+
|
|
1153
|
+
`session()` reads a value from the session, or returns the default. An object stores its values, and no arguments
|
|
1154
|
+
return the session:
|
|
1155
|
+
|
|
1156
|
+
```ts
|
|
1157
|
+
const value = session('key', 'default')
|
|
1158
|
+
|
|
1159
|
+
session({ chairs: 7, instruments: 3 })
|
|
1160
|
+
|
|
1161
|
+
const all = session().all()
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
[HTTP session](session.md) covers the session.
|
|
1165
|
+
|
|
1166
|
+
#### `tap()`
|
|
1167
|
+
|
|
1168
|
+
`tap()` calls a function with a value, then returns the value:
|
|
1169
|
+
|
|
1170
|
+
```ts
|
|
1171
|
+
declare const user: User
|
|
1172
|
+
// ---cut---
|
|
1173
|
+
const saved = tap(user, (user) => {
|
|
1174
|
+
user.name = 'Taylor'
|
|
1175
|
+
})
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
If the function returns a promise, `tap()` doesn't wait for it.
|
|
1179
|
+
|
|
1180
|
+
#### `value()`
|
|
1181
|
+
|
|
1182
|
+
`value()` returns the value it is given, or calls it when it is a function and returns the result:
|
|
1183
|
+
|
|
1184
|
+
```ts
|
|
1185
|
+
const five = value(5)
|
|
1186
|
+
|
|
1187
|
+
const six = value(() => 6)
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
#### `view()`
|
|
1191
|
+
|
|
1192
|
+
`view()` returns a view, with its data:
|
|
1193
|
+
|
|
1194
|
+
```ts
|
|
1195
|
+
declare const user: User
|
|
1196
|
+
// ---cut---
|
|
1197
|
+
const profile = view('user.profile', { user })
|
|
1198
|
+
```
|
|
1199
|
+
|
|
1200
|
+
With no arguments, it returns the view factory. [Views](views.md) covers views.
|
|
1201
|
+
|
|
1202
|
+
#### `vite()`
|
|
1203
|
+
|
|
1204
|
+
`vite()` returns the `<script>` and `<link>` tags of your Vite entry points, for a root view:
|
|
1205
|
+
|
|
1206
|
+
```tsx
|
|
1207
|
+
const head = <head>{vite(['resources/css/app.css', 'resources/js/app.tsx'])}</head>
|
|
1208
|
+
```
|
|
1209
|
+
|
|
1210
|
+
With no arguments, it returns the `Vite` instance.
|
|
1211
|
+
|
|
1212
|
+
## Other utilities
|
|
1213
|
+
|
|
1214
|
+
### Pipeline
|
|
1215
|
+
|
|
1216
|
+
A pipeline passes a value through a list of steps, each of which can inspect or change it and then call the next.
|
|
1217
|
+
It works as [middleware](middleware.md) does. Make one from the pipeline factory:
|
|
1218
|
+
|
|
1219
|
+
```ts
|
|
1220
|
+
declare const user: User
|
|
1221
|
+
// ---cut---
|
|
1222
|
+
const result = await app(PipelineFactory)
|
|
1223
|
+
.make<User>()
|
|
1224
|
+
.send(user)
|
|
1225
|
+
.through([
|
|
1226
|
+
async (user, next) => {
|
|
1227
|
+
// ...
|
|
1228
|
+
|
|
1229
|
+
return next(user)
|
|
1230
|
+
},
|
|
1231
|
+
async (user, next) => {
|
|
1232
|
+
// ...
|
|
1233
|
+
|
|
1234
|
+
return next(user)
|
|
1235
|
+
},
|
|
1236
|
+
])
|
|
1237
|
+
.then((user) => user)
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
Each step receives the value and `next`, which runs the steps after it and returns a promise of their result. A step
|
|
1241
|
+
that doesn't call `next` stops the pipeline: what it returns is the result. Once the last step calls `next`, the
|
|
1242
|
+
function you give `then()` runs. `thenReturn()` returns the value as the last step passed it, and `pipe()` adds steps
|
|
1243
|
+
after those of `through()`.
|
|
1244
|
+
|
|
1245
|
+
A step may also be a class with a `handle()` method. The pipeline makes it from the container, so its fields can use
|
|
1246
|
+
`inject()`:
|
|
1247
|
+
|
|
1248
|
+
```ts title="app/Pipes/GenerateProfilePhoto.ts"
|
|
1249
|
+
import type { PipeNext } from '@orieljs/framework/contracts'
|
|
1250
|
+
import type { User } from '../Models/User'
|
|
1251
|
+
|
|
1252
|
+
export class GenerateProfilePhoto {
|
|
1253
|
+
async handle(user: User, next: PipeNext<User>) {
|
|
1254
|
+
// ...
|
|
1255
|
+
|
|
1256
|
+
return next(user)
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
```ts
|
|
1262
|
+
declare const user: User
|
|
1263
|
+
declare class GenerateProfilePhoto {}
|
|
1264
|
+
declare class ActivateSubscription {}
|
|
1265
|
+
declare class SendWelcomeEmail {}
|
|
1266
|
+
// ---cut---
|
|
1267
|
+
const result = await app(PipelineFactory)
|
|
1268
|
+
.make<User>()
|
|
1269
|
+
.send(user)
|
|
1270
|
+
.through([GenerateProfilePhoto, ActivateSubscription, SendWelcomeEmail])
|
|
1271
|
+
.thenReturn()
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1274
|
+
`via('process')` calls another method on the classes. A class without the method fails the pipeline with a
|
|
1275
|
+
`PipelineError`: "Pipe [GenerateProfilePhoto] has no [handle] method.". `new BasePipeline<User>()` makes a pipeline
|
|
1276
|
+
without the factory.
|
|
1277
|
+
|
|
1278
|
+
> [!NOTE]
|
|
1279
|
+
> Laravel's pipeline runs its steps synchronously and has `withinTransaction()`. Oriel's steps may be async, so
|
|
1280
|
+
> `then()` and `thenReturn()` return a promise; there is no `withinTransaction()`: call `DB.transaction()` around the
|
|
1281
|
+
> pipeline instead.
|