1.0.1 • Published 5 years ago

strata-orm v1.0.1

Weekly downloads
3
License
MIT
Repository
github
Last release
5 years ago

StrataJS

An ORM infrastructure design relative to Rails' ActiveRecord implemented in JS

Installation

Using NPM:

$ npm install strata-orm

Or using Yarn:

$ yarn add strata-orm

Usage

Tips: Comment out section are WIP unless the featured is specifed with the tag [Completed]

import Strata from 'strata-orm';

/**
 *  Strata's configuration list with default values below:
 *  Strata.config.DB_FILE    = './db.sqlite3' [Completed]
 **/
 
/**
 *  Strata's available global hooks:
 *  Strata.hooks.beforeCreate  = function() {}
 *  Strata.hooks.afterCreate   = function() {}
 *  Strata.hooks.beforeUpdate  = function() {}
 *  Strata.hooks.afterUpdate   = function() {}
 *  Strata.hooks.beforeDestroy = function() {}
 *  Strata.hooks.afterDestroy  = function() {}
 *  Strata.hooks.beforeCommit  = function() {}
 *  Strata.hooks.afterCommit   = function() {}
 **/

const { INTEGER, STRING, BOOLEAN, TEXT } = Strata.Types;

class UserModel extends Strata.Model {
  constructor() {
    super({
      tableName: 'users',
      fields: [
        { name: 'name',    type: STRING,  required: true },
        { name: 'account', type: STRING,  required: true, unique: true },
        { name: 'age',     type: INTEGER,  default: null },
        { name: 'married', type: BOOLEAN, required: true, default: false }
      ]
    });
    /**
     *  Options parameter in `fields` are:
     *  - name: Name of the table which is `required`
     *  - type: Type of the table which default is `String`, but recommend specify it
     *  - required: The field value must not contain `null` value, default is `false`
     *  - unique: The field values in every record must be distinct, default is `false`
     *  - default: The field's default value, if required is `false`, then default is `null` or specified default value;
     *             Otherwise, if required is `true`, then default value will be ignored because every commit in record, you
     *             will need to save the record with some value in that field
     **/

    /**
     *  Defaults which you don't need to configured yourself, unless you need specific configuration:
     *  this.hasPrimaryKey = true;
     *  this.hasCreateField = true;
     *  this.hasUpdateField = true;
     *
     *  this.primaryKey = { name: 'id', type: Strata.Types.ID, required: true, autoIncrement: true };
     *  this.createField = { name: 'created', type: Strata.Types.Now, required: true };
     *  this.updateField = { name: 'updated', type: Strata.Types.Now, required: true };
     **/
  }
  
  /* You can define hook methods below also, it will override the global hook */
  // beforeCreate()  {}
  // afterCreate()   {}
  // beforeUpdate()  {}
  // afterUpdate()   {}
  // beforeDestroy() {}
  // afterDestroy()  {}
  // beforeCommit()  {}
  // afterCommit()   {}
  
  /* Custom queries */
  // adults() {
    // Query will be listed in the belowing sections
    // return this.all().where({ age: { gte: 18 } });
  // }
  
  /* Custom mutations */
  // getDivorce(id) {
  //   this.find(id).mutate((user) => {
  //     user.married = false;
  //     return user;
  //   });
  // }
}

class PostModel extends Strata.Model {
  constructor() {
    super({
      tableName: 'posts',
      fields: [
        { name: 'title',   type: STRING, required: true },
        { name: 'content', type: STRING, default: 'Content of $title' },
        { name: 'user_id', type: INTEGER, required: true }
      ]
    });
    // In content field, there is a template formatted value which interpolates the value of title field
    // in the defualt value of content field
    
    // void Strata.Reference(Strata.Model model, String primary_key = 'id')
    // Reference other model to create the model relationship through primary key, default primary_key is field `id`
  }
}

const $users = new UserModel();
const $posts = new PostModel();

/* Basic Queries */
// Get all records, for instance, find all users:
const allUsers = $users.all();

// Find some record, for instance, find the first user:
$users.find(1);

// Find first several rows of record, for instance, find the first five posts:
$posts.first(5);

// Find last several rows of record, for instance, find the last five posts:
$posts.last(5);

// Where expressions, for instance:
$users.where({ age: 18 });                       // find users where age == 18
$users.where({ age: { gte: 18 } });              // find users where age >= 18
$users.where({ age: { gte: 12, lt: 18 } });      // find users where age >= 12 && age < 18
$users.where({ name: 'Max', age: { gt: 18 } });  // find users where name == 'Max' && age > 18
$users.where({ age: { ne: 18 } });               // find users where age != 18

// Limit expressions, for instance:
$users.where({ age: { gte: 18 } }).limit(10);       // find at most 10 users where age >= 18

// Sort expressions, for instance:
// allUsers.sort({ id: 'DESC' });               // find all users sorted by ID in descending order
// allUsers.sort({ age: 'ASC' });               // find all users sorted by age in ascending order
// allUsers.sort({ age: 'ASC', id: 'DESC' });   // find all users first sorted by age in ascending order
                                                // then sort ID in descending order
// allUsers.sort((a, b) => a.age > b.age);      // find all users sorted by age in descending order

// Pluck expressions, get specific column value of the queried records:
// allUsers.pluck('name');                      // get an array of names of the user records
// allUsers.pluck(['name']);                    // same as above
// allUsers.pluck(['name', 'age']);             // get an array of pair of name and age (in array format) of the user records

// We need to create the relationship to generate those special query methods
$users.hasMany($posts, { foreignKey: 'user_id' });
$posts.belongsTo($users, { foreignKey: 'user_id' });

// You can then use:
$users.find(1).posts();
$posts.find(1).user();

// If you want to rename the relationship in a more instinctive way, such as:
// UserModel.hasMany(PostModel, { foreignKey: 'user_id', as: 'messages' });
// PostModel.belongsTo(UserModel, { foreignKey: 'user_id', as: 'author' });

// The query will become:
// $users.find(1).messages.all();
// $posts.find(1).author;

/* Basic Mutations */
// Creation
$users.create({ name: 'Maxwell', age: 18, married: false });

// New
// const u = $users.new();
// u.saved // false

// u.save() // Error: Name, Account and Married is Required

// u.name = 'Maxwell';
// u.age = 18;
// u.married = false;

// u.save();
// u.saved; // true

// Update - Similar to HTTP verb PUT
// $users.find(1).update({ name: 'Maximilian', age: 18, married: false });

// Mutation - Similar to HTTP verb PATCH, but you can simply pass a function
// $users.find(1).mutate((user) => {
//   user.name = 'Maximilian';
//   return user;
// });

// Batch Mutation - Update a batch of records, act like map function but with database commission
// $users.where({ age: { gte: 18 } }).mutate((user) => {
//   if (user.age > 30) {
//     user.married = true;
//   }
//   return user;
// });

// Destruction
// $users.find(1).destroy();

// Batch Destruction - Be careful with this
// $users.where({ updated: { lte: Strata.Time(1).month.ago } }).destroy();
// Delete user which havn't been active one month ago (Strata.Time is a pseudo helper function which
// should can be implement later)

// Relational Creation
// Notice that we assume you've already done creating relationships:
// $users.find(1).createPost({ title: '...', content: '...' });

// Relational Update
// You can just update the record when you've already fetched
// $posts.find(1).user().update({ name: '...', age: '...', ... });

// Relational Mutation
// $posts.find(1).user().mutate((user) => {
//   user.name = 'Alexius';
//   return user;
// });

// Relational Batch Mutation
// $users.find(1).posts().mutate((post) => {
//   post.title = '...';
//   return post;
// });

// Relational Destruction
// $posts.find(1).user().destroy();

// Relational Batch Destruction
// $users.find(1).posts().destroy();