diff --git a/packages/pinia-orm/src/model/Model.ts b/packages/pinia-orm/src/model/Model.ts index 181912cd4..b3112cf37 100644 --- a/packages/pinia-orm/src/model/Model.ts +++ b/packages/pinia-orm/src/model/Model.ts @@ -787,7 +787,9 @@ export class Model { if (mutator && typeof mutator !== 'function' && operation === 'set' && mutator.set) { keyValue = mutator.set(keyValue) } - if (cast && operation === 'set') { keyValue = cast.set(keyValue) } + if (cast && operation === 'set') { + keyValue = options.action === 'update' ? cast.get(keyValue) : cast.set(keyValue) + } this[key as keyof this] = this[key as keyof this] ?? keyValue } @@ -1064,12 +1066,16 @@ export class Model { /** * Serialize the given object to JSON. */ - protected serializeObject (value: { - [index: string]: number | string - }): object { + protected serializeObject (value: any): object { const obj: { [index: string]: number | string } = {} - for (const key in value) { obj[key] = this.serializeValue(value[key]) } + if (value.serialize && typeof value.serialize === 'function') { + return value.serialize(value) + } + + for (const key in value) { + obj[key] = this.serializeValue(value[key]) + } return obj } diff --git a/packages/pinia-orm/src/query/Query.ts b/packages/pinia-orm/src/query/Query.ts index 08f8e8e2a..2069c413a 100644 --- a/packages/pinia-orm/src/query/Query.ts +++ b/packages/pinia-orm/src/query/Query.ts @@ -836,15 +836,14 @@ export class Query { for (const id in elements) { const record = elements[id] const existing = currentData[id] - let model = existing + let model: M = existing ? Object.assign(this.hydrate(existing, { operation: 'set', action: 'update' }), record) : this.hydrate(record, { operation: 'set', action: 'save' }) - const isSaving = model.$self().saving(model, record) const isUpdatingOrCreating = existing ? model.$self().updating(model, record) : model.$self().creating(model, record) if (isSaving === false || isUpdatingOrCreating === false) { continue } - if (model.$isDirty()) { model = this.hydrate(model.$getAttributes(), { operation: 'set', action: existing ? 'update' : 'save' }) } + if (model.$isDirty()) { model = this.hydrate(model.$getAttributes(), { operation: 'set', action: 'update' }) } afterSavingHooks.push(() => model.$self().saved(model, record)) afterSavingHooks.push(() => existing ? model.$self().updated(model, record) : model.$self().created(model, record)) diff --git a/packages/pinia-orm/tests/feature/casts/custom_casts.spec.ts b/packages/pinia-orm/tests/feature/casts/custom_casts.spec.ts new file mode 100644 index 000000000..e99480338 --- /dev/null +++ b/packages/pinia-orm/tests/feature/casts/custom_casts.spec.ts @@ -0,0 +1,98 @@ +import { describe, expect, it } from 'vitest' + +import { CastAttribute, Model, useRepo } from '../../../src' +import { Attr, Str } from '../../../src/decorators' +import { assertState } from '../../helpers' + +describe('feature/casts/custom_casts', () => { + class House extends Model { + static entity = 'houses' + + @Attr() id!: any + @Str('') name!: string + @Attr('{}') owner!: Person + + static casts () { + return { + owner: PersonCast, + } + } + } + + class Person { + firstname: string + lastname: string + + constructor (firstname: string, lastname: string) { + this.firstname = firstname + this.lastname = lastname + } + + static deserialize (fullname: string): Person { + const [firstname, lastname] = fullname.split(' ') + return new Person(firstname, lastname) + } + + serialize (): string { + return `${this.firstname} ${this.lastname}` + } + } + + class PersonCast extends CastAttribute { + get (value?: string): Person | undefined { + if (typeof value == 'string') { + return Person.deserialize(value) + } + return value + } + + set (value?: Person): string | undefined { + if (value) { + return value.serialize() + } + return value + } + } + + it('check custom cast insertion', () => { + const houseRepo = useRepo(House) + + const returnedHouse = houseRepo.save({ id: 1, name: 'First', owner: new Person('John', 'Doe') }) + + expect(returnedHouse.owner.firstname).toEqual('John') + expect(returnedHouse.owner.lastname).toEqual('Doe') + + const savedHouse = houseRepo.find(1)! + + expect(savedHouse.owner.firstname).toEqual('John') + expect(savedHouse.owner.lastname).toEqual('Doe') + + assertState({ houses: { + 1: { id: 1, name: 'First', owner: 'John Doe' }, + } }) + }) + + it('check custom cast update', () => { + const houseRepo = useRepo(House) + + houseRepo.save({ id: 1, name: 'First', owner: new Person('John', 'Doe') }) + + assertState({ houses: { + 1: { id: 1, name: 'First', owner: 'John Doe' }, + } }) + + const returnedHouse = houseRepo.save({ id: 1, owner: new Person('Foo', 'Bar') }) + + expect(returnedHouse.owner.firstname).toEqual('Foo') + expect(returnedHouse.owner.lastname).toEqual('Bar') + + const updatedHouse = houseRepo.find(1)! + + expect(updatedHouse.owner.firstname).toEqual('Foo') + expect(updatedHouse.owner.lastname).toEqual('Bar') + + assertState({ houses: { + 1: { id: 1, name: 'First', owner: 'Foo Bar' }, + } }) + }) +})