Skip to content

Commit

Permalink
feat!: rename association classes to prevent conflicts with decorators (
Browse files Browse the repository at this point in the history
  • Loading branch information
ephys committed Dec 12, 2023
1 parent d9923a1 commit 665caf3
Show file tree
Hide file tree
Showing 17 changed files with 237 additions and 200 deletions.
125 changes: 70 additions & 55 deletions packages/core/src/associations/belongs-to-many.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ import type {
MultiAssociationOptions,
NormalizedAssociationOptions,
} from './base';
import type { BelongsTo } from './belongs-to';
import { HasMany } from './has-many';
import { HasOne } from './has-one';
import type { BelongsToAssociation } from './belongs-to.js';
import { HasManyAssociation } from './has-many.js';
import { HasOneAssociation } from './has-one.js';
import {
AssociationSecret,
defineAssociation,
Expand Down Expand Up @@ -107,7 +107,8 @@ function addInclude(findOptions: FindOptions, include: Includeable) {
*
* In the API reference below, add the name of the association to the method, e.g. for `User.belongsToMany(Project)` the getter will be `user.getProjects()`.
*/
export class BelongsToMany<
// Note: this class is named BelongsToManyAssociation instead of BelongsToMany to prevent naming conflicts with the BelongsToMany decorator
export class BelongsToManyAssociation<
SourceModel extends Model = Model,
TargetModel extends Model = Model,
ThroughModel extends Model = Model,
Expand Down Expand Up @@ -136,21 +137,21 @@ export class BelongsToMany<
}

/**
* @deprecated use {@link BelongsToMany#foreignKey}
* @deprecated use {@link BelongsToManyAssociation#foreignKey}
*/
get identifier() {
return this.foreignKey;
}

/**
* The corresponding column name of {@link BelongsToMany#foreignKey}
* The corresponding column name of {@link BelongsToManyAssociation#foreignKey}
*/
get identifierField(): string {
return this.fromThroughToSource.identifierField;
}

/**
* The corresponding column name of {@link BelongsToMany#otherKey}
* The corresponding column name of {@link BelongsToManyAssociation#otherKey}
*/
get foreignIdentifierField() {
return this.pairedWith.identifierField;
Expand Down Expand Up @@ -187,25 +188,25 @@ export class BelongsToMany<
/**
* The corresponding association this entity is paired with.
*/
pairedWith: BelongsToMany<TargetModel, SourceModel, ThroughModel, TargetKey, SourceKey>;
pairedWith: BelongsToManyAssociation<TargetModel, SourceModel, ThroughModel, TargetKey, SourceKey>;

// intermediary associations
// these create the actual associations on the model. Remove them would be a breaking change.
readonly fromSourceToThrough: HasMany<SourceModel, ThroughModel, SourceKey, any>;
readonly fromSourceToThroughOne: HasOne<SourceModel, ThroughModel, SourceKey, any>;
get fromThroughToSource(): BelongsTo<ThroughModel, SourceModel, any, SourceKey> {
readonly fromSourceToThrough: HasManyAssociation<SourceModel, ThroughModel, SourceKey, any>;
readonly fromSourceToThroughOne: HasOneAssociation<SourceModel, ThroughModel, SourceKey, any>;
get fromThroughToSource(): BelongsToAssociation<ThroughModel, SourceModel, any, SourceKey> {
return this.fromSourceToThrough.inverse;
}

get fromTargetToThrough(): HasMany<TargetModel, ThroughModel, TargetKey, any> {
get fromTargetToThrough(): HasManyAssociation<TargetModel, ThroughModel, TargetKey, any> {
return this.pairedWith.fromSourceToThrough;
}

get fromTargetToThroughOne(): HasOne<TargetModel, ThroughModel, TargetKey, any> {
get fromTargetToThroughOne(): HasOneAssociation<TargetModel, ThroughModel, TargetKey, any> {
return this.pairedWith.fromSourceToThroughOne;
}

get fromThroughToTarget(): BelongsTo<ThroughModel, TargetModel, any, TargetKey> {
get fromThroughToTarget(): BelongsToAssociation<ThroughModel, TargetModel, any, TargetKey> {
return this.pairedWith.fromThroughToSource;
}

Expand All @@ -222,13 +223,15 @@ export class BelongsToMany<
source: ModelStatic<SourceModel>,
target: ModelStatic<TargetModel>,
options: NormalizedBelongsToManyOptions<SourceKey, TargetKey, ThroughModel>,
pair?: BelongsToMany<TargetModel, SourceModel, ThroughModel, TargetKey, SourceKey>,
pair?: BelongsToManyAssociation<TargetModel, SourceModel, ThroughModel, TargetKey, SourceKey>,
parent?: Association<any>,
) {
super(secret, source, target, options, parent);

try {
this.pairedWith = pair ?? BelongsToMany.associate<TargetModel, SourceModel, ThroughModel, TargetKey, SourceKey>(
this.pairedWith = pair ?? BelongsToManyAssociation.associate<
TargetModel, SourceModel, ThroughModel, TargetKey, SourceKey
>(
secret,
target,
source,
Expand Down Expand Up @@ -281,48 +284,60 @@ export class BelongsToMany<

const sourceKey = options?.sourceKey || (source.primaryKeyAttribute as TargetKey);

this.fromSourceToThrough = HasMany.associate(AssociationSecret, this.source, this.throughModel, removeUndefined({
as: options.throughAssociations.fromSource || `${this.name.plural}${upperFirst(this.pairedWith.name.plural)}`,
scope: this.through.scope,
foreignKey: {
...this.options.foreignKey,
allowNull: this.options.foreignKey.allowNull ?? false,
name: this.options.foreignKey.name || (
this.fromSourceToThrough = HasManyAssociation.associate(
AssociationSecret,
this.source,
this.throughModel,
removeUndefined({
as: options.throughAssociations.fromSource || `${this.name.plural}${upperFirst(this.pairedWith.name.plural)}`,
scope: this.through.scope,
foreignKey: {
...this.options.foreignKey,
allowNull: this.options.foreignKey.allowNull ?? false,
name: this.options.foreignKey.name || (
this.isSelfAssociation ? camelize(`${this.pairedWith.name.singular}_${sourceKey}`)
: camelize(`${this.source.options.name.singular}_${sourceKey}`)
),
},
sourceKey: this.options.sourceKey,
foreignKeyConstraints: this.options.foreignKeyConstraints,
hooks: this.options.hooks,
inverse: {
as: options.throughAssociations.toSource || this.pairedWith.name.singular,
},
}), this);
),
},
sourceKey: this.options.sourceKey,
foreignKeyConstraints: this.options.foreignKeyConstraints,
hooks: this.options.hooks,
inverse: {
as: options.throughAssociations.toSource || this.pairedWith.name.singular,
},
}),
this,
);

this.fromSourceToThroughOne = HasOne.associate(AssociationSecret, this.source, this.throughModel, removeUndefined({
as: options.throughAssociations.fromSource
this.fromSourceToThroughOne = HasOneAssociation.associate(
AssociationSecret,
this.source,
this.throughModel,
removeUndefined({
as: options.throughAssociations.fromSource
? singularize(options.throughAssociations.fromSource)
: `${this.name.singular}${upperFirst(this.pairedWith.name.singular)}`,
scope: this.through.scope,
// foreignKey: this.options.foreignKey,
foreignKey: {
...this.options.foreignKey,
allowNull: this.options.foreignKey.allowNull ?? false,
name: this.options.foreignKey.name || (
scope: this.through.scope,
// foreignKey: this.options.foreignKey,
foreignKey: {
...this.options.foreignKey,
allowNull: this.options.foreignKey.allowNull ?? false,
name: this.options.foreignKey.name || (
this.isSelfAssociation ? camelize(`${this.pairedWith.name.singular}_${sourceKey}`)
: camelize(`${this.source.options.name.singular}_${sourceKey}`)
),
},
sourceKey: this.options.sourceKey,
foreignKeyConstraints: this.options.foreignKeyConstraints,
hooks: this.options.hooks,
inverse: {
as: options.throughAssociations.toSource
),
},
sourceKey: this.options.sourceKey,
foreignKeyConstraints: this.options.foreignKeyConstraints,
hooks: this.options.hooks,
inverse: {
as: options.throughAssociations.toSource
? singularize(options.throughAssociations.toSource)
: this.pairedWith.name.singular,
},
}), this);
},
}),
this,
);

// Get singular and plural names, trying to uppercase the first letter, unless the model forbids it
const plural = upperFirst(this.options.name.plural);
Expand Down Expand Up @@ -411,14 +426,14 @@ Add your own primary key to the through model, on different attributes than the
source: ModelStatic<S>,
target: ModelStatic<T>,
options: BelongsToManyOptions<SourceKey, TargetKey, ThroughModel>,
pair?: BelongsToMany<T, S, ThroughModel, TargetKey, SourceKey>,
pair?: BelongsToManyAssociation<T, S, ThroughModel, TargetKey, SourceKey>,
parent?: Association<any>,
): BelongsToMany<S, T, ThroughModel, SourceKey, TargetKey> {
): BelongsToManyAssociation<S, T, ThroughModel, SourceKey, TargetKey> {
return defineAssociation<
BelongsToMany<S, T, ThroughModel, SourceKey, TargetKey>,
BelongsToManyAssociation<S, T, ThroughModel, SourceKey, TargetKey>,
BelongsToManyOptions<SourceKey, TargetKey, ThroughModel>,
NormalizedBelongsToManyOptions<SourceKey, TargetKey, ThroughModel>
>(BelongsToMany, source, target, options, parent, normalizeBelongsToManyOptions, newOptions => {
>(BelongsToManyAssociation, source, target, options, parent, normalizeBelongsToManyOptions, newOptions => {
// self-associations must always set their 'as' parameter
if (isSameInitialModel(source, target)
&& (
Expand All @@ -431,7 +446,7 @@ Add your own primary key to the through model, on different attributes than the
throw new AssociationError('Both options "as" and "inverse.as" must be defined for belongsToMany self-associations, and their value must be different.');
}

return new BelongsToMany(secret, source, target, newOptions, pair, parent);
return new BelongsToManyAssociation(secret, source, target, newOptions, pair, parent);
});
}

Expand Down Expand Up @@ -846,7 +861,7 @@ Add your own primary key to the through model, on different attributes than the
}

// workaround https://github.com/evanw/esbuild/issues/1260
Object.defineProperty(BelongsToMany, 'name', {
Object.defineProperty(BelongsToManyAssociation, 'name', {
value: 'BelongsToMany',
});

Expand Down
27 changes: 14 additions & 13 deletions packages/core/src/associations/belongs-to.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ import { cloneDeep, removeUndefined } from '../utils/object.js';
import { camelize } from '../utils/string.js';
import { Association } from './base';
import type { AssociationOptions, SingleAssociationAccessors } from './base';
import { HasMany } from './has-many.js';
import { HasOne } from './has-one.js';
import { HasManyAssociation } from './has-many.js';
import { HasOneAssociation } from './has-one.js';
import { defineAssociation, mixinMethods, normalizeBaseAssociationOptions } from './helpers';
import type { NormalizeBaseAssociationOptions } from './helpers';

/**
* One-to-one association
* See {@link Model.belongsTo}
*
* This is almost the same as {@link HasOne}, but the foreign key will be defined on the source model.
* This is almost the same as {@link HasOneAssociation}, but the foreign key will be defined on the source model.
*
* In the API reference below, add the name of the association to the method, e.g. for `User.belongsTo(Project)` the getter will be `user.getProject()`.
*
Expand All @@ -41,7 +41,8 @@ import type { NormalizeBaseAssociationOptions } from './helpers';
* @typeParam SourceKey The name of the Foreign Key attribute on the Source model.
* @typeParam TargetKey The name of the attribute that the foreign key in the source model will reference, typically the Primary Key.
*/
export class BelongsTo<
// Note: this class is named BelongsToAssociation instead of BelongsTo to prevent naming conflicts with the BelongsTo decorator
export class BelongsToAssociation<
S extends Model = Model,
T extends Model = Model,
SourceKey extends AttributeNames<S> = any,
Expand Down Expand Up @@ -69,7 +70,7 @@ export class BelongsTo<

/**
* The name of the attribute the foreign key points to.
* In belongsTo, this key is on the Target Model, instead of the Source Model (unlike {@link HasOne.sourceKey}).
* In belongsTo, this key is on the Target Model, instead of the Source Model (unlike {@link HasOneAssociation.sourceKey}).
* The {@link Association.foreignKey} is on the Source Model.
*/
targetKey: TargetKey;
Expand All @@ -83,7 +84,7 @@ export class BelongsTo<
readonly targetKeyIsPrimary: boolean;

/**
* @deprecated use {@link BelongsTo.targetKey}
* @deprecated use {@link BelongsToAssociation.targetKey}
*/
get targetIdentifier(): string {
return this.targetKey;
Expand Down Expand Up @@ -218,11 +219,11 @@ export class BelongsTo<

switch (options.inverse.type) {
case 'hasMany':
HasMany.associate(secret, target, source, passDown, this, this);
HasManyAssociation.associate(secret, target, source, passDown, this, this);
break;

case 'hasOne':
HasOne.associate(secret, target, source, passDown, this, this);
HasOneAssociation.associate(secret, target, source, passDown, this, this);
break;

default:
Expand All @@ -242,20 +243,20 @@ export class BelongsTo<
target: ModelStatic<T>,
options: BelongsToOptions<SourceKey, TargetKey> = {},
parent?: Association<any>,
): BelongsTo<S, T, SourceKey, TargetKey> {
): BelongsToAssociation<S, T, SourceKey, TargetKey> {
return defineAssociation<
BelongsTo<S, T, SourceKey, TargetKey>,
BelongsToAssociation<S, T, SourceKey, TargetKey>,
BelongsToOptions<SourceKey, TargetKey>,
NormalizedBelongsToOptions<SourceKey, TargetKey>
>(BelongsTo, source, target, options, parent, normalizeBaseAssociationOptions, normalizedOptions => {
>(BelongsToAssociation, source, target, options, parent, normalizeBaseAssociationOptions, normalizedOptions => {
// self-associations must always set their 'as' parameter
if (isSameInitialModel(source, target) && options.inverse
// use 'options' because this will always be set in 'newOptions'
&& (!options.as || !options.inverse.as || options.as === options.inverse.as)) {
throw new AssociationError(`Both options "as" and "inverse.as" must be defined for belongsTo self-associations, and their value must be different, if you specify the 'inverse' option.`);
}

return new BelongsTo(secret, source, target, normalizedOptions, parent);
return new BelongsToAssociation(secret, source, target, normalizedOptions, parent);
});
}

Expand Down Expand Up @@ -407,7 +408,7 @@ export class BelongsTo<
}

// workaround https://github.com/evanw/esbuild/issues/1260
Object.defineProperty(BelongsTo, 'name', {
Object.defineProperty(BelongsToAssociation, 'name', {
value: 'BelongsTo',
});

Expand Down
Loading

0 comments on commit 665caf3

Please sign in to comment.