Skip to content

Commit 0caced0

Browse files
kiakingryandialpadcuebit
authored
feat: add MorphTo relation (#106)
Co-authored-by: ryandialpad <[email protected]> Co-authored-by: Cue <[email protected]>
1 parent 16603db commit 0caced0

File tree

18 files changed

+845
-53
lines changed

18 files changed

+845
-53
lines changed

src/database/Database.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Store } from 'vuex'
22
import { schema as Normalizr } from 'normalizr'
3-
import { Schema } from '../schema/Schema'
3+
import { Schema, Schemas } from '../schema/Schema'
44
import { Model } from '../model/Model'
55
import { Relation } from '../model/attributes/relations/Relation'
66
import { State } from '../modules/State'
@@ -26,7 +26,7 @@ export class Database {
2626
/**
2727
* The schema definition for the registered models.
2828
*/
29-
schemas: Record<string, Normalizr.Entity> = {}
29+
schemas: Schemas = {}
3030

3131
/**
3232
* Whether the database has already been installed to Vuex or not.

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export * from './model/decorators/attributes/relations/BelongsTo'
1616
export * from './model/decorators/attributes/relations/HasMany'
1717
export * from './model/decorators/attributes/relations/HasManyBy'
1818
export * from './model/decorators/attributes/relations/MorphOne'
19+
export * from './model/decorators/attributes/relations/MorphTo'
1920
export * from './model/decorators/Contracts'
2021
export * from './model/decorators/NonEnumerable'
2122
export * from './model/attributes/Attribute'
@@ -31,6 +32,7 @@ export { BelongsTo as BelongsToAttr } from './model/attributes/relations/Belongs
3132
export { HasMany as HasManyAttr } from './model/attributes/relations/HasMany'
3233
export { HasManyBy as HasManyByAttr } from './model/attributes/relations/HasManyBy'
3334
export { MorphOne as MorphOneAttr } from './model/attributes/relations/MorphOne'
35+
export { MorphTo as MorphToAttr } from './model/attributes/relations/MorphTo'
3436
export * from './modules/RootModule'
3537
export * from './modules/RootState'
3638
export * from './modules/Module'
@@ -63,6 +65,7 @@ import { BelongsTo as BelongsToAttr } from './model/attributes/relations/Belongs
6365
import { HasMany as HasManyAttr } from './model/attributes/relations/HasMany'
6466
import { HasManyBy as HasManyByAttr } from './model/attributes/relations/HasManyBy'
6567
import { MorphOne as MorphOneAttr } from './model/attributes/relations/MorphOne'
68+
import { MorphTo as MorphToAttr } from './model/attributes/relations/MorphTo'
6669
import { Repository } from './repository/Repository'
6770
import { Interpreter } from './interpreter/Interpreter'
6871
import { Query } from './query/Query'
@@ -88,6 +91,7 @@ export default {
8891
HasManyAttr,
8992
HasManyByAttr,
9093
MorphOneAttr,
94+
MorphToAttr,
9195
Repository,
9296
Interpreter,
9397
Query,

src/model/Model.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { BelongsTo } from './attributes/relations/BelongsTo'
1212
import { HasMany } from './attributes/relations/HasMany'
1313
import { HasManyBy } from './attributes/relations/HasManyBy'
1414
import { MorphOne } from './attributes/relations/MorphOne'
15+
import { MorphTo } from './attributes/relations/MorphTo'
1516

1617
export type ModelFields = Record<string, Attribute>
1718
export type ModelSchemas = Record<string, ModelFields>
@@ -247,6 +248,21 @@ export class Model {
247248
return new MorphOne(model, related.newRawInstance(), id, type, localKey)
248249
}
249250

251+
/**
252+
* Create a new MorphTo relation instance.
253+
*/
254+
static morphTo(
255+
related: typeof Model[],
256+
id: string,
257+
type: string,
258+
ownerKey: string = ''
259+
): MorphTo {
260+
const instance = this.newRawInstance()
261+
const relatedModels = related.map((model) => model.newRawInstance())
262+
263+
return new MorphTo(instance, relatedModels, id, type, ownerKey)
264+
}
265+
250266
/**
251267
* Get the constructor for this model.
252268
*/
@@ -332,16 +348,17 @@ export class Model {
332348
*/
333349
protected $fillField(key: string, attr: Attribute, value: any): void {
334350
if (value !== undefined) {
335-
this[key] = attr.make(value)
336-
return
337-
}
351+
this[key] =
352+
attr instanceof MorphTo
353+
? attr.make(value, this[attr.getType()])
354+
: attr.make(value)
338355

339-
if (this[key] !== undefined) {
340-
this[key] = this[key]
341356
return
342357
}
343358

344-
this[key] = attr.make()
359+
if (this[key] === undefined) {
360+
this[key] = attr.make()
361+
}
345362
}
346363

347364
/**

src/model/attributes/relations/BelongsTo.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,29 @@ export class BelongsTo extends Relation {
8484
/**
8585
* Match the eagerly loaded results to their respective parents.
8686
*/
87-
match(relation: string, models: Collection, results: Collection): void {
88-
const dictionary = results.reduce<Record<string, Model>>((dic, result) => {
89-
dic[result[this.ownerKey]] = result
90-
91-
return dic
92-
}, {})
87+
match(relation: string, models: Collection, query: Query): void {
88+
const dictionary = this.buildDictionary(query.get())
9389

9490
models.forEach((model) => {
95-
dictionary[model[this.foreignKey]]
96-
? model.$setRelation(relation, dictionary[model[this.foreignKey]])
91+
const key = model[this.foreignKey]
92+
93+
dictionary[key]
94+
? model.$setRelation(relation, dictionary[key])
9795
: model.$setRelation(relation, null)
9896
})
9997
}
10098

99+
/**
100+
* Build model dictionary keyed by relation's parent key.
101+
*/
102+
protected buildDictionary(models: Collection): Record<string, Model> {
103+
return models.reduce<Record<string, Model>>((dictionary, model) => {
104+
dictionary[model[this.ownerKey]] = model
105+
106+
return dictionary
107+
}, {})
108+
}
109+
101110
/**
102111
* Make a related model.
103112
*/

src/model/attributes/relations/HasMany.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Schema } from '../../../schema/Schema'
33
import { Element, Collection } from '../../../data/Data'
44
import { Query } from '../../../query/Query'
55
import { Model } from '../../Model'
6-
import { Relation, Dictionary } from './Relation'
6+
import { Dictionary, Relation } from './Relation'
77

88
export class HasMany extends Relation {
99
/**
@@ -61,8 +61,8 @@ export class HasMany extends Relation {
6161
/**
6262
* Match the eagerly loaded results to their parents.
6363
*/
64-
match(relation: string, models: Collection, results: Collection): void {
65-
const dictionary = this.buildDictionary(results)
64+
match(relation: string, models: Collection, query: Query): void {
65+
const dictionary = this.buildDictionary(query.get())
6666

6767
models.forEach((model) => {
6868
const key = model[this.localKey]

src/model/attributes/relations/HasManyBy.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,8 @@ export class HasManyBy extends Relation {
105105
/**
106106
* Match the eagerly loaded results to their parents.
107107
*/
108-
match(relation: string, models: Collection, results: Collection): void {
109-
const dictionary = results.reduce<Record<string, Model>>((dic, result) => {
110-
dic[result[this.ownerKey]] = result
111-
112-
return dic
113-
}, {})
108+
match(relation: string, models: Collection, query: Query): void {
109+
const dictionary = this.buildDictionary(query.get())
114110

115111
models.forEach((model) => {
116112
const relatedModels = this.getRelatedModels(
@@ -122,6 +118,17 @@ export class HasManyBy extends Relation {
122118
})
123119
}
124120

121+
/**
122+
* Build model dictionary keyed by the relation's foreign key.
123+
*/
124+
protected buildDictionary(models: Collection): Record<string, Model> {
125+
return models.reduce<Record<string, Model>>((dictionary, model) => {
126+
dictionary[model[this.ownerKey]] = model
127+
128+
return dictionary
129+
}, {})
130+
}
131+
125132
/**
126133
* Get all related models from the given dictionary.
127134
*/

src/model/attributes/relations/HasOne.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ export class HasOne extends Relation {
6161
/**
6262
* Match the eagerly loaded results to their parents.
6363
*/
64-
match(relation: string, models: Collection, results: Collection): void {
65-
const dictionary = this.buildDictionary(results)
64+
match(relation: string, models: Collection, query: Query): void {
65+
const dictionary = this.buildDictionary(query.get())
6666

6767
models.forEach((model) => {
6868
const key = model[this.localKey]

src/model/attributes/relations/MorphOne.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Schema } from '../../../schema/Schema'
33
import { Element, Collection } from '../../../data/Data'
44
import { Query } from '../../../query/Query'
55
import { Model } from '../../Model'
6-
import { Relation, Dictionary } from './Relation'
6+
import { Relation } from './Relation'
77

88
export class MorphOne extends Relation {
99
/**
@@ -63,32 +63,35 @@ export class MorphOne extends Relation {
6363
* Set the constraints for an eager load of the relation.
6464
*/
6565
addEagerConstraints(query: Query, models: Collection): void {
66-
query.where(this.morphType, this.parent.$entity())
67-
query.whereIn(this.morphId, this.getKeys(models, this.localKey))
66+
query
67+
.where(this.morphType, this.parent.$entity())
68+
.whereIn(this.morphId, this.getKeys(models, this.localKey))
6869
}
6970

7071
/**
7172
* Match the eagerly loaded results to their parents.
7273
*/
73-
match(relation: string, models: Collection, results: Collection): void {
74-
const dictionary = this.buildDictionary(results)
74+
match(relation: string, models: Collection, query: Query): void {
75+
const dictionary = this.buildDictionary(query.get())
7576

7677
models.forEach((model) => {
7778
const key = model[this.localKey]
7879

7980
dictionary[key]
80-
? model.$setRelation(relation, dictionary[key][0])
81+
? model.$setRelation(relation, dictionary[key])
8182
: model.$setRelation(relation, null)
8283
})
8384
}
8485

8586
/**
8687
* Build model dictionary keyed by the relation's foreign key.
8788
*/
88-
protected buildDictionary(results: Collection): Dictionary {
89-
return this.mapToDictionary(results, (result) => {
90-
return [result[this.morphId], result]
91-
})
89+
protected buildDictionary(models: Collection): Record<string, Model> {
90+
return models.reduce<Record<string, Model>>((dictionary, model) => {
91+
dictionary[model[this.morphId]] = model
92+
93+
return dictionary
94+
}, {})
9295
}
9396

9497
/**

0 commit comments

Comments
 (0)