diff --git a/packages/core/src/entity/Collection.ts b/packages/core/src/entity/Collection.ts index de74f9df1693..4ff47cab8c61 100644 --- a/packages/core/src/entity/Collection.ts +++ b/packages/core/src/entity/Collection.ts @@ -8,7 +8,7 @@ import { wrap } from './wrap'; export class Collection, O extends AnyEntity = AnyEntity> extends ArrayCollection { - private snapshot: T[] = []; // used to create a diff of the collection at commit time + private snapshot: T[] | undefined = []; // used to create a diff of the collection at commit time private initialized = false; private dirty = false; private _populated = false; @@ -66,10 +66,13 @@ export class Collection, O extends AnyEntity = AnyEnti this.validateModification(items); } + const wasInitialized = this.initialized; this.initialized = true; super.hydrate(items); - if (takeSnapshot) { + if (!wasInitialized) { + this.snapshot = undefined; + } else if (takeSnapshot) { this.takeSnapshot(); } } diff --git a/packages/knex/src/AbstractSqlDriver.ts b/packages/knex/src/AbstractSqlDriver.ts index f4c69231cb01..083f6eb3fda6 100644 --- a/packages/knex/src/AbstractSqlDriver.ts +++ b/packages/knex/src/AbstractSqlDriver.ts @@ -231,15 +231,16 @@ export abstract class AbstractSqlDriver wrap(item, true).__primaryKeys); + const snap = coll.getSnapshot(); + const snapshot = snap ? snap.map(item => wrap(item, true).__primaryKeys) : []; const current = coll.getItems(false).map(item => wrap(item, true).__primaryKeys); - const deleteDiff = snapshot.filter(item => !current.includes(item)); + const deleteDiff = snap ? snapshot.filter(item => !current.includes(item)) : true; const insertDiff = current.filter(item => !snapshot.includes(item)); const target = snapshot.filter(item => current.includes(item)).concat(...insertDiff); const equals = Utils.equals(current, target); // wrong order if we just delete and insert to the end - if (coll.property.fixedOrder && !equals) { + if (coll.property.fixedOrder && !equals && Array.isArray(deleteDiff)) { deleteDiff.length = insertDiff.length = 0; deleteDiff.push(...snapshot); insertDiff.push(...current); diff --git a/tests/EntityHelper.mysql.test.ts b/tests/EntityHelper.mysql.test.ts index dadb31a884b2..54fbdf601f6a 100644 --- a/tests/EntityHelper.mysql.test.ts +++ b/tests/EntityHelper.mysql.test.ts @@ -79,6 +79,27 @@ describe('EntityHelperMySql', () => { expect(book2.tags.getIdentifiers()).toMatchObject([tag2.id]); }); + test('assign() should update not initialized collection [mysql]', async () => { + const other = new BookTag2('other'); + await orm.em.persistAndFlush(other); + const jon = new Author2('Jon Snow', 'snow@wall.st'); + const book = new Book2('Book2', jon); + const tag1 = new BookTag2('tag 1'); + const tag2 = new BookTag2('tag 2'); + const tag3 = new BookTag2('tag 3'); + book.tags.add(tag1, tag2, tag3); + await orm.em.persistAndFlush(book); + orm.em.clear(); + + const book1 = await orm.em.findOneOrFail(Book2, book.uuid); + wrap(book1).assign({ tags: [tag1.id, other.id] }); + await orm.em.flush(); + orm.em.clear(); + + const book2 = await orm.em.findOneOrFail(Book2, book.uuid, ['tags']); + expect(book2.tags.getIdentifiers()).toMatchObject([tag1.id, other.id]); + }); + test('assign() allows deep merging of object properties [mysql]', async () => { const jon = new Author2('Jon Snow', 'snow@wall.st'); const book = new Book2('Book2', jon);