@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,218 @@
|
|
|
1
|
+
# Eloquent: collections
|
|
2
|
+
|
|
3
|
+
Every query of models returns an `EloquentCollection`: a collection with Laravel's collection methods, and a few more
|
|
4
|
+
that know about models and their keys.
|
|
5
|
+
|
|
6
|
+
## Introduction
|
|
7
|
+
|
|
8
|
+
`get()`, `all()`, a has-many relation and an eager load all return an `EloquentCollection`. It has every method of
|
|
9
|
+
[Collections](collections.md), and it is iterable:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const users = await User.whereNotNull('email_verified_at').get()
|
|
13
|
+
|
|
14
|
+
for (const user of users) {
|
|
15
|
+
console.log(user.name)
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Methods that keep the same models, such as `filter()`, `sortBy()` and `where()`, return an `EloquentCollection`.
|
|
20
|
+
Methods that make other values, such as `map()` and `pluck()`, return a plain `Collection`:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const names = (await User.all()).filter((user) => user.email_verified_at !== null).map((user) => user.name)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`toBase()` returns the models in a plain collection.
|
|
27
|
+
|
|
28
|
+
> [!WARNING]
|
|
29
|
+
> A collection's `sum()`, `avg()`, `min()`, `max()` and `median()` count numbers only. A column read as a string, such
|
|
30
|
+
> as a `decimal:2` cast or a large integer on PostgreSQL, is left out: convert it first, with
|
|
31
|
+
> `sum((order) => Number(order.price))`.
|
|
32
|
+
|
|
33
|
+
## Available methods
|
|
34
|
+
|
|
35
|
+
#### `append(attributes)`
|
|
36
|
+
|
|
37
|
+
`append()` adds attributes to every model's `toArray()` and JSON, as a model's `appends` does (see
|
|
38
|
+
[Appending values to JSON](eloquent-mutators.md#appending-values-to-json)).
|
|
39
|
+
|
|
40
|
+
#### `contains(key)`
|
|
41
|
+
|
|
42
|
+
`contains()` tells you whether the collection holds a model, given the model or its key:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
declare const user: User
|
|
46
|
+
// ---cut---
|
|
47
|
+
const users = await User.all()
|
|
48
|
+
|
|
49
|
+
users.contains(1)
|
|
50
|
+
|
|
51
|
+
users.contains(user)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
With a key and a value, or a function, it is the collection's `contains()`. `containsStrict()` matches a model only by
|
|
55
|
+
identity, not by key.
|
|
56
|
+
|
|
57
|
+
#### `diff(items)`
|
|
58
|
+
|
|
59
|
+
`diff()` returns the models whose keys aren't among the models you give:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const users = (await User.all()).diff(await User.whereIn('id', [1, 2, 3]).get())
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### `except(...keys)`
|
|
66
|
+
|
|
67
|
+
`except()` returns the models without these keys:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const users = (await User.all()).except(1, 2, 3)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### `find(key)`
|
|
74
|
+
|
|
75
|
+
`find()` returns the model with that key, or `undefined`. Given a list of keys, it returns a collection of those
|
|
76
|
+
models. `findOrFail()` throws a `ModelNotFoundError` instead of returning `undefined`:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const users = await User.all()
|
|
80
|
+
|
|
81
|
+
const user = users.find(1)
|
|
82
|
+
|
|
83
|
+
const some = users.find([1, 2])
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### `fresh(...relations)`
|
|
87
|
+
|
|
88
|
+
`fresh()` reads every model again from the database, with the relations you name loaded, in one query:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const users = await User.all()
|
|
92
|
+
|
|
93
|
+
const fresh = await users.fresh('posts')
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### `intersect(items)`
|
|
97
|
+
|
|
98
|
+
`intersect()` returns the models whose keys are also among the models you give:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const users = (await User.all()).intersect(await User.whereIn('id', [1, 2, 3]).get())
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### `load(...relations)`
|
|
105
|
+
|
|
106
|
+
`load()` loads relations on every model, one query per relation, and takes the forms `with()` takes:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const users = await User.all()
|
|
110
|
+
|
|
111
|
+
await users.load('posts', 'roles')
|
|
112
|
+
|
|
113
|
+
await users.load({ posts: (query) => query.where('title', 'like', '%Oriel%') })
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`loadCount()` adds the counts, and `loadSum()`, `loadAvg()`, `loadMin()`, `loadMax()` and `loadExists()` the other
|
|
117
|
+
aggregates, on every model.
|
|
118
|
+
|
|
119
|
+
#### `loadMissing(...relations)`
|
|
120
|
+
|
|
121
|
+
`loadMissing()` loads the relations that aren't loaded yet on each model:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const users = await User.all()
|
|
125
|
+
|
|
126
|
+
await users.loadMissing('posts', 'roles')
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### `modelKeys()`
|
|
130
|
+
|
|
131
|
+
`modelKeys()` returns the models' primary keys:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const keys = (await User.all()).modelKeys() // [1, 2, 3, 4, 5]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### `makeVisible(attributes)` / `makeHidden(attributes)`
|
|
138
|
+
|
|
139
|
+
`makeVisible()` shows attributes that the model hides in its `toArray()` and JSON, and `makeHidden()` hides more, on
|
|
140
|
+
every model. `setVisible()` and `setHidden()` replace the lists:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const users = (await User.all()).makeVisible(['password', 'remember_token'])
|
|
144
|
+
|
|
145
|
+
const safe = (await User.all()).makeHidden(['email'])
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### `only(...keys)`
|
|
149
|
+
|
|
150
|
+
`only()` returns the models with these keys:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const users = (await User.all()).only(1, 2, 3)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### `toQuery()`
|
|
157
|
+
|
|
158
|
+
`toQuery()` returns a query that matches the collection's models, by their keys, so you can update them all at once:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const users = await User.whereNull('email_verified_at').get()
|
|
162
|
+
|
|
163
|
+
await users.toQuery().update({ email_verified_at: new Date() })
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The models must all be of one class, and the collection mustn't be empty.
|
|
167
|
+
|
|
168
|
+
#### `unique(key)`
|
|
169
|
+
|
|
170
|
+
`unique()` returns one model per key. Given an attribute or a function, it is the collection's `unique()`:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const users = (await User.all()).unique()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `merge(items)`
|
|
177
|
+
|
|
178
|
+
`merge()` adds the models you give, replacing those with the same key.
|
|
179
|
+
|
|
180
|
+
## Custom collections
|
|
181
|
+
|
|
182
|
+
A model returns its collections from `newCollection()`. Override it to return a subclass of `EloquentCollection` with
|
|
183
|
+
your own methods:
|
|
184
|
+
|
|
185
|
+
```ts title="app/Models/Team.ts"
|
|
186
|
+
import { EloquentCollection, Model } from '@orieljs/framework'
|
|
187
|
+
|
|
188
|
+
export class TeamCollection<T extends Model> extends EloquentCollection<T> {
|
|
189
|
+
names(): string[] {
|
|
190
|
+
return this.map((team) => String(team.getAttribute('name'))).all()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export class Team extends Model {
|
|
195
|
+
override newCollection<T extends Model>(this: T, models: T[] = []): TeamCollection<T> {
|
|
196
|
+
return new TeamCollection(models)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Every query, relation and eager load of `Team` then returns a `TeamCollection`. Their type is still
|
|
202
|
+
`EloquentCollection`, so narrow it to call your methods:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
declare class Team extends Model {}
|
|
206
|
+
declare class TeamCollection<T extends Model> extends EloquentCollection<T> {
|
|
207
|
+
names(): string[]
|
|
208
|
+
}
|
|
209
|
+
const teams = await Team.all()
|
|
210
|
+
// ---cut---
|
|
211
|
+
if (teams instanceof TeamCollection) {
|
|
212
|
+
console.log(teams.names())
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
> [!NOTE]
|
|
217
|
+
> Laravel names the collection class with a `#[CollectedBy]` attribute or a `newCollection()` method. Oriel uses the
|
|
218
|
+
> method.
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# Eloquent: factories
|
|
2
|
+
|
|
3
|
+
A factory makes models with realistic attribute values, for your tests and your seeders: one model, or a hundred, with
|
|
4
|
+
their related models.
|
|
5
|
+
|
|
6
|
+
## Introduction
|
|
7
|
+
|
|
8
|
+
Each factory is a class in `database/factories` that describes one model's default attributes. A new app has
|
|
9
|
+
`UserFactory`:
|
|
10
|
+
|
|
11
|
+
```ts title="database/factories/UserFactory.ts"
|
|
12
|
+
import { Factory, Hash, fake } from '@orieljs/framework'
|
|
13
|
+
import { User } from '../../app/Models/User'
|
|
14
|
+
|
|
15
|
+
type Attributes = Record<string, unknown>
|
|
16
|
+
|
|
17
|
+
/** The password every factory-made user has, hashed once. */
|
|
18
|
+
let password: string | undefined
|
|
19
|
+
|
|
20
|
+
export default class UserFactory extends Factory<User> {
|
|
21
|
+
model = User
|
|
22
|
+
|
|
23
|
+
definition() {
|
|
24
|
+
return {
|
|
25
|
+
name: fake().name(),
|
|
26
|
+
email: fake().unique().safeEmail(),
|
|
27
|
+
email_verified_at: new Date(),
|
|
28
|
+
password: 'password',
|
|
29
|
+
remember_token: fake().lexify('??????????'),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Indicate that the model's email address should be unverified. */
|
|
34
|
+
unverified() {
|
|
35
|
+
return this.state({ email_verified_at: null })
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Hashes the default password (the model has no hashed cast). */
|
|
39
|
+
protected override async attributesFor(overrides: Attributes, index: number): Promise<Attributes> {
|
|
40
|
+
const attributes = await super.attributesFor(overrides, index)
|
|
41
|
+
if (attributes.password === 'password') attributes.password = password ??= await Hash.make('password')
|
|
42
|
+
return attributes
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`model` names the model, and `definition()` returns the attributes every model gets, unless you give others. `fake()`
|
|
48
|
+
makes the values: names, email addresses, sentences, numbers and dates.
|
|
49
|
+
|
|
50
|
+
Factories set every attribute they are given, whatever the model's `fillable` says.
|
|
51
|
+
|
|
52
|
+
## Defining model factories
|
|
53
|
+
|
|
54
|
+
### Generating factories
|
|
55
|
+
|
|
56
|
+
`make:factory` writes a factory in `database/factories`, named after its model:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
node oriel make:factory PostFactory
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Oriel finds the factories in `database/factories` by itself when your app runs through `oriel`, as a command or under
|
|
63
|
+
`oriel serve`, and in your tests: every factory class a file there exports. An app started another way, such as with
|
|
64
|
+
`public/index.ts`, registers its factories with `Factory.register()`, or `User.factory()` throws "No factory registered
|
|
65
|
+
for [User].".
|
|
66
|
+
|
|
67
|
+
A model's `factory()` returns its factory. To have its own methods, such as `unverified()`, typed, declare it, as a new
|
|
68
|
+
app's `User` does:
|
|
69
|
+
|
|
70
|
+
```ts title="app/Models/User.ts"
|
|
71
|
+
// @filename: database/factories/UserFactory.ts
|
|
72
|
+
import { Factory } from '@orieljs/framework'
|
|
73
|
+
import { User } from '../../app/Models/User'
|
|
74
|
+
export default class UserFactory extends Factory<User> {
|
|
75
|
+
model = User
|
|
76
|
+
definition() {
|
|
77
|
+
return {}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// ---cut---
|
|
81
|
+
import { Model, type HasFactory } from '@orieljs/framework'
|
|
82
|
+
import type UserFactory from '../../database/factories/UserFactory'
|
|
83
|
+
|
|
84
|
+
export class User extends Model {
|
|
85
|
+
declare static factory: HasFactory<UserFactory>
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Factory states
|
|
90
|
+
|
|
91
|
+
A state changes some attributes. Write it as a method that calls `state()`:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
class PostFactory extends Factory<Post> {
|
|
95
|
+
model = Post
|
|
96
|
+
|
|
97
|
+
definition() {
|
|
98
|
+
return { title: fake().sentence(), slug: fake().slug(), body: fake().paragraph() }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Indicate that the post is published. */
|
|
102
|
+
published() {
|
|
103
|
+
return this.state({ published_at: new Date() })
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`state()` also takes a function, which receives the attributes so far and the model's place in the batch:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const users = await User.factory(3)
|
|
112
|
+
.state((attributes, { index }) => ({ name: `User ${index + 1}` }))
|
|
113
|
+
.make()
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
#### "Trashed" state
|
|
117
|
+
|
|
118
|
+
`trashed()` makes soft-deleted models, with a `deleted_at` date:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const post = await Post.factory().trashed().create()
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Factory callbacks
|
|
125
|
+
|
|
126
|
+
`afterMaking()` and `afterCreating()` run a function with each model made or created. Register them in the factory's
|
|
127
|
+
`configure()`:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
class UserFactory extends Factory<User> {
|
|
131
|
+
model = User
|
|
132
|
+
|
|
133
|
+
definition() {
|
|
134
|
+
return { name: fake().name(), email: fake().unique().safeEmail() }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
override configure() {
|
|
138
|
+
return this.afterMaking((user) => {
|
|
139
|
+
// ...
|
|
140
|
+
}).afterCreating((user) => {
|
|
141
|
+
// ...
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Creating models using factories
|
|
148
|
+
|
|
149
|
+
### Instantiating models
|
|
150
|
+
|
|
151
|
+
`make()` makes models without saving them. With `count()`, or a number given to `factory()`, it makes a collection: a
|
|
152
|
+
plain `Collection`, where Laravel's is an Eloquent collection, so `modelKeys()` and `load()` aren't on it:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const user = await User.factory().make()
|
|
156
|
+
|
|
157
|
+
const users = await User.factory().count(3).make()
|
|
158
|
+
|
|
159
|
+
const others = await User.factory(3).make()
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Applying states
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
const users = await User.factory().count(5).unverified().make()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Overriding attributes
|
|
169
|
+
|
|
170
|
+
Give `make()` or `create()` the attributes to set:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const user = await User.factory().make({ name: 'Abigail Otwell' })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Persisting models
|
|
177
|
+
|
|
178
|
+
`create()` makes the models and saves them:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
const user = await User.factory().create()
|
|
182
|
+
|
|
183
|
+
const users = await User.factory().count(3).create()
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`makeOne()` and `createOne()` always return one model, typed as one. `createMany()` takes a number, or a list of
|
|
187
|
+
attributes, one model each. `raw()` returns the attributes without making a model.
|
|
188
|
+
|
|
189
|
+
### Sequences
|
|
190
|
+
|
|
191
|
+
`sequence()` gives the models of a batch attributes in turn, starting again after the last:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
const users = await User.factory().count(10).sequence({ name: 'Taylor' }, { name: 'Abigail' }).create()
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Five users are named Taylor, and five Abigail.
|
|
198
|
+
|
|
199
|
+
## Factory relationships
|
|
200
|
+
|
|
201
|
+
### Has many relationships
|
|
202
|
+
|
|
203
|
+
`has()` creates related models with each model, through a relation of the model, named after the related model in the
|
|
204
|
+
plural unless you name it:
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const user = await User.factory().has(Post.factory().count(3)).create()
|
|
208
|
+
|
|
209
|
+
const writer = await User.factory().has(Post.factory().count(3), 'posts').create()
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Belongs to relationships
|
|
213
|
+
|
|
214
|
+
`for()` sets the parent of the models it makes, a model you have or one its factory creates once for all of them:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const user = await User.factory().createOne()
|
|
218
|
+
|
|
219
|
+
const posts = await Post.factory().count(3).for(user, 'author').create()
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
An attribute of a factory can also be another factory: the factory creates that model, and the attribute takes its
|
|
223
|
+
key, as `author_id: UserFactory.new()` does in a post's definition.
|
|
224
|
+
|
|
225
|
+
### Many to many relationships
|
|
226
|
+
|
|
227
|
+
`has()` works on a many-to-many relation too, and its third argument gives the pivot table's extra columns:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
const user = await User.factory().has(Role.factory().count(3), 'roles', { active: 1 }).create()
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Polymorphic relationships
|
|
234
|
+
|
|
235
|
+
`has()` creates the models of a `morphMany()` relation, and `for()` sets the owner of a `morphTo()` one.
|
|
236
|
+
|
|
237
|
+
> [!NOTE]
|
|
238
|
+
> Laravel's "magic" relationship methods (`hasPosts()`, `forUser()`), `hasAttached()`, `recycle()` and the `Sequence`
|
|
239
|
+
> class aren't in Oriel. Use `has()` with its pivot argument, `for()`, and `sequence()`.
|
|
240
|
+
|
|
241
|
+
## The `fake()` helper
|
|
242
|
+
|
|
243
|
+
`fake()` returns the generator factories use: a seedable subset of Faker's methods. `unique()` gives a value that the
|
|
244
|
+
generator hasn't given before, and `seed()` makes the values repeat from one run to the next:
|
|
245
|
+
|
|
246
|
+
| Kind | Methods |
|
|
247
|
+
| --- | --- |
|
|
248
|
+
| People and companies | `firstName()`, `lastName()`, `name()`, `userName()`, `email()`, `safeEmail()`, `company()`, `phoneNumber()` |
|
|
249
|
+
| Text | `word()`, `words()`, `sentence()`, `sentences()`, `paragraph()`, `paragraphs()`, `text()`, `title()`, `slug()`, `url()`, `city()`, `country()` |
|
|
250
|
+
| Numbers | `numberBetween()`, `randomNumber()`, `randomFloat()`, `randomDigit()`, `random()` (a number from 0 to 1), `boolean()`, `uuid()` |
|
|
251
|
+
| Patterns and dates | `numerify('###')`, `lexify('???')`, `bothify('##??')`, `hexColor()`, `dateTime()`, `dateTimeBetween('-1 year', 'now')`, `date()` |
|
|
252
|
+
|
|
253
|
+
> [!NOTE]
|
|
254
|
+
> Laravel's factories use the full Faker library, with its locales. Oriel's `fake()` has the methods above, in
|
|
255
|
+
> English. To pick from a list, use `fake().numberBetween()` as an index.
|