Im having an issue and i have read almost all other active issues on this topic. Im trying to make a custom ManyToMany mapping table as per the TypeORM docs using a seperate entity:
Mapping entity:
import { Entity, Column, ManyToOne, JoinColumn } from 'typeorm';
import { Driver } from './driver.entity';
import { RegistrationStatus } from '../utils/enums';
import { EntityHelper } from './base/entity-helper';
import { Car } from './car.entity';
@Entity()
export class DriverCarsAndStatus extends EntityHelper {
@ManyToOne(() => Driver)
@JoinColumn()
driver: Driver;
@ManyToOne(() => Car)
@JoinColumn()
car: Car;
@Column({
type: 'enum',
enum: RegistrationStatus,
default: RegistrationStatus.PENDING,
})
status: RegistrationStatus;
}
Driver entity:
import { Exclude, Expose } from 'class-transformer';
import {
AfterLoad,
BeforeInsert,
BeforeUpdate,
Column,
Entity,
Index,
JoinColumn,
ManyToOne,
OneToMany,
OneToOne,
} from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { IsNumber, IsOptional, Max, Min } from 'class-validator';
import { DriverReview } from './driver-reviews.entity';
import { UserReview } from './user-reviews.entity';
import { RequestedDriver } from './requestedDriver.entity';
import {
AuthProvidersEnum,
OperationalStatus,
RegistrationStatus,
} from '../utils/enums';
import { MainEntity } from './base/main-entity';
import { Car } from '@app/shared/entities/car.entity';
import { Trip } from './trip.entity';
import { Emergency } from './emergency.entity';
import { Role } from './role.entity';
import { Document } from '@app/shared/entities/document.entity';
import { DocumentType } from '../utils/enums';
import { Address } from './address.entity';
import { DriverCarsAndStatus } from './driverCarsStatuses.entity';
@Entity()
export class Driver extends MainEntity {
@Column({ type: String, unique: true, nullable: true })
// @Expose({ groups: ['me', 'admin'] })
email: string;
@Column({ nullable: true })
@Exclude({ toPlainOnly: true })
password: string;
@Exclude({ toPlainOnly: true })
public previousPassword: string;
@Index()
@Column({ type: String, nullable: true })
firstName?: string;
@Index()
@Column({ type: String, nullable: true })
lastName?: string;
@Column({ type: String, nullable: true })
phoneCode: string;
@Column({ type: String, nullable: true })
phoneNumber: string;
@Column({ type: String, nullable: true })
countryCode: string;
@IsNumber({ maxDecimalPlaces: 1 })
@Max(5)
@Min(1)
@Column({ type: 'float', default: 5 })
rating: number;
@Column({ type: 'date', nullable: true })
birthDate: Date;
@Column({ nullable: true })
nationalId?: string;
@Column({
type: 'enum',
enum: RegistrationStatus,
default: RegistrationStatus.PENDING,
})
registrationStatus: RegistrationStatus;
@Column({
type: 'enum',
enum: OperationalStatus,
default: OperationalStatus.ACTIVATED,
})
operationalStatus: OperationalStatus;
@Column({ type: 'decimal', nullable: true })
totalCashFlow: number;
@Column({ type: 'decimal', nullable: true })
totalTaxes: number;
@Column({ type: 'decimal', nullable: true })
totalEarnings: number;
@Column({ type: 'decimal', nullable: true })
moneyInWallet: number;
@Column({ nullable: true })
employeeNumber: number;
@Column({ nullable: true })
statusReason: string;
@OneToMany(() => Trip, (trip) => trip.driver)
@JoinColumn({ name: 'driverId' })
trips: Trip[];
@ManyToOne(() => Role, {
eager: true,
})
role?: Role;
// @OneToMany(
// () => DriverCarsAndStatus,
// (driverCarStatus) => driverCarStatus.driver,
// )
// driverCarStatuses: DriverCarsAndStatus[];
@Column({ default: AuthProvidersEnum.email })
@Expose({ groups: ['me', 'admin'] })
provider: string;
@Index()
@Column({ type: String, nullable: true })
@Expose({ groups: ['me', 'admin'] })
socialId: string | null;
@OneToMany(() => Document, (document) => document.driver, {
nullable: true,
})
@IsOptional()
@JoinColumn()
documents: Document[] | null;
@Column({
type: 'jsonb',
nullable: true,
default: () => "'{}'",
})
declinedDocuments: Record<DocumentType, boolean>;
@ManyToOne(() => Address, { nullable: true })
address: Address;
@OneToOne(() => Car, (car) => car.currentDriver)
@JoinColumn()
currentCar: Car | null;
@OneToMany(() => DriverReview, (driverReview) => driverReview.author)
authoredReviews: DriverReview[];
@OneToMany(() => UserReview, (userReview) => userReview.targetDriver)
receivedReviews: UserReview[];
@OneToMany(() => Emergency, (emergency) => emergency.driver)
emergencies: Promise<Emergency[]>;
@OneToMany(() => RequestedDriver, (requestedDriver) => requestedDriver.driver)
requestedDrivers: RequestedDriver[];
@AfterLoad()
public loadPreviousPassword(): void {
this.previousPassword = this.password;
}
@BeforeInsert()
@BeforeUpdate()
async setPassword() {
if (this.previousPassword !== this.password && this.password) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password, salt);
}
}
}
Car entity:
import {
Entity,
Column,
JoinColumn,
ManyToOne,
OneToOne,
OneToMany,
} from 'typeorm';
import {
CarClass,
OperationalStatus,
RegistrationStatus,
} from '../utils/enums';
import { Make } from './make.entity';
import { Model } from './model.entity';
import { MainEntity } from './base/main-entity';
import { Driver } from '@app/shared/entities/driver.entity';
import { Trip } from './trip.entity';
import { Document } from './document.entity';
import { DriverCarsAndStatus } from './driverCarsStatuses.entity';
@Entity()
export class Car extends MainEntity {
@Column()
plateNumber: string;
@Column()
color: string;
@ManyToOne(() => Make, (make) => make.car)
@JoinColumn()
make: Make;
@ManyToOne(() => Model, (model) => model.car)
@JoinColumn()
model: Model;
@Column({ type: 'int' })
yearOfProduction: number;
@Column({
type: 'enum',
enum: CarClass,
})
carClass: CarClass;
@Column({ nullable: true })
taxiNumber: number;
@Column({
type: 'enum',
enum: OperationalStatus,
nullable: true,
})
operationalStatus: OperationalStatus;
@Column({ type: 'enum', enum: RegistrationStatus })
registrationStatus: RegistrationStatus;
// @OneToMany(
// () => DriverCarsAndStatus,
// (driverCarStatus) => driverCarStatus.car,
// )
// driverCarStatuses: DriverCarsAndStatus[];
@OneToMany(() => Document, (document) => document.car)
carDocuments: Document[];
@OneToOne(() => Driver, (driver) => driver.currentCar)
currentDriver: Driver | null;
@OneToMany(() => Trip, (trip) => trip.car, { nullable: true })
@JoinColumn({ name: 'carId' })
trips: Trip[];
}
I have the file name specified correctly:
driverCarAndStatuses.entity.ts as per the typeORM config file:
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
cli: {
entitiesDir: 'src',
migrationsDir: 'src/database/migrations',
subscribersDir: 'subscriber',
},
And whenever if codeout the OneToMany decorators from either Driver or Car entity to make it bi-directional as needed I get the error:
[Nest] 26396 – 08/01/2024, 4:14:55 PM ERROR [TypeOrmModule] Unable to connect to the database. Retrying (1)…
[3] TypeORMError: Entity metadata for Driver#driverCarStatuses was not found. Check if you specified a correct entity object and if it’s connected in the connection options.
Everything i have tried for this issue:
Drop schema and dist folder after changes, double check file names and imports (everything is the same as others), autoLoadEntities in orm config, checked
“emitDecoratorMetadata”: true,
“experimentalDecorators”: true,
for tsconfig,
added Entity DriverCarAndStatus in TypeORM.forFeatur in both Driver and Car modules.