Skip to content

Commit 6bfe237

Browse files
authored
feat: add lazy relationship loading feature (#44)
1 parent fd8314f commit 6bfe237

File tree

5 files changed

+121
-2
lines changed

5 files changed

+121
-2
lines changed

docs/guide/relationships/getting-started.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,59 @@ const users = store.$repo(User).with('posts', (query) => {
120120
}).get()
121121
```
122122

123+
### Lazy Relatinship Loading
124+
125+
Sometimes you may need to load a relationship after the model has already been retrieved. For example, this may be useful if you need to dynamically decide whether to load related models. You may use `load` method on a repository to load relationships on the fly in such a case.
126+
127+
```js
128+
const userRepo = store.$repo(User)
129+
130+
// Retrieve models without relationships.
131+
const users = userRepo.get()
132+
133+
// Load relationships on the fly.
134+
userRepo.with('posts').load(users)
135+
```
136+
137+
You may set any additional query constraints as usual to the `with` method.
138+
139+
```js
140+
userRepo.with('posts', (query) => {
141+
query.orderBy('createdAt', 'desc')
142+
}).load(users)
143+
```
144+
145+
Note that the `load` method will mutate the given models. It would be safer to use the method within a single `computed` method.
146+
147+
```js
148+
import { mapRepos } from '@vuex-orm/core'
149+
import User from '@/models/User'
150+
151+
export default {
152+
data () {
153+
return {
154+
condition: false
155+
}
156+
},
157+
158+
computed: {
159+
...mapRepos({
160+
userRepo: User
161+
}),
162+
163+
users () {
164+
const users = this.userRepo.get()
165+
166+
if (this.condition) {
167+
this.userRepo.with('posts').load(users)
168+
}
169+
170+
return users
171+
}
172+
}
173+
}
174+
```
175+
123176
## Inserting Relationships
124177

125178
When inserting new records into the store, Vuex ORM automatically normalizes and stores data that contains any nested relationships in it's data structure. For example, let's say you have the `User` model that has a relationship to the `Post` model:

docs/guide/repository/getting-started.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ export default {
5151

5252
### Map Repositories Helper
5353

54-
In addition to use `this.$store.$repo` to retrieve repositories, Vuex ORM provides a convenience helper function called `mapRepositories` to retrieve multiple repositories at once.
54+
In addition to use `this.$store.$repo` to retrieve repositories, Vuex ORM provides a convenience helper function called `mapRepos` to retrieve multiple repositories at once.
5555

5656
The `mapRepos` helper will receive a list of models with its name as a key. The repositories is going to be injected to the `this` context within Vue Component.
5757

5858
```js
59-
import { mapRepositories } from '@vuex-orm/core'
59+
import { mapRepos } from '@vuex-orm/core'
6060
import User from '@/models/User'
6161
import Post from '@/models/Post'
6262

src/model/Model.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,10 @@ export class Model {
584584
protected serializeRelation(relation: Item): Element | null
585585
protected serializeRelation(relation: Collection): Element[]
586586
protected serializeRelation(relation: any): any {
587+
if (relation === undefined) {
588+
return undefined
589+
}
590+
587591
if (relation === null) {
588592
return null
589593
}

src/query/Query.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,13 @@ export class Query<M extends Model = Model> {
328328
: models.slice(this.skip)
329329
}
330330

331+
/**
332+
* Eager load relations on the model.
333+
*/
334+
load(models: Collection<M>): void {
335+
this.eagerLoadRelations(models)
336+
}
337+
331338
/**
332339
* Eager load the relationships for the models.
333340
*/
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { createStore, fillState, assertModels } from 'test/Helpers'
2+
import { Model, Attr, Str, HasMany } from '@/index'
3+
4+
describe('feature/relations/lazy_loads/lazy_eager_load', () => {
5+
class User extends Model {
6+
static entity = 'users'
7+
8+
@Attr() id!: number
9+
@Str('') name!: string
10+
11+
@HasMany(() => Post, 'userId')
12+
posts!: Post[]
13+
}
14+
15+
class Post extends Model {
16+
static entity = 'posts'
17+
18+
@Attr() id!: number
19+
@Attr() userId!: number
20+
@Str('') title!: string
21+
}
22+
23+
it('can lazy eager load relations', async () => {
24+
const store = createStore()
25+
26+
fillState(store, {
27+
users: {
28+
1: { id: 1, name: 'John Doe' }
29+
},
30+
posts: {
31+
1: { id: 1, userId: 1, title: 'Title 01' },
32+
2: { id: 2, userId: 1, title: 'Title 02' }
33+
}
34+
})
35+
36+
const userRepo = store.$repo(User)
37+
38+
const users = userRepo.all()
39+
40+
assertModels(users, [{ id: 1, name: 'John Doe' }])
41+
42+
userRepo.with('posts').load(users)
43+
44+
assertModels(users, [
45+
{
46+
id: 1,
47+
name: 'John Doe',
48+
posts: [
49+
{ id: 1, userId: 1, title: 'Title 01' },
50+
{ id: 2, userId: 1, title: 'Title 02' }
51+
]
52+
}
53+
])
54+
})
55+
})

0 commit comments

Comments
 (0)