Conditional sails model schemas - orm

I'm trying write a conditional model schema based if exists other models.
For example, if exist model 'Message' then add a inbox/outbox:
var userSchema = {
attributes: {
...
}
}
var messageSchema = {
attributes: {
inbox: {
collection: 'Message'
},
outbox: {
collection: 'Message'
}
}
};
if (<function to check that Message Model is available)
_.merge userSchema, messageSchema
Is it possible do something like this?

I think we need one "pre model load" fase on plugins and one hook to alow others modules do alter it structure like drupal 7 scheme.
You cold exports one function to plug this "fase feature" in plugin npm loader like i did with default configs: https://github.com/wejs/we-plugin/blob/master/lib/index.js#L18 and set in https://github.com/wejs/we-example/blob/master/app.js#L63

Related

CakePHP 3.x - Modify user data in Auth component before session creation

When using the Auth component in CakePHP 3 you can define the findAuth() finder (or configure a different finder) to have control over what data is loaded:
// AppController
$this->loadComponent('Auth', [
//...
'authenticate' => [
'Form' => [
'finder' => 'auth'
]
],
//...
]);
// UsersTable
public function findAuth($query, array $options)
{
return $query
->...;
}
I need some functionality that cannot be done with the query builder. How can I post-process the loaded auth data before session creation?
Note that I have different ways of logging in my users, so I would prefer this be kept inside the AuthComponent logic.
(This is still for CakePHP 3, but a brief comment on how this could be done in the new CakePHP 4 Authentication plugion would also be appriciated.)
EDIT: Rough outline of what I need: data needs to re-organised in the users array based on current context, i.e. users can have an active project selected.
I'm still not really sure what exactly you need to re-organize in what way exactly, but generally you can modify the queried data using mappers/reducers and result formatters, the latter usually being the easier way.
Here's a quick example that would add an additional field named additional_data to the result in case a field named active_project_id is set:
$query->formatResults(function (\Cake\Collection\CollectionInterface $results) {
return $results->map(function ($row) {
if (isset($row['active_project_id'])) {
$row['additional_data'] = 'lorem ipsum';
}
return $row;
});
});
Such a finder query would work with the new authentication plugin too.
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields

Dexie.js table.name isn't working even though the table is under the tables property

I'm wanting to fetch all items from a table into a collection but am getting an error that the table name is undefined. Here is my store:
db.version(1).stores({
users: '++id,',
orgs: '++id,',
applications: '++id'
})
Then later here is my call:
db.orgs.toCollection().count(function (count) {
console.log(count)
})
It gives the following error:
TypeError: Cannot read property 'toCollection' of undefined
But when I stop the debugger at the call and type in db.tables sure enough:
1:Table {name: "orgs", schema: TableSchema, _tx: undefined, …}
_tx:undefined
hook:function rv(eventName, subscriber) { … }
name:"orgs"
Any help is appreciated - thanks.
UPDATE
I noticed that when I seeded the database on initial creation I could fetch the data out. So I copied in that code into my template. It still fails however, so there must be something simple I'm missing, here is that code:
import Dexie from '#/dexie.es.js'
export default {
name: 'ListOrgs',
data: () => {
return {
orgs: []
}
},
methods: {
populateOrgs: async function () {
let db = await new Dexie('myDatabase').open()
db.orgs.toCollection().count(function (count) {
console.log(count)
})
}
},
mounted () {
this.populateOrgs()
}
}
Dexie has two modes
Static - the most common one used in most samples.
Dynamic - Schema is not specified in code.
Static Mode
//
// Static Mode
//
const db = new Dexie('myDatabase');
db.version(1).stores({myTable1: '++'});
db.version(2).stores({myTable1: '++, foo'});
db.myTable1.add({foo: 'bar'}); // OK - dexie knows about myTable1!
Dynamic Mode
//
// Dynamic Mode
//
const db = new Dexie('myDatabase');
// FAIL: db.myTable1.add({foo: 'bar'}); // myTable1 is unknown to the API.
// Here, you must wait for db to open, and then access tables using db.table() method:
db.open().then(db => {
const myTable = db.table('myTable');
if (myTable) {
myTable.add({foo: 'bar'});
}
}).catch(error => {
console.error(error);
});
If omitting any version() specification, Dexie will just try to open any existing database with the same name, no matter version or schema. But it won't create the implicit table properties onto the db instance.
When Dynamic Mode is Useful
Dynamic mode can be useful when building an arbritary database utility that should adapt to any indexedDB database - such as a DB explorer. Dynamic mode can also be useful when the javascript code is by design not aware of the schema (what tables are expected to be queried and what indexes there are).
Benefits with Static Mode
No need to wait for db.open() to complete.
Automatic DB creation when neeeded. No complex app code to deal with database versioning.
Automatic DB population when needed.
Design Patterns in Static Mode
db.js
import Dexie from 'dexie';
//
// Let this module do several things:
//
// * Create the singleton Dexie instance for your application.
// * Declare it's schema (and version history / migrations)
// * (Populate default data http://dexie.org/docs/Dexie/Dexie.on.populate)
//
export const db = new Dexie('myDatabase');
db.version(1).stores({
users: '++id,',
orgs: '++id,',
applications: '++id'
});
db.on('populate', () => {
return db.orgs.bulkAdd([
{'foo': 'bar'},
]);
});
app.js
import {db} from './db';
// Wherever you use the database, include your own db module
// instead of creating a new Dexie(). This way your code will
// always make sure to create or upgrade your database whichever
// of your modules that comes first in accessing the database.
//
// You will not have to take care of creation or upgrading scenarios.
//
// Let Dexie do that for you instead.
//
async function countOrgs() {
return await db.orgs.count();
}

one to many - sequelize update - not removing/inserting children

So I've been struggling for a few hours now with a one-to-many mapping update.
I've got a project which has certain tasks (for example).
I add and remove tasks through the frontend and send the revised object to by backend running with sequelize.
Then I tried to update the records as follows:
return models.Project
.findOne({
where: { id: projectToUpdate.id },
include: [models.Task]
})
.then(function (ProjectFromDb) {
return models.sequelize
.transaction({
isolationLevel: models.sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
},
function (t) {
return ProjectFromDb
.update(projectToUpdate,
{
include: [{ model: models.Task }]
})
});
})
.then(function (result) {
return output.getSuccessResult(....
})
.catch(function (error) {
return output.getErrorResult(....
});
But this would only update the Project
Next I tried to update them with an additional then call:
.then(function (updateResult) {
return updateResult.setTasks(projectToUpdate.Tasks, {transaction: t})
})
But this would give me the result that he is trying to update the Task and set the ProjectId to NULL which is not possible because it is non-nullable.
I am currently "manually" adding the tasks and removing them but this seems to be a silly way of using the framework.
Can anyone tell me how to properly make this work with a one-to-many relationship without me calling Tasks.createBulk and Tasks.destroy?
EDIT TO INCLUDE MODEL
JSON object looks like this:
{
id: 1,
projectName: 'nameOfTheProject',
Tasks: [
projectId: 1,
name: 'taskName'
]
}
Please try changing the property name projectId to ProjectId on your Tasks objects that are nested to the projectToUpdate object.
Update
Looking at sequelize's source, it seems that the Instance.$save() function (which is called by Instance.$update() that you're using) does not support nested models creation when you're updating it - it checks if the flag wasNewRecord is true before doing it.

mean.js uses require without var

Have a question with regards to the default meanjs app from yeoman.
Inside the express.js file it has a statement like so:
// Globbing model files
config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
require(path.resolve(modelPath));
});
Now I understand that it gets all the .js files inside the path "./app/models/", but what I am trying to understand is the alone standing
require(path.resolve(modelPath));
How is the require function being used without it being set to a "var"?
An example of one of these included files is like this:
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Article Schema
*/
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
content: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Article', ArticleSchema);
This file doesn't expose anything.
So why is the require being called with a "var" and without the contents exposing a function?
How will this allow for the contents to be used later?
The code in the express.js file executes the contents of your model files. The anatomy of a MEAN.js model file is the following;
Load the mongoose and schema packages, along with all your included
models (if any).
Declare the schema for the given model
Register the given schema under the model name (Article for the given example).
There is nothing to return, hence the lack of any variable assignment in the express.js file. From now on you can call models by the label you assigned in the third part. Therefore, in your controller, you would write something like;
var articles = Article.query();
This line of code would load up the Article schema and run the provided query() method in your back-end (which by default returns all the instances in the database under that model).
In general, remember; not all functions return something.

Nesting Backbone models or collections so that they are linked

I have a User entity that has a subscriptions property. This is an array of IDs.
When I perform a fetch, the API will populate those subscriptions, and return something like this:
{
subscriptions: [1, 2, 3],
__subscriptions: [
{
id: 1,
name: 'Example'
},
{
id: 2,
name: 'Example'
},
{
id: 3,
name: 'Example'
}
]
}
I have done this so that I can still perform actions on the original subscriptions and then save them back to the API. Any changes I make to __subscriptions will not be persisted as the API doesn't recognise this field – it is simply the populated data.
In the parse function of my User, I create the nested collection:
parse: function (response) {
this.subscriptions = new Subscriptions(response.__subscriptions)
}
However, if I want to remove a subscription, I have to splice it from the subscriptions field of the User entity, and then I also have to remove it from the subscriptions collected that is nested as a property on the User:
// Clone the subscriptions property, delete the model with a matching ID, and then set it again.
var value = _.clone(this.get('subscriptions'))
// Use splice instead of delete so that we don't leave an undefined value
// in the array
value.splice(value.indexOf(model.id), 1)
// Also remove the same model from the nested collection
var removedSubscription = this.subscriptions.get(model)
this.subscriptions.remove(removedSubscription)
this.set('subscriptions', value)
this.save()
This is sort of annoying. Ideally, removing an ID from the subscriptions property should automatically update the collection.
Does this seem like a good way to deal with nested models and collections? I've heard bad things about Backbone.Relational so I was interested in a simpler solution.
I would listen to events of Subscriptions collection and update subscriptions argument accordingly.
var User = Backbone.Model.extend({
initialize: function () {
this.subscriptions = new Subscriptions;
this.subscriptions.on('add remove', this.updateSubscriptions, this)
},
updateSubscriptions: function() {
this.set('subscriptions', this.subscriptions.pluck('id'))
},
parse: function (response) {
this.subscriptions.reset(response.__subscriptions);
return Backbone.Model.parse.call(this, response);
}
});
So then removing subscription will update subscriptions attribute of user model:
user.subscriptions.remove(subscription)