Skip to content

Commit

Permalink
fix(core): ensure M:N collections are not dirty after populating of i…
Browse files Browse the repository at this point in the history
…nverse side

Closes #3287
  • Loading branch information
B4nan committed Jul 7, 2022
1 parent 4bd606a commit 21ba9b2
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 8 deletions.
8 changes: 4 additions & 4 deletions packages/core/src/entity/Collection.ts
Expand Up @@ -135,10 +135,10 @@ export class Collection<T, O = unknown> extends ArrayCollection<T, O> {
/**
* @internal
*/
hydrate(items: T[]): void {
hydrate(items: T[], forcePropagate?: boolean): void {
this.initialized = true;
super.hydrate(items);
this.takeSnapshot();
this.takeSnapshot(forcePropagate);
}

/**
Expand Down Expand Up @@ -251,11 +251,11 @@ export class Collection<T, O = unknown> extends ArrayCollection<T, O> {
/**
* @internal
*/
takeSnapshot(): void {
takeSnapshot(forcePropagate?: boolean): void {
this.snapshot = [...this.items];
this.setDirty(false);

if (this.property.owner) {
if (this.property.owner || forcePropagate) {
this.items.forEach(item => {
this.propagate(item, 'takeSnapshot');
});
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/entity/EntityLoader.ts
Expand Up @@ -225,7 +225,7 @@ export class EntityLoader {
private initializeManyToMany<T extends AnyEntity<T>>(filtered: T[], children: AnyEntity[], prop: EntityProperty, field: keyof T): void {
for (const entity of filtered) {
const items = children.filter(child => (child[prop.mappedBy] as unknown as Collection<AnyEntity>).contains(entity));
(entity[field] as unknown as Collection<AnyEntity>).hydrate(items);
(entity[field] as unknown as Collection<AnyEntity>).hydrate(items, true);
}
}

Expand Down Expand Up @@ -349,7 +349,7 @@ export class EntityLoader {
});
return this.em.getUnitOfWork().registerManaged<T>(entity, item, { refresh, loaded: true });
});
(entity[prop.name] as unknown as Collection<AnyEntity>).hydrate(items);
(entity[prop.name] as unknown as Collection<AnyEntity>).hydrate(items, true);
children.push(...items);
}

Expand Down
3 changes: 1 addition & 2 deletions packages/mariadb/src/MariaDbSchemaHelper.ts
Expand Up @@ -232,8 +232,7 @@ export class MariaDbSchemaHelper extends SchemaHelper {
}

protected wrap(val: string | undefined | null, type: Type<unknown>): string | undefined | null {
const stringType = type instanceof StringType || type instanceof TextType || type instanceof EnumType;
return typeof val === 'string' && val.length > 0 && stringType ? this.platform.quoteValue(val) : val;
return val;
}

}
56 changes: 56 additions & 0 deletions tests/issues/GH3287.test.ts
@@ -0,0 +1,56 @@
import { Collection, Entity, ManyToMany, MikroORM, PrimaryKey } from '@mikro-orm/core';

@Entity()
export class Group {

@PrimaryKey()
id!: number;

@ManyToMany({
entity: 'Participant',
mappedBy: 'groups',
})
participants = new Collection<Participant>(this);

}

@Entity()
export class Participant {

@PrimaryKey()
id!: number;

@ManyToMany({
entity: () => Group,
inversedBy: 'participants',
})
groups = new Collection<Group>(this);

}

let orm: MikroORM;

beforeAll(async () => {
orm = await MikroORM.init({
entities: [Participant],
dbName: ':memory:',
type: 'better-sqlite',
});
await orm.getSchemaGenerator().createSchema();
});

afterAll(() => orm.close(true));

test('removing items from m:n (GH 3287)', async () => {
const group1 = new Group();
group1.participants.add(new Participant());
await orm.em.fork().persistAndFlush(group1);

const group = await orm.em.findOneOrFail(Group, group1, {
populate: ['participants'],
});

orm.em.remove(group);

await orm.em.flush();
});

0 comments on commit 21ba9b2

Please sign in to comment.