1-1 relation load with bookshelf - bookshelf.js

I have 2 tables something likes;
Property {id, name, address_id}
Address {id, city, country, add_line_1, ...}
I linked Property to Address via address_id.
How can I config Bookshelf model to load Property with associated Address via address_id.
I'm trying something likes;
PROPERTY = bookshelf.Model.extend({
tableName: 'PROPERTY',
address: function() {
return this.hasOne(ADDRESS, 'address_id');
}
});
ADDRESS = bookshelf.Model.extend({
tableName: 'ADDRESS',
property: function() {
return this.belongsTo(PROPERTY);
}
});
The error: "Unknown ADDRESS.address_id in where clause"

From the documentation the hasOne() expects the foreign key to be ON THE OTHER table. So try changing your models to:
PROPERTY = bookshelf.Model.extend({
tableName: 'PROPERTY',
address: function() {
return this.belongsTo(ADDRESS, 'address_id');
}
});
ADDRESS = bookshelf.Model.extend({
tableName: 'ADDRESS',
property: function() {
return this.hasOne(PROPERTY);
}
});
And query using, by example:
PROPERTY.fetchAll({withRelated:'address'}).then((a)=>
{console.log(a.toJSON())});

Related

How to get specific columns from join table using withRelated bookshelf

for example i got 2 tables like;
table1 {Id, Name, Description}
table2 {Id, Table1Id, Name, Amount}
With bookshelfJS when i using withRelated something like;
new table1({Id: 1})
.fetchAll({
withRelated: ['Childs']})
.then(function(rows) {
callback(null, rows);
});
I expected my result something like;
{results: [{Id: '', Name: '', Description: '', Childs: [{Id: '', Name: '', Amount: 123}]}]}
I don't want to get Table1Id in the Childs list. How can I specify what columns in my output?
UPDATE
My models;
table1 = bookshelf.Model.extend({
tableName: 'table1',
Childs: function() {
return this.hasMany(table2, 'Table1Id');
}
});
table2 = bookshelf.Model.extend({
tableName: 'table2',
Parent: function() {
return this.belongsTo(Table1);
}
});
If I'm not select Table1Id
new table1({Id: 1})
.fetchAll({
withRelated: ['Childs':function(qb) {
qb.select('Id', 'Name', 'Description');
}]})
.then(function(rows) {
callback(null, rows);
});
then return empty for Childs[].
Should be;
new table1({Id: 1})
.fetchAll({
withRelated: ['Childs':function(qb) {
qb.select('Id', 'Table1Id', 'Name', 'Description');
}]})
.then(function(rows) {
callback(null, rows);
});
well here's the thing: this can be solved pretty easily, but you NEED to select the primary ID of the table in question, otherwise Bookshelf won't know how to tie the data together. The idea is that you get the query builder from the Knex.js and use the select method (http://knexjs.org/#Builder-select).
Here's the solution for your case:
new table1({
Id: 1
})
.fetchAll({
withRelated: [{
'Childs': function(qb) {
//always select the primary Id of the table, otherwise there will be no relations between the tables
qb.select('Id', 'Name', 'Amount'); //Table1Id is omitted!
}
}]
})
.then(function(rows) {
callback(null, rows);
});
Let me know if this solves your problem.
in your bookshelf.js file, add the visibility plugin as below
bookshelf.plugin('visibility');
in your table2 model, hide the unwanted field(s) as below
table2 = bookshelf.Model.extend({
tableName: 'table2',
hidden: ['Table1Id'],
Parent: function() {
return this.belongsTo(Table1);
}
});
you can learn more about the visibility plugin from here
https://github.com/tgriesser/bookshelf/wiki/Plugin:-Visibility

How to validate an update against SimpleSchema before updating a document in collection

I'm trying to validate my data against a SimpleSchema before it gets submitted to the collection but for some reason I'm not able to do so with this error.
Exception while invoking method 'createVendorCategory' { stack: 'TypeError: Cannot call method \'simpleSchema\' of undefined
I have one collections with two SimpleSchemas as follows.
Vendors = new Mongo.Collection('vendors'); //Define the collection.
VendorCategoriesSchema = new SimpleSchema({
name: {
type: String,
label: "Category"
},
slug: {
type: String,
label: "Slug"
},
createdAt : {
type: Date,
label: "Created At",
autoValue: function(){
return new Date()//return the current date timestamp to the schema
}
}
});
VendorSchema = new SimpleSchema({
name: {
type: String,
label: "Name"
},
phone: {
type: String,
label: "Phone"
},
vendorCategories:{
type: [VendorCategoriesSchema],
optional: true
}
});
Vendors.attachSchema(VendorSchema);
The vendorCategory will be added after the Vendor document is created by the user.
Here is what my client side looks like.
Template.addCategory.events({
'click #app-vendor-category-submit': function(e,t){
var category = {
vendorID: Session.get("currentViewingVendor"),
name: $.trim(t.find('#app-cat-name').value),
slug: $.trim(t.find('#app-cat-slug').value),
};
Meteor.call('createVendorCategory', category, function(error) {
//Server-side validation
if (error) {
alert(error);
}
});
}
});
And here is what my server side Meteor.methods look like
createVendorCategory: function(category)
{
var vendorID = Vendors.findOne(category.vendorID);
if(!vendorID){
throw new Meteor.Error(403, 'This Vendor is not found!');
}
//build the arr to be passed to collection
var vendorCategories = {
name: category.name,
slug: category.slug
}
var isValid = check( vendorCategories, VendorSchema.vendorCategories.simpleSchema());//This is not working?
if(isValid){
Vendors.update(VendorID, vendorCategories);
// return vendorReview;
console.log(vendorCategories);
}
else{
throw new Meteor.Error(403, 'Data is not valid');
}
}
I'm guessing this is where the error is coming from.
var isValid = check( vendorCategories, VendorSchema.vendorCategories.simpleSchema());//This is not working?
Any help would be greatly appreciated.
Since you've already defined a sub-schema for the sub-object you can directly check against that:
check(vendorCategories,VendorCategoriesSchema)

meteor - How to add a subdocument as reference with SimpleSchema

I have the following SimpleSchema
Schema.Team = new SimpleSchema({
name:{
type:String
},
members: {
type: [Schema.User],
optional:true
}
});
I would like to insert (on the server) a new team document with the current user, as a reference (not as an embedded document).
I have tried:
Teams.insert({name:"theName",members:[Meteor.user()]}) // works but insert the user as an embedded doc.
Teams.insert({name:"theName",members:[Meteor.user()._id]}) // Error: 0 must be an object
I have also tried in two steps:
var id = Teams.insert({name:teamName});
Teams.update({ _id: id },{ $push: { 'users': Meteor.user()._id } });
Then I have another error I don't understand: Error: When the modifier option is true, validation object must have at least one operator
So how can I insert a document with a reference to another schema?
If you just want to store an array of userIds in your Team collection try:
Schema.Team = new SimpleSchema({
name:{
type:String
},
members: {
type: [String],
optional:true
}
});
Then
Teams.insert({ name: "theName", members: [Meteor.userId()] });
Should work. Later when you want to add an additional id you can just:
Teams.update({ _id: teamId },{ $addToSet: { members: Meteor.userId() }});
The following is probably the syntax you are after, assuming you are also using AutoForm.
If you are using collection2, you can also add an autovalue for when a team is created to automatically add the creator to that team for more convenience.
Schema.Team = new SimpleSchema({
name: {
type:String
},
members: {
type: [String],
defaultValue: [],
allowedValues: function () {
// only allow references to the user collection.
return Meteor.users.find().map(function (doc) {
return doc._id
});
},
autoform: {
// if using autoform, this will display their username as the option instead of their id.
options: function () {
return Meteor.users.find().map(function (doc) {
return {
value: doc._id,
label: doc.username // or something
}
})
}
},
autoValue: function () {
if (this.isInsert && !this.isFromTrustedCode) {
return [this.userId];
}
}
}
});

withRelated binding is undefined

I have two model:
var Book = bookshelf.Model.extend({
tableName: 'books',
chapters: function(){
var chapters = require('./chapter').Chapter;
return this.hasMany(chapters, 'bookId');
}
});
module.exports = {
Book: Book
};
var Chapter = bookshelf.Model.extend({
tableName: 'chapters',
hasTimestamps: ['dateCreated', 'dateUpdated'],
//relationship
book: function(){
var book = require('./user').Book;
return this.belongsTo(book, 'bookId');
}
});
module.exports = {
Chapter: Chapter
}
I do this in one of the controller:
new Book.Book()
.fetch({withRelated:['chapters']})
.then(function(books){
resolve(books);
}).catch(function(err){
reject(err);
});
The "chapters": [] because the debug log give me this, the bindings is undefined:
{ method: 'select',
options: {},
bindings: [ 1 ],
sql: 'select `books`.* from `books` limit ?' }
{ method: 'select',
options: {},
bindings: [ undefined ],
sql: 'select `chapters`.* from `chapters` where `chapters`.`bookId` in (?)' }
Bookshelf assumes that you have a column called 'id' in each table which uniquely identifies each row. If you are using a primary key which is not called 'id' you need to specify what it is called to be able to use the model in a relation. This is done by the 'idAttribute' member e.g.
var ModelA = bookshelf.Model.extend({
tableName: "ModelA",
idAttribute: "modelA_id"
...
}
});
http://bookshelfjs.org/#Model-instance-idAttribute
Solved it with idAttribute: 'cardId' specified.

How to change a schema key?

I have a schema defined below and how can I change the predefined schema key (summary: key) via meteor template?
Schemas.Books = new SimpleSchema(
{
summary: {
type: String
}
}
);
For instance, I want to change this key through a session variable that was defined by router or through a user input.
Not Sure, Try this
If your schema is like this
Books = new SimpleSchema(
{
summary: {
type: String
}
}
);
then in tempalte helpers,
Books._schema.summary.type = function() {
return Session.get("typeValue");
};
In my project I have schema like this
RegisterSchema = new SimpleSchema({
name: {
type: String
},
email: {
type: String,
regEx: SimpleSchema.RegEx.Email
},
password: {
type: String,
label: "Password",
min: 8
},
confirmPassword: {
type: String,
label: "Confirm Password",
min: 8,
custom: function () {
if (this.value !== this.field('password').value) {
return "passwordMismatch";
}
}
}
});
and I'm dynamically setting optional value for email like
RegisterSchema._schema.email.optional = function() { return true };
this worked for me.
All d best
This is not the thing that I'm trying to do but I have learned a new trick : )
I want to change the schema key that I described above like this.
Books = new SimpleSchema(
{
bookName: {
type: String
}
}
);
Changing summary: with bookName:
Actually I want to define schema keys dynamically with respect to user information (userId, userName etc.).

Resources