mythix-orm
Advanced tools
Comparing version 1.8.2 to 1.8.3
@@ -10,2 +10,5 @@ 'use strict'; | ||
/// The base query generator class. | ||
/// | ||
/// Alias: QueryGenerator | ||
class QueryGeneratorBase { | ||
@@ -12,0 +15,0 @@ constructor(connection) { |
@@ -110,6 +110,6 @@ 'use strict'; | ||
_newQueryEngineScope() { | ||
const QueryEngine = this.getQueryEngineClass(); | ||
let context = this.currentContext; | ||
let newContext = this._inheritContext(context, 'queryEngine'); | ||
let newScope = new QueryEngine(newContext); | ||
const QueryEngineClass = this.getQueryEngineClass(); | ||
let context = this.currentContext; | ||
let newContext = this._inheritContext(context, 'queryEngine'); | ||
let newScope = new QueryEngineClass(newContext); | ||
@@ -116,0 +116,0 @@ return newScope; |
@@ -182,3 +182,3 @@ 'use strict'; | ||
/// connection is serializing it for storage in the database. | ||
/// In this case, <see>connection.convertDateToDBTime</see> is | ||
/// In this case, <see>Connection.convertDateToDBTime</see> is | ||
/// called and provided the `value` for the connection to turn | ||
@@ -200,3 +200,3 @@ /// the date into a proper value for the underlying database. | ||
/// proxy serialization to the underlying database connection | ||
/// via the <see>connection.convertDateToDBTime</see> method. | ||
/// via the <see>Connection.convertDateToDBTime</see> method. | ||
serialize(_value, connection) { | ||
@@ -203,0 +203,0 @@ let value = _value; |
@@ -184,3 +184,3 @@ 'use strict'; | ||
/// connection is serializing it for storage in the database. | ||
/// In this case, <see>connection.convertDateToDBTime</see> is | ||
/// In this case, <see>Connection.convertDateToDBTime</see> is | ||
/// called and provided the `value` for the connection to turn | ||
@@ -202,3 +202,3 @@ /// the date into a proper value for the underlying database. | ||
/// proxy serialization to the underlying database connection | ||
/// via the <see>connection.convertDateToDBTime</see> method. | ||
/// via the <see>Connection.convertDateToDBTime</see> method. | ||
serialize(_value, connection) { | ||
@@ -205,0 +205,0 @@ let value = _value; |
@@ -15,3 +15,3 @@ 'use strict'; | ||
/// Foreign keys are one of the ways Mythix ORM knows that models | ||
/// are related, along with the `Model` and `Models` virtual types. | ||
/// are related, along with the <see>ModelType</see> and <see>ModelsType</see> virtual types. | ||
/// The "target" field must be a fully qualified field name. A | ||
@@ -288,3 +288,3 @@ /// fully qualified field name is a name that is also prefixed by | ||
/// because the type class can modify the model it | ||
/// exists on. For example, the `Model` and `Models` type | ||
/// exists on. For example, the <see>ModelType</see> and <see>ModelsType</see> types | ||
/// inject custom relational methods onto each model instance. | ||
@@ -291,0 +291,0 @@ /// |
@@ -247,3 +247,3 @@ 'use strict'; | ||
/// because the type class can modify the model it | ||
/// exists on. For example, the `Model` and `Models` type | ||
/// exists on. For example, the <see>ModelType</see> and <see>ModelsType</see> types | ||
/// inject custom relational methods onto each model instance. | ||
@@ -250,0 +250,0 @@ /// |
@@ -125,2 +125,124 @@ 'use strict'; | ||
/// A "virtual" field `type` that defines a one to one | ||
/// relationship with another model. | ||
/// | ||
/// Relationships (also known as "associations") in Mythix ORM | ||
/// are defined by virtual fields that return a "relationship query" | ||
/// from a method known as a "query generator" that is defined along | ||
/// with the type definition. | ||
/// This "relationship query" itself defines the model relationship, no matter | ||
/// how complicated. Other ORMs require that you define relationships | ||
/// via their "through table", matching foreign key, type, etc... | ||
/// In Mythix ORM, everything is simply defined as a query for the | ||
/// relationship. | ||
/// | ||
/// This field `type` can be used to define a virtual field | ||
/// that defines a relationship to another model. When your | ||
/// model is instantiated into an instance, then this type will | ||
/// inject the following methods into your model instance to | ||
/// assist you in interacting with the relationship. The "injected methods" | ||
/// listed below are "prefixes". The full names of these methods are | ||
/// always postfixed with the actual name of your field that defines | ||
/// the relationship. For example, if you had a `primaryRole` field | ||
/// that defined this type of relationship, then the methods injected | ||
/// would be `model.getPrimaryRole`, `model.destroyPrimaryRole`, `model.pluckPrimaryRole`, etc... | ||
/// | Injected Method Prefix | Full Method Name | Signature | Description | | ||
/// | ---------------------- | ---------------- | ----------- | --------- | | ||
/// | `create` | `create${fieldName}` | `create(modelInstanceOrAttributes: Model \| object, options?: object, ...args: Array<any>): Promise<Model>` | Create the model defined by the relationship. Returns the newly created model. | | ||
/// | `destroy` | `destroy${fieldName}` | `destroy(options?: object, ...args: Array<any>): Promise<number>` | Destroy the model defined by the relationship. Returns the number of models destroyed. | | ||
/// | `get` | `get${fieldName}` | `get(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<Model>` | Get the model specified by the relationship, if one is found. Returned the related model (if there is any). | | ||
/// | `has` | `has${fieldName}` | `has(options?: object, ...args: Array<any>): Promise<boolean>` | Check if the model defined by the relationship exists. | | ||
/// | `pluck` | `pluck${fieldName}` | `pluck(fields: Field \| string \| Array<Field \| string>, options?: object, ...args: Array<any>): Promise<Array<any>>` | Pluck specific fields from the related model. | | ||
/// | `queryFor` | `queryFor${fieldName}` | `queryFor(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<QueryEngine>` | Get the raw relationship query defined by this field type (returned by the "query generator"). | | ||
/// | `update` | `update${fieldName}` | `update(modelInstanceOrAttributes: Model \| object, options?: object, ...args: Array<any>): Promise<Model>` | Update the model specified by the relationship. Return the updated model. | | ||
/// | ||
/// The `...args` provided for each injected method are pass-through args for the user, that allow | ||
/// the user to pass any arguments they desire to the "relational query generator" method defined | ||
/// by the field (the `...userArgs` as seen in the example below). One minor exception to this is | ||
/// any method that has a `userQuery` argument. The `userQuery` argument will always also be passed as the | ||
/// first argument to the `...userArgs` of the "query generator" method. The `userQuery` argument | ||
/// is to allow the user to define an extra query to merge into the primary/root query of the relationship. | ||
/// For example, in the example provided below, we could call `await user.getPrimaryRole(Role.where.name.EQ('admin'))` | ||
/// to only get the primary role if it also had a `name` attribute with the value `'admin'`. | ||
/// | ||
/// This type also injects duplicates of each of these relational methods with an underscore prefix, i.e. | ||
/// `_getPrimaryRole`. These are known as "root methods". They are provided so the user can overload the | ||
/// default implementations. For example, you could define a `getPrimaryRole` method on your model class, | ||
/// and from that call the "super" method simply by calling `this._getPrimaryRole`. This is required because | ||
/// with method injection `super` doesn't actually exist, because the method was injected, instead of being | ||
/// defined on the model class's prototype. | ||
/// | ||
/// Example: | ||
/// class User extends Model { | ||
/// static fields = { | ||
/// ..., | ||
/// // Target primary role ID | ||
/// // as a foreign key | ||
/// primaryRoleID: { | ||
/// type: Types.FOREIGN_KEY('Role:id', { | ||
/// // When the role is deleted then | ||
/// // clear this attribute by setting it to NULL | ||
/// onDelete: 'SET NULL', | ||
/// onUpdate: 'CASCADE', | ||
/// }), | ||
/// allowNull: true, | ||
/// index: true, | ||
/// }, | ||
/// // Add a virtual relationship field, which creates a | ||
/// // one to one relationship with the Role model. | ||
/// // | ||
/// // Notice how a "type" can always be used directly as | ||
/// // the field definition, instead of defining an object | ||
/// // with a "type" property. | ||
/// primaryRole: Types.Model( | ||
/// // Specify the target/root model. | ||
/// 'Role', | ||
/// // Define our "query generator" method that | ||
/// // will return our "relationship query". | ||
/// (context, connectionModels, ...userArgs) => { | ||
/// // Get the model we need from the connection, | ||
/// // which is conveniently passed to us as the | ||
/// // `connectionModels` argument here. | ||
/// let { Role } = connectionModels; | ||
/// | ||
/// // Get the "self", which is the model instance | ||
/// // calling this method | ||
/// // (i.e. with `model.getPrimaryRole()`, "self" would be "model") | ||
/// let { self } = context; | ||
/// | ||
/// // Now return a relationship query | ||
/// return Role.where | ||
/// .id | ||
/// .EQ(self.primaryRoleID) | ||
/// .AND | ||
/// .MERGE(userArgs[0]); // Apply the `userQuery` (will do nothing if nullish) | ||
/// }, | ||
/// ), | ||
/// }; | ||
/// } | ||
/// | ||
/// // ... later on | ||
/// // get the "primary role" for the first user in the database | ||
/// let user = await User.first(); | ||
/// // Call relationship method injected by the `Types.Model` type. | ||
/// let primaryRole = await user.getPrimaryRole(); | ||
/// | ||
/// Note: | ||
/// See the [Associations](./Associations) article for a more in-depth discussion | ||
/// of Mythix ORM model relationship/associations. | ||
/// | ||
/// Note: | ||
/// The "query generator" method defined with the type can be an asynchronous method. | ||
/// | ||
/// Note: | ||
/// Relational methods **will not** be injected into the model instance if a method of | ||
/// the same name already exists on the model instance. For example, if you define a | ||
/// `getPrimaryRole` method on your model class, then the default `get` relational method | ||
/// (`getPrimaryRole` in our example) won't be injected. However, the "root method" `_getPrimaryRole` | ||
/// will still be injected, allowing you to call that as the `super` method instead. | ||
/// | ||
/// Note: | ||
/// Behind the scenes the relationship methods provided always apply a `LIMIT(1)` to | ||
/// any query used. This is because this relationship type is singular, so it is expected | ||
/// that only a single model can ever be returned. | ||
class ModelType extends RelationalTypeBase { | ||
@@ -131,2 +253,19 @@ isManyRelation() { | ||
/// Internal method used to generate injected method names. | ||
/// | ||
/// This method likely should never be called by the user. | ||
/// It is used to generate the injected method names. | ||
/// | ||
/// Return: string | ||
/// The method name to inject into the model instance. | ||
/// | ||
/// Arguments: | ||
/// field: <see>Field</see> | ||
/// The parent field defining this relationship. | ||
/// operation: string | ||
/// The operation being injected into the model instance, which is one of | ||
/// the prefixes listed above, i.e. `update`, `get`, `destroy`, etc... | ||
/// rootMethod: boolean | ||
/// If `true`, then return a generated "root method" name, instead of a "user method". | ||
/// The only difference is that the "root method" name is prefixed with an underscore `_`. | ||
fieldNameToOperationName(field, operation, rootMethod) { | ||
@@ -139,2 +278,14 @@ if (rootMethod) | ||
/// Inject type methods into the model instance. | ||
/// | ||
/// The <see>Type.initialize</see> method is always called | ||
/// for each model that is instantiated. For this type, this | ||
/// is used to inject the relational methods into the model | ||
/// instance. | ||
/// | ||
/// Arguments: | ||
/// connection: <see>Connection</see> | ||
/// The connection for the model being instantiated. | ||
/// self: <see>Model</see> | ||
/// The model instance the type is being initialized for. | ||
initialize(connection, self) { | ||
@@ -141,0 +292,0 @@ return super.initialize(connection, self, TYPE_OPERATIONS); |
@@ -242,2 +242,120 @@ 'use strict'; | ||
/// A "virtual" field `type` that defines a one to many, | ||
/// or many to many relationship with other model(s). | ||
/// | ||
/// Many to many relationships interact with "sets", which in | ||
/// Mythix ORM is the term used to define an "array of models", | ||
/// in our N to many relationship. | ||
/// | ||
/// Relationships (also known as "associations") in Mythix ORM | ||
/// are defined by virtual fields that return a "relationship query" | ||
/// from a method known as a "query generator" that is defined along | ||
/// with the type definition. | ||
/// This "relationship query" itself defines the model relationship, no matter | ||
/// how complicated. Other ORMs require that you define relationships | ||
/// via their "through table", matching foreign key, type, etc... | ||
/// In Mythix ORM, everything is simply defined as a query for the | ||
/// relationship. | ||
/// | ||
/// This field `type` can be used to define a virtual field | ||
/// that defines a relationship to other models. When your | ||
/// model is instantiated into an instance, then this type will | ||
/// inject the following methods into your model instance to | ||
/// assist you in interacting with the relationship set. The "injected methods" | ||
/// listed below are "prefixes". The full names of these methods are | ||
/// always postfixed with the actual name of your field that defines | ||
/// the relationship. For example, if you had a `roles` field | ||
/// that defined this type of relationship, then the methods injected | ||
/// would be `model.getRoles`, `model.destroyRoles`, `model.pluckRoles`, etc... | ||
/// | Injected Method Prefix | Full Method Name | Signature | Description | | ||
/// | ---------------------- | ---------------- | ----------- | --------- | | ||
/// | `addTo` | `addTo${fieldName}` | `addTo(models: Array<Model \| object>, options?: object): Promise<Array<Model>>` | Create the models defined by the relationship, and add them to the set. Returns the newly created models. | | ||
/// | `count` | `count${fieldName}` | `count(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<number>` | Count the number of models in the set. | | ||
/// | `destroy` | `destroy${fieldName}` | `destroy(options?: object, ...args: Array<any>): Promise<number>` | Destroy the models defined by the relationship set. Returns the number of models destroyed. Note that this not only destroys the target models, but also any through-table relationships involved. | | ||
/// | `get` | `get${fieldName}` | `get(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<Array<Model>>` | Get the models specified by the relationship set. Return the entire model set. | | ||
/// | `has` | `has${fieldName}` | `has(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<boolean>` | Check if the relationship set contains any models. | | ||
/// | `pluck` | `pluck${fieldName}` | `pluck(userQuery?: QueryEngine, fields: Field \| string \| Array<Field \| string>, options?: object, ...args: Array<any>): Promise<Array<any>>` | Pluck specific fields from the related models (set). | | ||
/// | `queryFor` | `queryFor${fieldName}` | `queryFor(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<QueryEngine>` | Get the raw relationship query defined by this field type (returned by the "query generator"). | | ||
/// | `removeFrom` | `removeFrom${fieldName}` | `removeFrom(models: Array<Model>, options?: object): Promise<number>` | Remove the specified models from the relationship set without destroying the target models (destroy only the through-table linking models). Returns the number of models remaining in the set after the operation. | | ||
/// | `set` | `set${fieldName}` | `set(models: Array<Model \| object>, options?: object): Promise<Array<Model>>` | Overwrite the model set with the models specified, destroying models outside the specified set of models. Returns the new models. | | ||
/// | ||
/// The `...args` provided for each injected method are pass-through args for the user, that allow | ||
/// the user to pass any arguments they desire to the "relational query generator" method defined | ||
/// by the field (the `...userArgs` as seen in the example below). One minor exception to this is | ||
/// any method that has a `userQuery` argument. The `userQuery` argument will always also be passed as the | ||
/// first argument to the `...userArgs` of the "query generator" method. The `userQuery` argument | ||
/// is to allow the user to define an extra query to merge into the primary/root query of the relationship. | ||
/// For example, in the example provided below, we could call `await user.getRoles(Role.where.name.EQ('admin'))` | ||
/// to only get the related roles where each role also has a `name` attribute with the value `'admin'`. | ||
/// | ||
/// This type also injects duplicates of each of these relational methods with an underscore prefix, i.e. | ||
/// `_getRoles`. These are known as "root methods". They are provided so the user can overload the | ||
/// default implementations. For example, you could define a `getRoles` method on your model class, | ||
/// and from that call the "super" method simply by calling `this._getRoles`. This is required because | ||
/// with method injection `super` doesn't actually exist, because the method was injected, instead of being | ||
/// defined on the model class's prototype. | ||
/// | ||
/// Example: | ||
/// class User extends Model { | ||
/// static fields = { | ||
/// ..., | ||
/// // Add a virtual relationship field, which creates a | ||
/// // one to many relationship with the Role model. | ||
/// // | ||
/// // Notice how a "type" can always be used directly as | ||
/// // the field definition, instead of defining an object | ||
/// // with a "type" property. | ||
/// roles: Types.Models( | ||
/// // Specify the target/root model. | ||
/// 'Role', | ||
/// // Define our "query generator" method that | ||
/// // will return our "relationship query". | ||
/// (context, connectionModels, ...userArgs) => { | ||
/// // Get the model we need from the connection, | ||
/// // which is conveniently passed to us as the | ||
/// // `connectionModels` argument here. | ||
/// let { Role } = connectionModels; | ||
/// | ||
/// // Get the "self", which is the model instance | ||
/// // calling this method | ||
/// // (i.e. with `model.getRoles()`, "self" would be "model") | ||
/// let { self } = context; | ||
/// | ||
/// // Now return a relationship query | ||
/// return Role.where | ||
/// .userID | ||
/// .EQ(self.id) | ||
/// .AND | ||
/// .MERGE(userArgs[0]); // Apply the `userQuery` (will do nothing if nullish) | ||
/// }, | ||
/// ), | ||
/// }; | ||
/// } | ||
/// | ||
/// // ... later on | ||
/// // get the "roles" for the first user in the database | ||
/// let user = await User.first(); | ||
/// // Call relationship method injected by the `Types.Models` type. | ||
/// let allUserRoles = await user.getRoles(); | ||
/// | ||
/// Note: | ||
/// See the [Associations](./Associations) article for a more in-depth discussion | ||
/// of Mythix ORM model relationship/associations. | ||
/// | ||
/// Note: | ||
/// The "query generator" method defined with the type can be an asynchronous method. | ||
/// | ||
/// Note: | ||
/// Relational methods **will not** be injected into the model instance if a method of | ||
/// the same name already exists on the model instance. For example, if you define a | ||
/// `getRoles` method on your model class, then the default `get` relational method | ||
/// (`getRoles` in our example) won't be injected. However, the "root method" `_getRoles` | ||
/// will still be injected, allowing you to call that as the `super` method instead. | ||
/// | ||
/// Note: | ||
/// This field type will not be "exposed" to models. This means that your model will not | ||
/// have an attribute with the name provided by the field defining this type. The only | ||
/// thing that will exist on your model will be the relational methods injected by this type. | ||
/// Said another way, and using the examples provided, there will be no "roles" attribute | ||
/// on your `User` model. | ||
class ModelsType extends RelationalTypeBase { | ||
@@ -252,2 +370,19 @@ static exposeToModel() { | ||
/// Internal method used to generate injected method names. | ||
/// | ||
/// This method likely should never be called by the user. | ||
/// It is used to generate the injected method names. | ||
/// | ||
/// Return: string | ||
/// The method name to inject into the model instance. | ||
/// | ||
/// Arguments: | ||
/// field: <see>Field</see> | ||
/// The parent field defining this relationship. | ||
/// operation: string | ||
/// The operation being injected into the model instance, which is one of | ||
/// the prefixes listed above, i.e. `addTo`, `get`, `destroy`, etc... | ||
/// rootMethod: boolean | ||
/// If `true`, then return a generated "root method" name, instead of a "user method". | ||
/// The only difference is that the "root method" name is prefixed with an underscore `_`. | ||
fieldNameToOperationName(field, operation, rootMethod) { | ||
@@ -264,2 +399,14 @@ let fieldName = field.pluralName; | ||
/// Inject type methods into the model instance. | ||
/// | ||
/// The <see>Type.initialize</see> method is always called | ||
/// for each model that is instantiated. For this type, this | ||
/// is used to inject the relational methods into the model | ||
/// instance. | ||
/// | ||
/// Arguments: | ||
/// connection: <see>Connection</see> | ||
/// The connection for the model being instantiated. | ||
/// self: <see>Model</see> | ||
/// The model instance the type is being initialized for. | ||
initialize(connection, self) { | ||
@@ -266,0 +413,0 @@ return super.initialize(connection, self, TYPE_OPERATIONS); |
@@ -20,12 +20,2 @@ 'use strict'; | ||
// Model types work by specifying a "target" | ||
// and a "value provider" (source). | ||
// These are fully qualified names, meaning | ||
// they also point to the model as well as the field. | ||
// If no model is specified, then it always defaults to | ||
// "this" model. If no field is specified, then it always | ||
// defaults to "this PK" of the model. | ||
// Mythix ORM will recursively walk all models and fields | ||
// defined until it has the full relationships between | ||
// all fields. | ||
constructor(_targetModelName, queryFactory, _options) { | ||
@@ -32,0 +22,0 @@ let options = _options || {}; |
@@ -0,1 +1,3 @@ | ||
///! DocScope: ModelUtils | ||
'use strict'; | ||
@@ -14,2 +16,44 @@ | ||
/// Parse a fully qualified field name. | ||
/// | ||
/// A fully qualified field name is a field name that | ||
/// is prefixed by its parent model. For example, | ||
/// `'User:email'` is a fully qualified field name, | ||
/// whereas just `'email'` is not. | ||
/// | ||
/// This method will actually parse both formats however. | ||
/// It will parse fully qualified field names, and short-hand | ||
/// field names (just the field name). If just a field name | ||
/// is provided with no model name prefix, then the resulting | ||
/// `modelName` property of the returned object will be `undefined`. | ||
/// | ||
/// Field names themselves can be deeply nested. For example, | ||
/// `'User:metadata.ipAddress'`. Field nesting was designed into | ||
/// the Mythix ORM framework, and is partially supported in some | ||
/// areas of the framework. However, field nesting likely won't work | ||
/// in many places without further support being added. This feature | ||
/// was left in-place for now, because it will likely be expanded upon | ||
/// in the future. | ||
/// Because of this "field nesting", instead of just one field name being | ||
/// returned, an array of field names is *always* returned. Generally, | ||
/// to get the field name you are looking for you would just get the first | ||
/// index (`fieldNames[0]`) on the returned object. | ||
/// | ||
/// This method can and will also parse just a model name, so long as the provided | ||
/// name starts with an upper-case letter. If no colon is found (meaning no model | ||
/// prefix is present), then the provided value will be assumed to be a model name | ||
/// only if it starts with an upper-case letter. Otherwise, it is treated as a field | ||
/// name, and returned in the `fieldNames` array. | ||
/// | ||
/// Arguments: | ||
/// fieldName: string | ||
/// A model name, field name, or fully qualified field name to parse. | ||
/// | ||
/// Return: object | ||
/// An object with the shape `{ modelName: string | undefined; fieldNames: Array<string> }`. | ||
/// The `modelName` property will contain the name of the model for the field, if it is known, | ||
/// which will only be the case for a fully qualified name (i.e. `'User:email'`), or a | ||
/// model name alone (i.e. `'User'`). `fieldNames` will always be an array of field names | ||
/// parsed, which will generally only be one. It can be empty if no field name was provided, | ||
/// for example if only a model name was provided (i.e. `'User'`). | ||
function parseQualifiedName(str) { | ||
@@ -16,0 +60,0 @@ let fieldNames = []; |
@@ -65,7 +65,7 @@ 'use strict'; | ||
'*': (Model, fieldName, query, value) => { | ||
return query.LIKE(value); | ||
return query.AND[fieldName].LIKE(value); | ||
}, | ||
// NOT Like | ||
'!*': (Model, fieldName, query, value) => { | ||
return query.NOT_LIKE(value); | ||
return query.AND[fieldName].NOT_LIKE(value); | ||
}, | ||
@@ -72,0 +72,0 @@ }; |
{ | ||
"name": "mythix-orm", | ||
"version": "1.8.2", | ||
"version": "1.8.3", | ||
"description": "ORM for Mythix framework", | ||
@@ -5,0 +5,0 @@ "main": "lib/index", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
650992
15748