Joi validation - how to require or optional field based on another key exists in array?

天涯浪子 提交于 2020-06-12 18:54:24

问题


I have this Joi schema:

  let schema = {};

  let stations = {
    contact: {
      first_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      last_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      phone: Joi.string().min(10).max(10).regex(Regex.num, 'num').allow("").error(JoiCustomErrors),
    },
    address: {
      place: Joi.string().min(2).max(10).regex(Regex.alphanum, 'alphanum').required().error(JoiCustomErrors),
      city: Joi.string().min(2).max(30).required().error(JoiCustomErrors),
      street: Joi.string().min(2).max(30).regex(Regex.alphabeta, 'alphabeta').required().error(JoiCustomErrors),
      house_number: Joi.string().min(1).max(6).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
    },
    passengers_amount: Joi.number().min(0).max(4).required().error(JoiCustomErrors),
    notes: Joi.string().min(2).max(100).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
  };
  schema.stations = Joi.array().items(stations).min(1).max(5).required().error(JoiCustomErrors);

As you can see, schema.stations is an array of min 1 and max 5 elements. I want that each of the elements will have the field of "address.place" only if the "contact.first_name" AND "contact.last_name" AND "contact.phone" exists (or filled properly based on the schema).

How can I do that?


回答1:


You can to use Joi.when() and create a schema like this:

Joi.object().keys({
    contact: Joi.object().keys({
        first_name: Joi.string(),
        last_name: Joi.string(),
        phone: Joi.string(),
    }),
    address: Joi.object().keys({
        place: Joi.string(),
        city: Joi.string().min(2).max(30),
        street: Joi.string(),
        house_number: Joi.string()
    }).when('contact', {
        is: Joi.object().keys({
            first_name: Joi.exist(),
            last_name: Joi.exist(),
            phone: Joi.exist(),
        }),
        then: Joi.object({ place: Joi.required() }).required(),
        otherwise: Joi.object({ place: Joi.forbidden() })
    }),
    passengers_amount: Joi.number(),
    notes: Joi.string()
});

I just simplified your shema so it's easy to understand.

Basically, what we are saying here is, if contact.first_name, contact.last_name and contact.phone exists, then address and address.place are required, otherwise address.place is forbidden.

For instance, this object will fail, because address does not exist:

{
    address: {
        first_name: 'a',
        last_name: 'b',
        phone: 'c'
    }
}

and this will fail because address.place does not exist:

{
    address: {
        first_name: 'a',
        last_name: 'b',
        phone: 'c'
    },
    contact: {
    }
}

Finally, according with the schema defined, this object will pass:

{
    address: {
        first_name: 'a',
        last_name: 'b',
        phone: 'c'
    },
    contact: {
        place: 'd'
    }
};



回答2:


Thanks to Soltex, thats the right schema that should be used (but please refer to the changes I have made):

Joi.object().keys({
    contact: {
      first_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      last_name: Joi.string().min(2).max(10).regex(Regex.alphabeta, 'alphabeta').allow("").error(JoiCustomErrors),
      phone: Joi.string().min(10).max(10).regex(Regex.num, 'num').allow("").error(JoiCustomErrors),
    },
    address: Joi.object().keys({
      place: Joi.string().min(2).max(10).regex(Regex.alphanum, 'alphanum').error(JoiCustomErrors),
      city: Joi.string().min(2).max(30).required().error(JoiCustomErrors),
      street: Joi.string().min(2).max(30).regex(Regex.alphabeta, 'alphabeta').required().error(JoiCustomErrors),
      house_number: Joi.string().min(1).max(6).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
    }).when('contact', {
      is: Joi.object().keys({
        first_name: Joi.string().min(1),
        last_name: Joi.string().min(1),
        phone: Joi.string().min(1),
      }),
      then: Joi.object({ place: Joi.required() }).required(),
      otherwise: Joi.object({
        place: Joi.optional().allow("")
      })
    }),
    passengers_amount: Joi.number().min(0).max(4).required().error(JoiCustomErrors),
    notes: Joi.string().min(2).max(100).regex(Regex.alphanum, 'alphanum').allow("").error(JoiCustomErrors)
  })

Please note that changes from my answer to Soltex's answer: he made the when "contact.first_name" "contact.last_name" "contact.phone" to be: Joi.exists(). That is not good, since in such a way even an empty object is "exists" and then require the user to provide the "address.place". We don't want such a thing, we need at leat one char in each of those fields.

Plus, the otherwise statement in Soltex's answers is using the Joi.forbidden() while this is not the desired behaviour in here - we still need to allow the user to provide place, even without a contact, but this shouldn't be mandatory - so I used the: Joi.optional() instead.



来源:https://stackoverflow.com/questions/56423558/joi-validation-how-to-require-or-optional-field-based-on-another-key-exists-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!