环回外键验证

时间:2016-01-14 13:31:47

标签: loopbackjs

我在POST新记录时尝试在我的模型中验证foreignKey。 我的关系有“选项”:{“validate”:true,“forceId”:false}。

但是当我发布新对象时,插入新记录时返回OK,关系对象的inválidID。

{
  "name": "Neighborhood",
  "base": "PersistedModel",
  "idInjection": true,
  "options": {
    "validateUpsert": true
  },
  "properties": {
    "name": {
      "type": "string",
      "required": true
    },
    "address": {
      "type": "string"
    }
  },
  "validations": [],
  "relations": {
    "country": {
      "type": "belongsTo",
      "model": "Country",
      "foreignKey": "",
      "options": {
        "validate": true,
        "forceId": false
      }
    },
    "employees": {
      "type": "hasMany",
      "model": "Employee",
      "foreignKey": ""
    }
  },
  "acls": [        
  ],
  "methods": {}
}

{
  "name": "Employee",
  "base": "PersistedModel",
  "idInjection": true,
  "options": {
    "validateUpsert": true
  },
  "properties": {
    "name": {
      "type": "string",
      "required": true
    },
    "lastName": {
      "type": "string",
      "required": true
    },
    "rol": {
      "type": "string",
      "required": true
    },
    "birthDate": {
      "type": "date"
    }
  },
  "validations": [],
  "relations": {
    "neighborhood": {
      "type": "belongsTo",
      "model": "Neighborhood",
      "foreignKey": "",
      "options": {        
        "validate": true,
        "forceId": false
      }
    }
  },
  "acls": [],
  "methods": {}
}

感谢您的帮助。

4 个答案:

答案 0 :(得分:0)

正如我在评论中提到的,我不认为这些选项会以您使用它们的方式做任何事情,它们仅用于嵌入式关系。相反,您可能需要使用before save操作挂钩:

Neighborhood.observe('before save', function verifyForeignKeys(ctx, next) {
  if (ctx.instance) { // for single model update
    // check ctx.instance.fkField
    if (theKeyisNotValid) {
      return next(new Error('Bad foreign key...'));
    }
  } else { // for multi-model update
    // check ctx.data.fkField
  }
  next();
});

答案 1 :(得分:0)

谢谢@jakerella,这很有效。这是完整的样本:

module.exports = function (Neighborhood) {

    Neighborhood.observe('before save', function verifyForeignKeys(ctx, next) {
        if (ctx.instance) { // for single model update
            // check ctx.instance.fkField
            var s = ctx.instance;
            var countryId = s.__data.countryId;            

            //Get the Application object which the model attached to, and we do what ever we want
            Neighborhood.getApp(function (err, app) {
                //App object returned in the callback
                //PersistedModel.exists(id, callback ((err, exists)))
                app.models.Country.exists(countryId, function (err, exists) {
                    if (err) throw err;
                    if (!exists)
                        return next(new Error('Bad foreign key...'));
                });
            });
            next();
        }});
}

答案 2 :(得分:0)

我认为最好的方法是:

Neighborhood.validateAsync('countryId', hasCountryId, {
    code: 'notFound.relatedInstance',
    message: 'related instance not found'
});


function hasCountryId(err, next) {
    // Use the next if countryId is not required
    if (!this.countryId) {
        return next();
    }

    var Country = Neighborhood.app.models.Country;

    Country.exists(this.countryId, function (error, instance) {
        if (error || !instance) {
            err();
        }
        next();
    });
}

答案 3 :(得分:0)

这可以通过创建和使用mixin来实现(对不起法语评论)

/common/mixins/BelongsToIntegrityCheck.js

'use strict'

let _ = require('lodash');

let checkBelongsToIntegrity = function (ctx, next) {
  if (ctx.instance) {
    let relations = ctx.Model.definition.settings.relations;
    let relationsArray = _.map(relations, rel => {
      return { modelName: rel.model, fk: rel.foreignKey, type: rel.type };
    });

    /* On utilise Lodash pour transformer l'objet des relations en Tableau de la forme
      [
        { modelName: 'achat', fk: 'achat_id', type: 'belongsTo' },
        { modelName: 'FED_AGENT', fk: 'agent_rfagent', type: 'belongsTo' }
      ]
    */

    let thisModel = ctx.Model;
    // Le message qui sera renvoyé en cas d'échec de vérification des contraintes d'intégrité
    let message = "";
    // Le tableau des promises correspondant aux requêtes vérifiants les contraintes
    let promiseArray = [];

    relationsArray.forEach(function (relation) {
      if (relation.type == 'belongsTo') {
        let parentModelName = relation.modelName;
        let parentModel = thisModel.app.models[parentModelName];
        let parentId = ctx.instance[relation.fk];

        // On cherche le modèle parent qui correspond à l'id demandé pour le modèle enfant...
        promiseArray.push(parentModel.findById(parentId).then(function (parentInstance) {
          if (parentInstance === null) {
            message += 'No ' + parentModelName + ' with "' + parentId + '" id. ';
          }
        }));

      }
    }
    );

    /* Une fois que toutes les promesses ont été déterminées et conduisent vers un message en cas de non respect de la contrainte d'intégrité,
    on les regroupe dans une promesse commune résolue quand toutes sont résolues et qui renvoit le message en cas de non respect de contrainte */
    Promise.all(promiseArray)
      .then(
      function () {
        next(message);
      }
      , console.error)
      .catch(function (err) {
        next(err);
      });
  }
}

module.exports = function (Model, options) {
  Model.observe('before save', checkBelongsToIntegrity);
};

/common/mixins/HasManyIntegrityCheck.js

'use strict'

let _ = require('lodash');

let checkHasManyIntegrity = function (ctx, next) {
  if (ctx.where) {
    let relations = ctx.Model.definition.settings.relations;
    let relationsArray = _.map(relations, rel => {
      return { modelName: rel.model, fk: rel.foreignKey, type: rel.type };
    });

    /* On utilise Lodash pour transformer l'objet des relations en Tableau de la forme
      [
        { modelName: 'achat', fk: 'achat_id', type: 'belongsTo' },
        { modelName: 'FED_AGENT', fk: 'agent_rfagent', type: 'belongsTo' }
      ]
    */

    let thisModel = ctx.Model;
    // Le message qui sera renvoyé en cas d'échec de vérification des contraintes d'intégrité
    let message = "";
    // Le tableau des promises correspondant aux requêtes vérifiants les contraintes
    let promiseArray = [];

    relationsArray.forEach(function (relation) {
      if (relation.type == 'hasMany') {
        let childrenModelName = relation.modelName;
        let childrenModel = thisModel.app.models[childrenModelName];
        let parentId = ctx.where.id;
        let whereObject = {};
        whereObject[relation.fk] = ctx.where.id;
        // On cherche les enfants éventuels
        promiseArray.push(childrenModel.find({
          where: whereObject
        }).then(function (data) {
          if (data.length > 0) { // Si il y a des enfants, on renvoit une erreur
            message += 'This "' + thisModel.modelName + '" has "' + childrenModelName + '", and can\'t be deleted. ';
          }
        }));

      }
    }
    );

    /* Une fois que toutes les promesses ont été déterminées et conduisent vers un message en cas de non respect de la contrainte d'intégrité,
    on les regroupe dans une promesse commune résolue quand toutes sont résolues et qui renvoit le message en cas de non respect de contrainte */
    Promise.all(promiseArray)
      .then(
      function () {
        next(message);
      }
      , console.error)
      .catch(function (err) {
        next(err);
      });
  }
}

module.exports = function (Model, options) {
  Model.observe('before delete', checkHasManyIntegrity);
};

在你的模特中:

父模型:

"mixins": {
  "HasManyIntegrityCheck": {}
},

儿童模特:

"mixins": {
  "BelongsToIntegrityCheck": {}
},