Properties
- Source:
src/packages/database/model/index.js:553
The name of the corresponding database table for the model.
- Source:
src/packages/database/model/index.js:117
An object where you declare
hasOne
relationships.When declaring a relationship you must specify the inverse of the relationship.
class User extends Model { static hasOne = { profile: { inverse: 'user' // The line above lets Lux know that this relationship is accessible // on profile instances via `profile.user`. } }; } class Profile extends Model { static belongsTo = { user: { inverse: 'profile' // The line above lets Lux know that this relationship is accessible // on user instances via `user.profile`. } }; }
If the name of the model is different than the key of the relationship, you must specify it in the relationship object.
class Profile extends Model { static belongsTo = { owner: { inverse: 'profile', model: 'user' // The line above lets Lux know that this is a relationship with the // `User` model and not a non-existent `Owner` model. } }; }
- Source:
src/packages/database/model/index.js:169
An object where you declare
hasMany
relationships.When declaring a relationship you must specify the inverse of the relationship.
class Author extends Model { static hasMany = { books: { inverse: 'author' // The line above lets Lux know that this relationship is accessible // on book instances via `book.author`. } }; } class Book extends Model { static belongsTo = { author: { inverse: 'books' // The line above lets Lux know that this relationship is accessible // on author instances via `author.books`. } }; }
If the name of the model is different than the key of the relationship, you must specify it in the relationship object.
class Author extends Model { static hasMany = { publications: { inverse: 'author', model: 'book' // The line above lets Lux know that this is a relationship with the // `Book` model and not a non-existent `Publication` model. } }; }
Many to Many
In the examples above there is only one owner of relationship. Sometimes we need to express a many to many relationship. Typically in relational databases, this is done with a join table. When declaring a many to many relationship that uses a join table, you must specify the join model.
class Categorization extends Model { static belongsTo = { tag: { inverse: 'categorization' }, post: { inverse: 'categorization' } } } class Tag extends Model { static hasMany = { posts: { inverse: 'tags', through: 'categorizations' } }; } class Post extends Model { static hasMany = { tags: { inverse: 'posts', through: 'categorizations' } }; }
- Source:
src/packages/database/model/index.js:326
An object where you declare validations for an instance's attributes.
Before a model instance is saved, validations declared in this block are executed. To declare a validation for a model attribute, simply add the attribute name as a key to the validates object. The value for the attribute key should be a function that takes a single argument (the value to validate against) and return a boolean value represent whether or not the attribute is valid.
class User extends Model { static validates { username: value => /^\w{2,30}$/.test(value), password: value => String(value).length >= 8 }; }
In the spirit of have a small api surface area, Lux provides no validation helper functions. You can roll your own helpers with or use one of the many excellent validation libraries like validator.
import { isEmail } from 'validator'; class User extends Model { static validates { email: isEmail }; }
- Source:
src/packages/database/model/index.js:543
A reference to the application's logger.
- Source:
src/packages/database/model/index.js:64
A timestamp representing when the Model instance was created.
- Source:
src/packages/database/model/index.js:573
The name of the resource the model represents.
- Source:
src/packages/database/model/index.js:583
The column name to use for a model's primary key.
- Source:
src/packages/database/model/index.js:259
An object where you declare
belongsTo
relationships.When declaring a relationship you must specify the inverse of the relationship.
class Book extends Model { static belongsTo = { author: { inverse: 'books' // The line above lets Lux know that this relationship is accessible // on author instances via `author.books`. } }; } class Author extends Model { static hasMany = { books: { inverse: 'book' // The line above lets Lux know that this relationship is accessible // on book instances via `book.author`. } }; }
If the name of the model is different than the key of the relationship, you must specify it in the relationship object.
class Book extends Model { static belongsTo = { writer: { inverse: 'books', model: 'author' // The line above lets Lux know that this is a relationship with the // `Author` model and not a non-existent `Writer` model. } }; }
Sometimes our foreign keys in the database do not follow conventions (i.e
author_id
). You have the option to manually specify foreign keys when a situation like this occurs.class Book extends Model { static belongsTo = { author: { inverse: 'books', foreignKey: 'SoMe_UnCoNvEnTiOnAl_FoReIgN_KeY' } }; }
- Source:
src/packages/database/model/index.js:796
- Source:
src/packages/database/model/index.js:820
- Source:
src/packages/database/model/index.js:367
An object where you declare custom query scopes for the model.
Scopes allow you to DRY up query logic by chaining custom set's of queries with built-in query method such as
where
,not
,page
, etc. To declare a scope, add it as a method on the scopes object.class Post extends Model { static hasMany = { tags: { inverse: 'posts' }, comments: { inverse: 'post' } }; static belongsTo = { user: { inverse: 'posts' } }; static scopes = { isPublic() { return this.where({ isPublic: true }); }, byUser(user) { return this.where({ userId: user.id }); }, withEverything() { return this.includes('tags', 'user', 'comments'); } }; }
Given the scopes declared in the example above, here is how we could return all the public posts with relationships eager loaded for the user with the id of 1.
const user = await User.find(1); return Post .byUser(user) .isPublic() .withEverything();
Since scopes can be chained with built-in query methods, we can easily paginate this collection.
const user = await User.find(1); return Post .byUser(user) .isPublic() .withEverything() .page(1);
- Source:
src/packages/database/model/index.js:445
An object where you declare hooks to execute at certain times in a model instance's lifecycle.
There are many lifecycle hooks that are executed through out a model instance's lifetime. The have many use cases such as sanitization of attributes, creating dependent relationships, hashing passwords, and much more.
Execution Order
When creating a record.
- beforeValidation
- afterValidation
- beforeCreate
- beforeSave
- afterCreate
- afterSave
When updating a record.
- beforeValidation
- afterValidation
- beforeUpdate
- beforeSave
- afterUpdate
- afterSave
When deleting a record.
- beforeDestroy
- afterDestroy
Anatomy
Hooks are async functions that are called with two arguments. The first argument is the record that the hook applies to and the second argument is the transaction object relevant to the method from which the hook was called.
The only time you will need to use the transaction object is if you are creating, updating, or deleting different record(s) within the hook. Using the transaction object when modifying the database in a hook ensures that any modifications made within the hook will be rolled back if the function that initiated the transaction fails.
import Notification from 'app/models/notification'; class Comment extends Model { static belongsTo = { post: { inverse: 'comments' }, user: { inverse: 'comments' } }; static hooks = { async afterCreate(comment, trx) { let [post, commenter] = await Promise.all([ comment.post, comment.user ]); const commentee = await post.user; post = post.title; commenter = commenter.name; // Calling .transacting(trx) prevents the commentee from getting a // notification if the comment fails to be persisted in the database. await Notification .transacting(trx) .create({ user: commentee, message: `${commenter} commented on your post "${post}"` }); }, async afterSave() { // Good thing you called transacting in afterCreate. throw new Error('Fatal Error'); } }; }
- Source:
src/packages/database/model/index.js:730
Indicates if the model is dirty.
import Post from 'app/models/post'; Post .find(1) .then(post => { post.isDirty; // => false post.isPublic = true; post.isDirty; // => true return post.save(); }) .then(post => { post.isDirty; // => false });
- Source:
src/packages/database/model/index.js:697
Indicates if the model is new.
import Post from 'app/models/post'; let post = new Post({ body: '', title: 'New Post', isPublic: false }); post.isNew; // => true Post.create({ body: '', title: 'New Post', isPublic: false }).then(post => { post.isNew; // => false; });
- Source:
src/packages/database/model/index.js:563
The canonical name of the model.
- Source:
src/packages/database/model/index.js:46
The canonical name of a
Model
's constructor. - Source:
src/packages/database/model/index.js:763
Indicates if the model is persisted.
import Post from 'app/models/post'; Post .find(1) .then(post => { post.persisted; // => true post.isPublic = true; post.persisted; // => false return post.save(); }) .then(post => { post.persisted; // => true });
- Source:
src/packages/database/model/index.js:55
The name of the API resource a
Model
instance's constructor represents. - Source:
src/packages/database/model/index.js:36
The name of the corresponding database table for a
Model
instance's constructor. - Source:
src/packages/database/model/index.js:73
A timestamp representing the last time the Model instance was updated.
Methods
- Source:
src/packages/database/model/index.js:1396
Check if a value is an instance of a model.
Parameters
- value: Any
The value in question.
Return Value
- Source:
src/packages/database/model/index.js:1383
Check if a model has a scope.
Parameters
- name: String
The name of the scope to look for.
Return Value
- Source:
src/packages/database/model/index.js:1262
Manually begin a new transaction.
Most of the time, you don't need to start transactions yourself. However, the transaction method can be useful if you need to do something like bulk creating records.
await Post.transaction(trx => { return Promise.all([ Post.transacting(trx).create({ // ...props }), Post.transacting(trx).create({ // ...props }) ]); });
Parameters
- fn: Function
The function used for executing the tranasction. This function is called with a new transaction object as it's only argument and is expected to return a promise.
- Source:
src/packages/database/model/index.js:1224
Specify the transaction object to use for following save, update, or destroy method calls.
When you call a method like update or destroy, lux will create a transaction and wrap the internals of the method and other downstream method calls like model hooks within. In some edge cases it can be more useful to manually initiate the transaction. Bulk updating or destroying are good examples of this. When you manually begin a transaction, you can call this method to specify the transaction object that you would like to use for calls to the static create method so lux knows not to automatically begin a new transaction if/when the static create method is called.
// This call to create uses the transaction that lux will initiate. await Post.create(); await Post.transaction(trx => { // This call to create uses the transaction that we created with the // call to the transaction method. return Post .transacting(trx) .create(); });
Parameters
- transaction: Transaction
A transaction object to forward to create method calls.
Return Value
Returns a proxied version of
this
that delagates the transaction param to subsquent create method calls. - Source:
src/packages/database/model/index.js:1152
Create and persist a new instance of the model.
Parameters
- properties: Object
An object containing key, value pairs of the attributes and/or relationships you would like to assign to the instance.
- Source:
src/packages/database/model/index.js:1072
Permanently delete the instance from the database.
Parameters
- Source:
src/packages/database/model/index.js:1097
Reload the record from the database.
Parameters
- Source:
src/packages/database/model/index.js:1112
Rollback attributes and relationships to the last known persisted set of values.
Parameters
- Source:
src/packages/database/model/index.js:955
Persist any unsaved changes to the database.
const post = await Post.first(); console.log(post.title, post.isDirty); // => 'New Post' false post.title = 'How to Save a Lux Model'; console.log(post.title, post.isDirty); // => 'How to Update a Lux Model' true await post.save(); console.log(post.title, post.isDirty); // => 'How to Save a Lux Model' false
Parameters
- Source:
src/packages/database/model/index.js:877
Specify the transaction object to use for following save, update, or destroy method calls.
When you call a method like update or destroy, lux will create a transaction and wrap the internals of the method and other downstream method calls like model hooks within. In some edge cases it can be more useful to manually initiate the transaction. Bulk updating or destroying are good examples of this. When you manually begin a transaction, you can call this method to specify the transaction object that you would like to use for subsequent mutation methods (save, update, destroy, etc.) so lux knows not to automatically begin a new transaction if/when a mutation method is called.
const post = await Post.first(); // This call to update uses the transaction that lux will initiate. await post.update({ // updates to post... }); await post.transaction(trx => { // This call to update uses the transaction that we created with the // call to the transaction method. return post .transacting(trx) .update({ // updates to post... }); });
Parameters
- transaction: Transaction
A transaction object to forward to save, update, or destroy method calls.
Return Value
Returns a proxied version of
this
that delagates the transaction param to subsquent save, update, or destroy method calls. - Source:
src/packages/database/model/index.js:921
Manually begin a new transaction.
Most of the time, you don't need to start transactions yourself. However, if you need to do something like implement bulk updating of related records the transaction method can be useful.
const post = await Post.first().include('user'); const user = await post.user; await post.transaction(trx => { return Promise.all([ post.transacting(trx).update({ // updates to post... }), user.transacting(trx).update({ // updates to user... }) ]); });
Parameters
- fn: Function
The function used for executing the tranasction. This function is called with a new transaction object as it's only argument and is expected to return a promise.
- Source:
src/packages/database/model/index.js:983
Assign values to the instance and persist any changes to the database.
const post = await Post.first(); console.log(post.title, post.isPublic, post.isDirty); // => 'New Post' false false await post.update({ title: 'How to Update a Lux Model', isPublic: true }); console.log(post.title, post.isPublic, post.isDirty); // => 'How to Update a Lux Model' true false
Parameters
- properties: Object
An object containing key, value pairs of the attributes and/or relationships you would like to assign to the instance.