Skip to content

Commit 4fe84a5

Browse files
author
Daan van der Kallen
committed
Add linkRelations option
1 parent cec7709 commit 4fe84a5

2 files changed

Lines changed: 44 additions & 7 deletions

File tree

src/Model.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export default class Model {
7373
__activeCurrentRelations = [];
7474
__repository;
7575
__store;
76+
__linkRelations;
7677
api = null;
7778
// A `cid` can be used to identify the model locally.
7879
cid = `m${uniqueId()}`;
@@ -166,6 +167,11 @@ export default class Model {
166167
constructor(data, options = {}) {
167168
this.__store = options.store;
168169
this.__repository = options.repository;
170+
this.__linkRelations = options.linkRelations || 'tree';
171+
invariant(
172+
['tree', 'graph'].includes(this.__linkRelations),
173+
`Unknown relation linking method: ${this.__linkRelations}`,
174+
);
169175
// Find all attributes. Not all observables are an attribute.
170176
forIn(this, (value, key) => {
171177
if (!key.startsWith('__') && isObservableProp(this, key)) {
@@ -236,7 +242,10 @@ export default class Model {
236242
RelModel,
237243
`Specified relation "${relName}" does not exist on model.`
238244
);
239-
const options = { relations: otherRelNames };
245+
const options = {
246+
relations: otherRelNames,
247+
linkRelations: this.__linkRelations,
248+
};
240249
if (RelModel.prototype instanceof Store) {
241250
return new RelModel(options);
242251
}
@@ -528,7 +537,7 @@ export default class Model {
528537
// e.g. "animal_kind", while the relation name would be "kind".
529538
// `relMapping` maps relation names to repositories.
530539
@action
531-
fromBackend({ data, repos, relMapping, reverseRelMapping, }) {
540+
fromBackend({ data, repos, relMapping, reverseRelMapping, relCache = {}, relPath = 'root' }) {
532541
// We handle the fromBackend recursively. On each relation of the source model
533542
// fromBackend gets called as well, but with data scoped for itself
534543
//
@@ -555,6 +564,8 @@ export default class Model {
555564
repos: scopedRepos,
556565
relMapping: scopedRelMapping,
557566
reverseRelMapping: scopedReverseRelMapping,
567+
relCache,
568+
relPath: `${relPath}.${relName}`,
558569
});
559570
});
560571

src/Store.js

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const AVAILABLE_CONST_OPTIONS = [
1818
'comparator',
1919
'params',
2020
'repository',
21+
'linkRelations',
2122
];
2223

2324
export default class Store {
@@ -38,6 +39,7 @@ export default class Store {
3839
Model = null;
3940
api = null;
4041
__repository;
42+
__linkRelations;
4143
static backendResourceName = '';
4244

4345
url() {
@@ -81,6 +83,11 @@ export default class Store {
8183
);
8284
});
8385
this.__repository = options.repository;
86+
this.__linkRelations = options.linkRelations || 'tree';
87+
invariant(
88+
['tree', 'graph'].includes(this.__linkRelations),
89+
`Unknown relation linking method: ${this.__linkRelations}`,
90+
);
8491
if (options.relations) {
8592
this.__parseRelations(options.relations);
8693
}
@@ -113,33 +120,52 @@ export default class Store {
113120
}
114121

115122
@action
116-
fromBackend({ data, repos, relMapping, reverseRelMapping }) {
123+
fromBackend({ data, repos, relMapping, reverseRelMapping, relCache = {}, relPath = 'root' }) {
117124
invariant(
118125
data,
119126
'Backend error. Data is not set. HINT: DID YOU FORGET THE M2M again?'
120127
);
121128

122129
this.models.replace(
123130
data.map(record => {
131+
const relCacheKey = `${relPath}:${record.id}`
132+
133+
if (this.__linkRelations === 'graph') {
134+
const model = relCache[relCacheKey];
135+
if (model !== undefined) {
136+
return model;
137+
}
138+
}
139+
124140
// TODO: I'm not happy at all about how this looks.
125141
// We'll need to finetune some things, but hey, for now it works.
126-
const model = this._newModel();
142+
143+
const model = this._newModel(null);
127144
model.fromBackend({
128145
data: record,
129146
repos,
130147
relMapping,
131148
reverseRelMapping,
149+
relCache,
150+
relPath,
132151
});
152+
153+
if (this.__linkRelations === 'graph') {
154+
relCache[relCacheKey] = model;
155+
}
156+
133157
return model;
134158
})
135159
);
136160
this.sort();
137161
}
138162

139-
_newModel(model = null) {
163+
_newModel(model = null, options = {}) {
140164
return new this.Model(model, {
165+
...options,
141166
store: this,
142167
relations: this.__activeRelations,
168+
linkRelations: this.__linkRelations,
143169
});
144170
}
145171

@@ -170,7 +196,7 @@ export default class Store {
170196
);
171197
// Parse does not mutate __setChanged, as it is used in
172198
// fromBackend in the model...
173-
this.models.replace(models.map(this._newModel.bind(this)));
199+
this.models.replace(models.map((data) => this._newModel(data)));
174200
this.sort();
175201

176202
return this;
@@ -193,7 +219,7 @@ export default class Store {
193219
const singular = !isArray(models);
194220
models = singular ? [models] : models.slice();
195221

196-
const modelInstances = models.map(this._newModel.bind(this));
222+
const modelInstances = models.map((data) => this._newModel(data));
197223

198224
modelInstances.forEach(modelInstance => {
199225
const primaryValue = modelInstance[this.Model.primaryKey];

0 commit comments

Comments
 (0)