Skip to content

Commit

Permalink
refactor: add generic owner type to Collection<T, O> (#406)
Browse files Browse the repository at this point in the history
  • Loading branch information
B4nan committed Mar 14, 2020
1 parent 894e055 commit 7eca6e2
Show file tree
Hide file tree
Showing 5 changed files with 20 additions and 21 deletions.
2 changes: 1 addition & 1 deletion lib/drivers/AbstractSqlDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export abstract class AbstractSqlDriver<C extends AbstractSqlConnection = Abstra
await this.updateCollectionDiff(coll.property, pk, deleteDiff, insertDiff, ctx);
}

async loadFromPivotTable<T extends AnyEntity<T>>(prop: EntityProperty, owners: Primary<T>[], where?: FilterQuery<T>, orderBy?: QueryOrderMap, ctx?: Transaction): Promise<Dictionary<T[]>> {
async loadFromPivotTable<T extends AnyEntity<T>, O extends AnyEntity<O>>(prop: EntityProperty, owners: Primary<O>[], where?: FilterQuery<T>, orderBy?: QueryOrderMap, ctx?: Transaction): Promise<Dictionary<T[]>> {
const pivotProp2 = this.getPivotInverseProperty(prop);
const meta = this.metadata.get(prop.type);

Expand Down
2 changes: 1 addition & 1 deletion lib/drivers/DatabaseDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export abstract class DatabaseDriver<C extends Connection> implements IDatabaseD
throw new Error(`Aggregations are not supported by ${this.constructor.name} driver`);
}

async loadFromPivotTable<T extends AnyEntity<T>>(prop: EntityProperty, owners: Primary<T>[], where?: FilterQuery<T>, orderBy?: QueryOrderMap, ctx?: Transaction): Promise<Dictionary<T[]>> {
async loadFromPivotTable<T extends AnyEntity<T>, O extends AnyEntity<O>>(prop: EntityProperty, owners: Primary<O>[], where?: FilterQuery<T>, orderBy?: QueryOrderMap, ctx?: Transaction): Promise<Dictionary<T[]>> {
throw new Error(`${this.constructor.name} does not use pivot tables`);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/drivers/IDatabaseDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface IDatabaseDriver<C extends Connection = Connection> {
/**
* When driver uses pivot tables for M:N, this method will load identifiers for given collections from them
*/
loadFromPivotTable<T extends AnyEntity<T>>(prop: EntityProperty, owners: Primary<T>[], where?: FilterQuery<T>, orderBy?: QueryOrderMap, ctx?: Transaction): Promise<Dictionary<T[]>>;
loadFromPivotTable<T extends AnyEntity<T>, O extends AnyEntity<O>>(prop: EntityProperty, owners: Primary<O>[], where?: FilterQuery<T>, orderBy?: QueryOrderMap, ctx?: Transaction): Promise<Dictionary<T[]>>;

getPlatform(): Platform;

Expand Down
27 changes: 13 additions & 14 deletions lib/entity/ArrayCollection.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { Dictionary, EntityProperty, AnyEntity, IPrimaryKey, Primary } from '../typings';
import { AnyEntity, Dictionary, EntityProperty, IPrimaryKey, Primary } from '../typings';
import { ReferenceType } from './enums';
import { Collection } from './Collection';
import { wrap } from './EntityHelper';

export class ArrayCollection<T extends AnyEntity<T>> {
export class ArrayCollection<T extends AnyEntity<T>, O extends AnyEntity<O>> {

[k: number]: T;

protected readonly items: T[] = [];
private _property?: EntityProperty;

constructor(readonly owner: AnyEntity, items?: T[]) {
constructor(readonly owner: O, items?: T[]) {
if (items) {
this.items = items;
Object.assign(this, items);
Expand Down Expand Up @@ -112,14 +112,14 @@ export class ArrayCollection<T extends AnyEntity<T>> {
/**
* @internal
*/
get property() {
get property(): EntityProperty {
if (!this._property) {
const meta = wrap(this.owner).__meta;
const field = Object.keys(meta.properties).find(k => this.owner[k] === this);
this._property = meta.properties[field!];
}

return this._property;
return this._property!;
}

protected propagate(item: T, method: 'add' | 'remove'): void {
Expand All @@ -131,35 +131,34 @@ export class ArrayCollection<T extends AnyEntity<T>> {
}

protected propagateToInverseSide(item: T, method: 'add' | 'remove'): void {
const collection = item[this.property.inversedBy as keyof T] as unknown as Collection<T>;
const collection = item[this.property.inversedBy as keyof T] as unknown as Collection<O, T>;

if (this.shouldPropagateToCollection(collection, method)) {
collection[method](this.owner as T);
collection[method](this.owner);
}
}

protected propagateToOwningSide(item: T, method: 'add' | 'remove'): void {
const collection = item[this.property.mappedBy as keyof T] as unknown as Collection<T>;
const collection = item[this.property.mappedBy as keyof T] as unknown as Collection<O, T>;

if (this.property.reference === ReferenceType.MANY_TO_MANY && this.shouldPropagateToCollection(collection, method)) {
collection[method](this.owner as T);
collection[method](this.owner);
} else if (this.property.reference === ReferenceType.ONE_TO_MANY) {
const value = method === 'add' ? this.owner : null;
item[this.property.mappedBy as keyof T] = value as T[keyof T];
item[this.property.mappedBy] = method === 'add' ? this.owner : null;
}
}

protected shouldPropagateToCollection(collection: Collection<T>, method: 'add' | 'remove'): boolean {
protected shouldPropagateToCollection(collection: Collection<O, T>, method: 'add' | 'remove'): boolean {
if (!collection || !collection.isInitialized()) {
return false;
}

if (method === 'add') {
return !collection.contains(this.owner as T);
return !collection.contains(this.owner);
}

// remove
return collection.contains(this.owner as T);
return collection.contains(this.owner);
}

}
8 changes: 4 additions & 4 deletions lib/entity/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { ArrayCollection } from './ArrayCollection';
import { ReferenceType } from './enums';
import { ValidationError } from '../utils';

export class Collection<T extends AnyEntity<T>> extends ArrayCollection<T> {
export class Collection<T extends AnyEntity<T>, O extends AnyEntity<O> = AnyEntity> extends ArrayCollection<T, O> {

private snapshot: T[] = []; // used to create a diff of the collection at commit time
private initialized = false;
private dirty = false;
private _populated = false;

constructor(owner: AnyEntity, items?: T[], initialized = true) {
constructor(owner: O, items?: T[], initialized = true) {
super(owner, items);
this.initialized = !!items || initialized;
Object.defineProperty(this, 'snapshot', { enumerable: false });
Expand Down Expand Up @@ -120,8 +120,8 @@ export class Collection<T extends AnyEntity<T>> extends ArrayCollection<T> {
}

if (!this.initialized && this.property.reference === ReferenceType.MANY_TO_MANY && em.getDriver().getPlatform().usesPivotTable()) {
const map = await em.getDriver().loadFromPivotTable<T>(this.property, [wrap(this.owner).__primaryKey], options.where, options.orderBy);
this.hydrate(map[wrap(this.owner).__primaryKey].map(item => em.merge<T>(this.property.type, item)));
const map = await em.getDriver().loadFromPivotTable<T, O>(this.property, [wrap(this.owner).__primaryKey], options.where, options.orderBy);
this.hydrate(map[wrap(this.owner).__primaryKey as string].map(item => em.merge<T>(this.property.type, item)));

return this;
}
Expand Down

0 comments on commit 7eca6e2

Please sign in to comment.