Skip to content

Commit

Permalink
Merge fbac708 into 9ad19f2
Browse files Browse the repository at this point in the history
  • Loading branch information
B4nan committed Mar 1, 2019
2 parents 9ad19f2 + fbac708 commit ae5a3c0
Show file tree
Hide file tree
Showing 32 changed files with 779 additions and 268 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ MikroORM's documentation, included in this repo in the root directory, is built
- [Preloading deeply nested structures via populate](https://b4nan.github.io/mikro-orm/nested-populate/)
- [Property validation](https://b4nan.github.io/mikro-orm/property-validation/)
- [Lifecycle hooks](https://b4nan.github.io/mikro-orm/lifecycle-hooks/)
- [Vanilla JS support](https://b4nan.github.io/mikro-orm/usage-with-js/)

## Quick start

Expand Down
3 changes: 3 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
- debugging section in docs (add logger, set debug mode, query logger)
- test request context is properly garbage collected or we need some clean manual up
- single table inheritance
- implement transactions in mongo driver
- use bulk inserts/deletes in mongo driver (insertMany/deleteMany) when flushing
- adopt commitizen to have automatic changelog (http://commitizen.github.io/cz-cli/)
2 changes: 2 additions & 0 deletions docs/defining-entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ export interface Author extends IEntity { }

More information about how collections work can be found on [collections page](collections.md).

If you want to define your entity in Vanilla JavaScript, take a look [here](usage-with-js.md).

## Entity file names

You are free to choose one of those formats for entity filename (for a `BookTag` entity):
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ Heavily inspired by [Doctrine](https://www.doctrine-project.org/) and [Nextras O
- [Usage with MongoDB](usage-with-mongo.md)
- Recipes
- [Usage with NestJS](usage-with-nestjs.md)
- [Usage with Vanilla JS](usage-with-js.md)
97 changes: 97 additions & 0 deletions docs/usage-with-js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Usage with JavaScript

To use `mikro-orm` with Vanilla JavaScript, define your entities like this:

```javascript
const { Collection } = require('mikro-orm');
const { Book } = require('./Book');
const { BaseEntity } = require('./BaseEntity');

/**
* @property {number} id
* @property {Date} createdAt
* @property {Date} updatedAt
* @property {string} name
* @property {string} email
* @property {number} age
* @property {boolean} termsAccepted
* @property {string[]} identities
* @property {Date} born
* @property {Collection<Book>} books
* @property {Book} favouriteBook
* @property {number} version
* @property {string} versionAsString
*/
class Author extends BaseEntity {

/**
* @param {string} name
* @param {string} email
*/
constructor(name, email) {
super();
this.name = name;
this.email = email;
this.createdAt = new Date();
this.updatedAt = new Date();
this.termsAccepted = false;
}

}

const schema = {
name: 'Author',
extends: 'BaseEntity',
properties: {
createdAt: 'Date',
updatedAt: {
type: 'Date',
onUpdate: () => new Date(),
},
name: 'string',
email: 'string',
age: 'number',
termsAccepted: 'boolean',
identities: 'string[]',
born: 'Date',
books: {
reference: '1:m',
fk: 'author',
type: 'Book',
},
favouriteBook: {
reference: 'm:1',
type: 'Book',
fk: 'id',
},
},
};

module.exports = { Author, schema };
```

Reference parameter can be one of (where `SCALAR` is the default one):

```typescript
export enum ReferenceType {
SCALAR = 'scalar',
MANY_TO_ONE = 'm:1',
ONE_TO_MANY = '1:m',
MANY_TO_MANY = 'm:n',
}
```

When initializing ORM, provide `JavaScriptMetadataProvider` as metadata provider:

```javascript
const orm = await MikroORM.init({
entitiesDirs: ['entities'],
dbName: '...',
metadataProvider: JavaScriptMetadataProvider,
});
```

For more examples of plain JavaScript entity definitions,
[take a look here](https://github.com/B4nan/mikro-orm/blob/master/tests/entities-js).

[&larr; Back to table of contents](index.md#table-of-contents)
3 changes: 1 addition & 2 deletions lib/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,7 @@ export class Collection<T extends IEntityType<T>> {

private get property() {
if (!this._property) {
const metadata = MetadataStorage.getMetadata();
const meta = metadata[this.owner.constructor.name];
const meta = MetadataStorage.getMetadata(this.owner.constructor.name);
const field = Object.keys(meta.properties).find(k => this.owner[k] === this);
this._property = meta.properties[field!];
}
Expand Down
6 changes: 5 additions & 1 deletion lib/MikroORM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { FileCacheAdapter } from './cache/FileCacheAdapter';
import { CacheAdapter } from './cache/CacheAdapter';
import { Logger } from './utils/Logger';
import { Utils } from './utils/Utils';
import { TypeScriptMetadataProvider } from './metadata/TypeScriptMetadataProvider';
import { MetadataProvider } from './metadata/MetadataProvider';
import { EntityRepository } from './EntityRepository';
import { EntityClass, IEntity } from './decorators/Entity';

Expand All @@ -22,6 +24,7 @@ const defaultOptions = {
adapter: FileCacheAdapter,
options: { cacheDir: process.cwd() + '/temp' },
},
metadataProvider: TypeScriptMetadataProvider,
};

export class MikroORM {
Expand Down Expand Up @@ -67,7 +70,7 @@ export class MikroORM {
async connect(): Promise<IDatabaseDriver> {
await this.driver.getConnection().connect();
const clientUrl = this.options.clientUrl!.replace(/\/\/([^:]+):(\w+)@/, '//$1:*****@');
this.logger.info(`MikroORM: successfully connected to database ${this.options.dbName} on ${clientUrl}`);
this.logger.info(`MikroORM: successfully connected to database ${this.options.dbName}${clientUrl ? ' on ' + clientUrl : ''}`);

return this.driver;
}
Expand Down Expand Up @@ -105,6 +108,7 @@ export interface MikroORMOptions {
adapter: { new (...params: any[]): CacheAdapter },
options: { [k: string]: any },
},
metadataProvider: { new (options: MikroORMOptions): MetadataProvider },
}

export type Options = Pick<MikroORMOptions, Exclude<keyof MikroORMOptions, keyof typeof defaultOptions>> | MikroORMOptions;
2 changes: 1 addition & 1 deletion lib/cache/CacheAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export interface CacheAdapter {

get(name: string, origin: string): any;
get(name: string): any;

set(name: string, data: any, origin: string): void;

Expand Down
16 changes: 8 additions & 8 deletions lib/decorators/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { IPrimaryKey } from './PrimaryKey';

export function Entity(options: EntityOptions = {}): Function {
return function <T extends { new(...args: any[]): IEntity }>(target: T) {
const storage = MetadataStorage.getMetadata(target.name);
const meta = Utils.merge(storage[target.name], options);
const meta = MetadataStorage.getMetadata(target.name);
Utils.merge(meta, options);
meta.name = target.name;
meta.constructorParams = Utils.getParamNames(target);
meta.extends = Object.getPrototypeOf(target).name || undefined;
Expand Down Expand Up @@ -49,15 +49,15 @@ export type EntityData<T extends IEntityType<T>> = {
} & { [key: string]: any; };

export enum ReferenceType {
SCALAR = 0,
MANY_TO_ONE = 1,
ONE_TO_MANY = 2,
MANY_TO_MANY = 3,
SCALAR = 'scalar',
MANY_TO_ONE = 'm:1',
ONE_TO_MANY = '1:m',
MANY_TO_MANY = 'm:n',
}

export enum Cascade {
PERSIST = 0,
REMOVE = 1,
PERSIST = 'persist',
REMOVE = 'remove',
}

export interface EntityProperty {
Expand Down
4 changes: 1 addition & 3 deletions lib/decorators/ManyToMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { MetadataStorage } from '../metadata/MetadataStorage';
export function ManyToMany(options: ManyToManyOptions): Function {
return function (target: IEntity, propertyName: string) {
const entity = target.constructor.name;
const storage = MetadataStorage.getMetadata(entity);
const meta = storage[entity];
meta.properties = meta.properties || {};
const meta = MetadataStorage.getMetadata(entity);

if (!options.entity) {
throw new Error(`'@ManyToMany({ entity: string | Function })' is required in '${target.constructor.name}.${propertyName}'`);
Expand Down
6 changes: 1 addition & 5 deletions lib/decorators/ManyToOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import { MetadataStorage } from '../metadata/MetadataStorage';

export function ManyToOne(options: ManyToOneOptions = {}): Function {
return function (target: IEntity, propertyName: string) {
const entity = target.constructor.name;
const storage = MetadataStorage.getMetadata(entity);

const meta = storage[entity];
meta.properties = meta.properties || {};
const meta = MetadataStorage.getMetadata(target.constructor.name);
const property = { name: propertyName, reference: ReferenceType.MANY_TO_ONE, cascade: [Cascade.PERSIST] };
meta.properties[propertyName] = Object.assign(property, options) as EntityProperty;
};
Expand Down
6 changes: 1 addition & 5 deletions lib/decorators/OneToMany.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import { MetadataStorage } from '../metadata/MetadataStorage';

export function OneToMany(options: OneToManyOptions): Function {
return function (target: IEntity, propertyName: string) {
const entity = target.constructor.name;
const storage = MetadataStorage.getMetadata(entity);

const meta = storage[entity];
meta.properties = meta.properties || {};
const meta = MetadataStorage.getMetadata(target.constructor.name);

if (!options.entity) {
throw new Error(`'@OneToMany({ entity: string | Function })' is required in '${target.constructor.name}.${propertyName}'`);
Expand Down
6 changes: 1 addition & 5 deletions lib/decorators/PrimaryKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ import { EntityProperty, IEntity, ReferenceType } from './Entity';

export function PrimaryKey(options: PrimaryKeyOptions = {}): Function {
return function (target: IEntity, propertyName: string) {
const entity = target.constructor.name;
const storage = MetadataStorage.getMetadata(entity);

const meta = storage[entity];
const meta = MetadataStorage.getMetadata(target.constructor.name);
options.name = propertyName;
meta.properties = meta.properties || {};
meta.primaryKey = propertyName;
meta.properties[propertyName] = Object.assign({ reference: ReferenceType.SCALAR, primary: true }, options) as EntityProperty;
};
Expand Down
6 changes: 1 addition & 5 deletions lib/decorators/Property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@ import { MetadataStorage } from '../metadata/MetadataStorage';

export function Property(options: PropertyOptions = {}): Function {
return function (target: IEntity, propertyName: string) {
const entity = target.constructor.name;
const storage = MetadataStorage.getMetadata(entity);

const meta = storage[entity];
const meta = MetadataStorage.getMetadata(target.constructor.name);
options.name = propertyName;
meta.properties = meta.properties || {};
meta.properties[propertyName] = Object.assign({ reference: ReferenceType.SCALAR }, options) as EntityProperty;
};
}
Expand Down
3 changes: 1 addition & 2 deletions lib/decorators/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ export function AfterDelete() {

function hook(type: string) {
return function (target: any, method: string) {
const storage = MetadataStorage.getMetadata(target.constructor.name);
const meta = storage[target.constructor.name];
const meta = MetadataStorage.getMetadata(target.constructor.name);

if (!meta.hooks) {
meta.hooks = {};
Expand Down
3 changes: 3 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ export * from './drivers/DatabaseDriver';
export * from './naming-strategy/NamingStrategy';
export * from './naming-strategy/MongoNamingStrategy';
export * from './naming-strategy/UnderscoreNamingStrategy';
export * from './metadata/MetadataProvider';
export * from './metadata/JavaScriptMetadataProvider';
export * from './metadata/TypeScriptMetadataProvider';
export { Cascade, Entity, IEntity, EntityOptions } from './decorators/Entity';
export * from './decorators/OneToMany';
export * from './decorators/ManyToOne';
Expand Down
51 changes: 51 additions & 0 deletions lib/metadata/JavaScriptMetadataProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { MetadataProvider } from './MetadataProvider';
import { Cascade, EntityMetadata, EntityProperty, ReferenceType } from '../decorators/Entity';
import { Utils } from '../utils/Utils';

export class JavaScriptMetadataProvider extends MetadataProvider {

discoverEntity(meta: EntityMetadata, name: string): void {
const { schema } = require(meta.path);
Object.entries(schema.properties).forEach(([name, prop]) => {
if (typeof prop === 'string') {
schema.properties[name] = { type: prop };
}
});

Utils.merge(meta, schema);
Object.entries(meta.properties).forEach(([name, prop]) => {
this.initProperty(prop, name);
});
}

/**
* Re-hydrates missing attributes like `onUpdate` (functions are lost when caching to JSON)
*/
loadFromCache(meta: EntityMetadata, cache: EntityMetadata): void {
Utils.merge(meta, cache);
const { schema } = require(meta.path);

Object.entries(schema.properties).forEach(([name, prop]) => {
if (Utils.isObject(prop)) {
Object.entries(prop).forEach(([attribute, value]) => {
if (!(attribute in meta.properties[name])) {
meta.properties[name][attribute as keyof EntityProperty] = value;
}
});
}
});
}

private initProperty(prop: EntityProperty, propName: string): void {
prop.name = propName;

if (typeof prop.reference === 'undefined') {
prop.reference = ReferenceType.SCALAR;
}

if (prop.reference !== ReferenceType.SCALAR && typeof prop.cascade === 'undefined') {
prop.cascade = [Cascade.PERSIST];
}
}

}
15 changes: 15 additions & 0 deletions lib/metadata/MetadataProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { EntityMetadata } from '../decorators/Entity';
import { MikroORMOptions } from '../MikroORM';
import { Utils } from '../utils/Utils';

export abstract class MetadataProvider {

constructor(protected readonly options: MikroORMOptions) { }

abstract discoverEntity(meta: EntityMetadata, name: string): void;

loadFromCache(meta: EntityMetadata, cache: EntityMetadata): void {
Utils.merge(meta, cache);
}

}

0 comments on commit ae5a3c0

Please sign in to comment.