Skip to content

Commit

Permalink
feat: insert pivot data into many to many relations default key pivot
Browse files Browse the repository at this point in the history
  • Loading branch information
UncertiantyP authored and UncertiantyP committed Jan 23, 2020
1 parent b2d2729 commit d79f42f
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/attributes/relations/BelongsToMany.ts
Expand Up @@ -188,7 +188,7 @@ export default class BelongsToMany extends Relation {
const relatedId = data[this.related.entity][id][this.relatedKey]
const pivotKey = JSON.stringify([relatedId, parentId])
const pivotRecord = data[this.pivot.entity] ? data[this.pivot.entity][pivotKey] : {}
const pivotData = data[this.related.entity][id][this.pivotKey] || {};
const pivotData = data[this.related.entity][id][this.pivotKey] || {}

data[this.pivot.entity] = {
...data[this.pivot.entity],
Expand Down
11 changes: 10 additions & 1 deletion src/attributes/relations/MorphToMany.ts
Expand Up @@ -45,6 +45,11 @@ export default class MorphToMany extends Relation {
*/
relatedKey: string

/**
* The key name of the pivot data.
*/
pivotKey: string

/**
* Create a new belongs to instance.
*/
Expand All @@ -56,7 +61,8 @@ export default class MorphToMany extends Relation {
id: string,
type: string,
parentKey: string,
relatedKey: string
relatedKey: string,
pivotKey: string
) {
super(model) /* istanbul ignore next */

Expand All @@ -67,6 +73,7 @@ export default class MorphToMany extends Relation {
this.type = type
this.parentKey = parentKey
this.relatedKey = relatedKey
this.pivotKey = pivotKey
}

/**
Expand Down Expand Up @@ -179,11 +186,13 @@ export default class MorphToMany extends Relation {
const parentId = record[this.parentKey]
const relatedId = data[this.related.entity][id][this.relatedKey]
const pivotKey = `${parentId}_${id}_${parent.entity}`
const pivotData = data[this.related.entity][id][this.pivotKey] || {}

data[this.pivot.entity] = {
...data[this.pivot.entity],

[pivotKey]: {
...pivotData,
$id: pivotKey,
[this.relatedId]: relatedId,
[this.id]: parentId,
Expand Down
11 changes: 10 additions & 1 deletion src/attributes/relations/MorphedByMany.ts
Expand Up @@ -45,6 +45,11 @@ export default class MorphedByMany extends Relation {
*/
relatedKey: string

/**
* The key name of the pivot data.
*/
pivotKey: string

/**
* Create a new belongs to instance.
*/
Expand All @@ -56,7 +61,8 @@ export default class MorphedByMany extends Relation {
id: string,
type: string,
parentKey: string,
relatedKey: string
relatedKey: string,
pivotKey: string
) {
super(model) /* istanbul ignore next */

Expand All @@ -67,6 +73,7 @@ export default class MorphedByMany extends Relation {
this.type = type
this.parentKey = parentKey
this.relatedKey = relatedKey
this.pivotKey = pivotKey
}

/**
Expand Down Expand Up @@ -174,11 +181,13 @@ export default class MorphedByMany extends Relation {
related.forEach((id) => {
const parentId = record[this.parentKey]
const pivotKey = `${id}_${parentId}_${this.related.entity}`
const pivotData = data[this.related.entity][id][this.pivotKey] || {}

data[this.pivot.entity] = {
...data[this.pivot.entity],

[pivotKey]: {
...pivotData,
$id: pivotKey,
[this.relatedId]: parentId,
[this.id]: this.model.getIdFromRecord(data[this.related.entity][id]),
Expand Down
27 changes: 14 additions & 13 deletions src/model/Model.ts
Expand Up @@ -182,7 +182,7 @@ export default class Model {
relatedPivotKey,
this.localKey(parentKey),
this.relation(related).localKey(relatedKey),
this.pivotKey(related, pivotKey)
this.pivotKey(pivotKey)
)
}

Expand Down Expand Up @@ -217,7 +217,8 @@ export default class Model {
id: string,
type: string,
parentKey?: string,
relatedKey?: string
relatedKey?: string,
pivotKey?: string
): Attributes.MorphToMany {
return new Attributes.MorphToMany(
this,
Expand All @@ -227,7 +228,8 @@ export default class Model {
id,
type,
this.localKey(parentKey),
this.relation(related).localKey(relatedKey)
this.relation(related).localKey(relatedKey),
this.pivotKey(pivotKey)
)
}

Expand All @@ -241,7 +243,8 @@ export default class Model {
id: string,
type: string,
parentKey?: string,
relatedKey?: string
relatedKey?: string,
pivotKey?: string
): Attributes.MorphedByMany {
return new Attributes.MorphedByMany(
this,
Expand All @@ -251,7 +254,8 @@ export default class Model {
id,
type,
this.localKey(parentKey),
this.relation(related).localKey(relatedKey)
this.relation(related).localKey(relatedKey),
this.pivotKey(pivotKey)
)
}

Expand Down Expand Up @@ -508,17 +512,14 @@ export default class Model {
}

/**
* Get the pivot key with related.
* Get pivot key.
*/
static pivotKey (related: typeof Model | string, pivotKey?: string): string {
if (pivotKey) {
return pivotKey
static pivotKey (key?: string): string {
if (key) {
return key
}

return [
(typeof related === "string" ? related : related.entity),
this.entity
].sort().join('_')
return 'pivot'
}

/**
Expand Down
71 changes: 68 additions & 3 deletions test/feature/relations/BelongsToMany_Persist.spec.js
Expand Up @@ -43,7 +43,7 @@ describe('Feature – Relations – Belongs To Many – Persist', () => {
await store.dispatch('entities/users/create', {
data: {
id: 1,
permissions: [{ id: 1, roles_users: { level: 1 } }, { id: 2 }]
permissions: [{ id: 1, pivot: { level: 1 } }, { id: 2 }]
}
})

Expand Down Expand Up @@ -157,7 +157,8 @@ describe('Feature – Relations – Belongs To Many – Persist', () => {
static fields () {
return {
role_id: this.attr(null),
user_id: this.attr(null)
user_id: this.attr(null),
level: this.attr(null)
}
}
}
Expand All @@ -171,7 +172,7 @@ describe('Feature – Relations – Belongs To Many – Persist', () => {
team_id: 1,
id: 1,
roles: [
{ id: 2 },
{ id: 2, pivot: { level: 1 } },
{ id: 3 }
]
}]
Expand All @@ -183,7 +184,9 @@ describe('Feature – Relations – Belongs To Many – Persist', () => {
expect(store.state.entities.roles.data['2'].id).toBe(2)
expect(store.state.entities.roles.data['3'].id).toBe(3)
expect(store.state.entities.roleUser.data['[2,1]'].$id).toStrictEqual('[2,1]')
expect(store.state.entities.roleUser.data['[2,1]'].level).toBe(1)
expect(store.state.entities.roleUser.data['[3,1]'].$id).toStrictEqual('[3,1]')
expect(store.state.entities.roleUser.data['[3,1]'].level).toBe(null)
})

it('can retrieve data by filtering with `whereHas`', async () => {
Expand Down Expand Up @@ -420,4 +423,66 @@ describe('Feature – Relations – Belongs To Many – Persist', () => {

expect(store.state.entities).toEqual(expected)
})

it('can create a data with belongs to many relation with pivot data having custom key', async () => {
class User extends Model {
static entity = 'users'

static fields () {
return {
id: this.attr(null),
permissions: this.belongsToMany(Role, RoleUser, 'user_id', 'role_id', 'id', 'id', 'role_user')
}
}
}

class Role extends Model {
static entity = 'roles'

static fields () {
return {
id: this.attr(null)
}
}
}

class RoleUser extends Model {
static entity = 'roleUser'

static primaryKey = ['role_id', 'user_id']

static fields () {
return {
role_id: this.attr(null),
user_id: this.attr(null),
level: this.attr(null)
}
}
}

const store = createStore([{ model: User }, { model: Role }, { model: RoleUser }])

await store.dispatch('entities/users/create', {
data: {
id: 1,
permissions: [{ id: 1, role_user: { level: 1 } }, { id: 2 }]
}
})

const expected = createState({
users: {
1: { $id: '1', id: 1, permissions: [] }
},
roles: {
1: { $id: '1', id: 1 },
2: { $id: '2', id: 2 }
},
roleUser: {
'[1,1]': { $id: '[1,1]', role_id: 1, user_id: 1, level: 1 },
'[2,1]': { $id: '[2,1]', role_id: 2, user_id: 1, level: null }
}
})

expect(store.state.entities).toEqual(expected)
})
})
70 changes: 68 additions & 2 deletions test/feature/relations/MorphToMany_Persist.spec.js
Expand Up @@ -43,7 +43,8 @@ describe('Features – Relations – Morph To Many – Persist', () => {
return {
tag_id: this.attr(null),
taggable_id: this.attr(null),
taggable_type: this.attr(null)
taggable_type: this.attr(null),
public: this.attr(null)
}
}
}
Expand All @@ -54,7 +55,7 @@ describe('Features – Relations – Morph To Many – Persist', () => {
data: {
id: 1,
tags: [
{ id: 2, name: 'news' },
{ id: 2, name: 'news', pivot: { public: true } },
{ id: 3, name: 'cast' }
]
}
Expand All @@ -65,8 +66,10 @@ describe('Features – Relations – Morph To Many – Persist', () => {
expect(store.state.entities.tags.data['3'].id).toBe(3)
expect(store.state.entities.taggables.data['1_2_posts'].taggable_id).toBe(1)
expect(store.state.entities.taggables.data['1_2_posts'].taggable_type).toBe('posts')
expect(store.state.entities.taggables.data['1_2_posts'].public).toBe(true)
expect(store.state.entities.taggables.data['1_3_posts'].taggable_id).toBe(1)
expect(store.state.entities.taggables.data['1_3_posts'].taggable_type).toBe('posts')
expect(store.state.entities.taggables.data['1_3_posts'].public).toBe(null)
})

it('can create a morph to many data without relation field', async () => {
Expand Down Expand Up @@ -283,6 +286,69 @@ describe('Features – Relations – Morph To Many – Persist', () => {
expect(store.state.entities).toEqual(expected)
})

it('can create a morph to many relation data with pivot data having custom key', async () => {
class Post extends Model {
static entity = 'posts'

static fields () {
return {
id: this.attr(null),
tags: this.morphToMany(Tag, Taggable, 'tag_id', 'taggable_id', 'taggable_type', 'id', 'id', 'tag_pivot')
}
}
}

class Video extends Model {
static entity = 'videos'

static fields () {
return {
id: this.attr(null),
tags: this.morphToMany(Tag, Taggable, 'tag_id', 'taggable_id', 'taggable_type')
}
}
}

class Tag extends Model {
static entity = 'tags'

static fields () {
return {
id: this.attr(null),
name: this.attr('')
}
}
}

class Taggable extends Model {
static entity = 'taggables'

static fields () {
return {
tag_id: this.attr(null),
taggable_id: this.attr(null),
taggable_type: this.attr(null),
public: this.attr(null)
}
}
}

const store = createStore([{ model: Post }, { model: Video }, { model: Tag }, { model: Taggable }])

await Post.create({
data: {
id: 1,
tags: [
{ id: 2, name: 'news', tag_pivot: { public: true } },
{ id: 3, name: 'cast' }
]
}
})

expect(store.state.entities.taggables.data['1_2_posts'].public).toBe(true)
expect(store.state.entities.taggables.data['1_3_posts'].public).toBe(null)
})

it('can resolve a morph to many relation', async () => {
class Post extends Model {
static entity = 'posts'
Expand Down

0 comments on commit d79f42f

Please sign in to comment.