diff --git a/adonis-typings/relations.ts b/adonis-typings/relations.ts index 701f7eb6..4fe2338b 100644 --- a/adonis-typings/relations.ts +++ b/adonis-typings/relations.ts @@ -56,6 +56,12 @@ declare module '@ioc:Adonis/Lucid/Orm' { foreignKey?: string serializeAs?: string | null onQuery?(query: Related['builder'] | Related['subQuery']): void + serialize?: ( + value: Related['instance'] | null, + attribute: string, + model: LucidRow, + cherryPickRelations?: CherryPick + ) => any } /** @@ -260,6 +266,14 @@ declare module '@ioc:Adonis/Lucid/Orm' { readonly serializeAs: string | null readonly booted: boolean readonly model: ParentModel + + readonly serialize?: ( + value: LucidRow, + attribute: string, + model: LucidRow, + cherryPickRelations?: CherryPick + ) => any + relatedModel(): RelatedModel boot(): void clone(parent: LucidModel): this diff --git a/src/Orm/BaseModel/index.ts b/src/Orm/BaseModel/index.ts index 234159e4..8e46bfe3 100644 --- a/src/Orm/BaseModel/index.ts +++ b/src/Orm/BaseModel/index.ts @@ -1886,6 +1886,19 @@ export class BaseModel implements LucidRow { * to the relationships */ const relationOptions = cherryPick ? cherryPick[relation.serializeAs] : undefined + + if (typeof relation.serialize === 'function') { + if (Array.isArray(value)) { + result[relation.serializeAs] = value.map((one) => + relation.serialize?.(one, key, this, relationOptions) + ) + return result + } + + result[relation.serializeAs] = relation.serialize(value, key, this, relationOptions) + return result + } + result[relation.serializeAs] = Array.isArray(value) ? value.map((one) => one.serialize(relationOptions)) : value === null diff --git a/src/Orm/Relations/HasMany/index.ts b/src/Orm/Relations/HasMany/index.ts index 44552d49..1abf7a0d 100644 --- a/src/Orm/Relations/HasMany/index.ts +++ b/src/Orm/Relations/HasMany/index.ts @@ -43,6 +43,8 @@ export class HasMany implements HasManyRelationContract public serializeAs = this.options.serializeAs === undefined ? this.relationName : this.options.serializeAs + public serialize = this.options.serialize + /** * Local key is reference to the primary key in the self table * @note: Available after boot is invoked diff --git a/src/Orm/Relations/HasOne/index.ts b/src/Orm/Relations/HasOne/index.ts index 17e10b97..8dbe989d 100644 --- a/src/Orm/Relations/HasOne/index.ts +++ b/src/Orm/Relations/HasOne/index.ts @@ -32,6 +32,8 @@ export class HasOne implements HasOneRelationContract { public serializeAs = this.options.serializeAs === undefined ? this.relationName : this.options.serializeAs + public serialize = this.options.serialize + /** * Local key is reference to the primary key in the self table * @note: Available after boot is invoked diff --git a/test/orm/model-has-many.spec.ts b/test/orm/model-has-many.spec.ts index 314da789..75dc3674 100644 --- a/test/orm/model-has-many.spec.ts +++ b/test/orm/model-has-many.spec.ts @@ -5424,4 +5424,71 @@ test.group('Model | HasMany | delete', (group) => { assert.deepEqual(bindings, rawBindings) assert.deepEqual(sql, rawSql) }) + + test('add hasMany with serialize', async ({ assert }) => { + class Profile extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public userId: number + + @column() + public displayName: string + } + + class User extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public username: string + + @hasMany(() => Profile, { + serialize: (value) => { + if (value === null) { + return { type: 'profile', displayable: '---' } + } + + return { + type: 'profile', + displayable: `${value.userId} - ${value.displayName}`, + } + }, + }) + public profile: HasMany + } + + const user = new User() + user.username = 'virk' + await user.save() + + await user.load('profile') + + assert.deepEqual(user.serialize(), { + username: 'virk', + id: 1, + profile: [], + }) + + const firstProfile = new Profile() + firstProfile.displayName = 'Hvirk 1' + + const secondProfile = new Profile() + secondProfile.displayName = 'Hvirk 2' + + await user.related('profile').save(firstProfile) + await user.related('profile').save(secondProfile) + + await user.load('profile') + + assert.deepEqual(user.serialize(), { + username: 'virk', + id: 1, + profile: [ + { type: 'profile', displayable: '1 - Hvirk 1' }, + { type: 'profile', displayable: '1 - Hvirk 2' }, + ], + }) + }) }) diff --git a/test/orm/model-has-one.spec.ts b/test/orm/model-has-one.spec.ts index 4e4c639e..d9c8d37a 100644 --- a/test/orm/model-has-one.spec.ts +++ b/test/orm/model-has-one.spec.ts @@ -2864,4 +2864,63 @@ test.group('Model | HasOne | delete', (group) => { assert.deepEqual(bindings, rawBindings) assert.deepEqual(sql, rawSql) }) + + test('add hasOne with serialize', async ({ assert }) => { + class Profile extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public userId: number + + @column() + public displayName: string + } + + class User extends BaseModel { + @column({ isPrimary: true }) + public id: number + + @column() + public username: string + + @hasOne(() => Profile, { + serialize: (value) => { + if (value === null) { + return { type: 'profile', displayable: '---' } + } + + return { + type: 'profile', + displayable: `${value.userId} - ${value.displayName}`, + } + }, + }) + public profile: HasOne + } + + const user = new User() + user.username = 'virk' + await user.save() + + await user.load('profile') + + assert.deepEqual(user.serialize(), { + username: 'virk', + id: 1, + profile: { type: 'profile', displayable: '---' }, + }) + + const profile = new Profile() + profile.displayName = 'Hvirk' + + await user.related('profile').save(profile) + await user.load('profile') + + assert.deepEqual(user.serialize(), { + username: 'virk', + id: 1, + profile: { type: 'profile', displayable: '1 - Hvirk' }, + }) + }) })