diff --git a/README.md b/README.md index 9eb0f82..0b199a5 100644 --- a/README.md +++ b/README.md @@ -125,11 +125,30 @@ V::lang('ar'); ``` +You can conditionally require values using required conditional rules. In this example we're requiring either a token or an email address/password combination for authentication. +```php +// this rule set would work for either data set... +$data = ['email' => 'test@test.com', 'password' => 'mypassword']; +// or... +$data = ['token' => 'jashdjahs83rufh89y38h38h']; +$v = new Valitron\Validator($data); +$v->rules([ + 'requiredWithout' => [ + ['token', ['email', 'password']] + ], + 'requiredWith' => [] + ['password', ['email']] + ] +]); +$this->assertTrue($v->validate()); +``` ## Built-in Validation Rules * `required` - Field is required + * `requiredWith` - Field is required if any other fields are present + * `requiredWithout` - Field is required if any other fields are NOT present * `equals` - Field must match another field (email/password confirmation) * `different` - Field must be different than another field * `accepted` - Checkbox or Radio must be accepted (yes, on, 1, true) @@ -198,7 +217,101 @@ $v->rules([ $v->validate(); ``` -Example using alternate syntax. +## requiredWith fields usage +The `requiredWith` rule checks that the field is required, not null, and not the empty string, if any other fields are present, not null, and not the empty string. +```php +// password field will be required when the username field is provided and not empty +$v->rule('requiredWith', 'password', 'username') +``` + +Alternate syntax. +```php +$v = new Valitron\Validator(['username' => 'spiderman', 'password' => 'Gr33nG0Blin']); +$v->rules([ + 'requiredWith' => [ + ['password', 'username'] + ] +]); +$v->validate(); +``` + +*Note* You can provide multiple values either as comma-separated list or as an array. In this case if ANY of the fields are present the field will be required. +```php +// in this case the password field will be required if the username or email fields are present +$v->rule('requiredWith', 'password', 'username', 'email'); +// this is the same as the above line +$v->rule('requiredWith', 'password', ['username', 'email']); +``` + +Alternate syntax. +```php +$v = new Valitron\Validator(['username' => 'spiderman', 'password' => 'Gr33nG0Blin']); +$v->rules([ + 'requiredWith' => [ + ['password', 'username', 'email'] + ] +]); +$v->validate(); +``` +This is the same as the above example: +```php +$v = new Valitron\Validator(['username' => 'spiderman', 'password' => 'Gr33nG0Blin']); +$v->rules([ + 'requiredWith' => [ + ['password', ['username', 'email']] + ] +]); +$v->validate(); +``` + +## requiredWithout fields usage +The `requiredWithout` rule checks that the field is required, not null, and not the empty string, if any other fields are NOT present. +```php +// this rule will require the username field when the first_name is not present +$v->rule('requiredWithout', 'username', 'first_name') +``` + +Alternate syntax. +```php +// this will return true, as the username is provided when the first_name is not provided +$v = new Valitron\Validator(['username' => 'spiderman']); +$v->rules([ + 'requiredWithout' => [ + ['username', 'first_name'] + ] +]); +$v->validate(); +``` + +*Note* You can provide multiple values either as comma-separated list or as an array. In this case if ANY of the fields are NOT present the field will be required. +```php +// in this case the password field will be required if either the first_name or last_name fields are not present +$v->rule('requiredWithout', 'username', 'first_name', 'last_name'); +// this is the same as the above line +$v->rule('requiredWithout', 'username', ['first_name', 'last_name']); +``` + +Alternate syntax. +```php +// this passes validation because although the last_name field is not present, the username is provided +$v = new Valitron\Validator(['username' => 'spiderman', 'first_name' => 'Peter']); +$v->rules([ + 'requiredWithout' => [ + ['username', 'first_name', 'last_name'] + ] +]); +$v->validate(); +``` +This is the same as the above example: +```php +$v = new Valitron\Validator(['username' => 'spiderman', 'first_name' => 'Peter']); +$v->rules([ + 'requiredWithout' => [ + ['username', ['first_name', 'last_name']] + ] +]); +$v->validate(); +``` ## equals fields usage The `equals` rule checks if two fields are equals in the data array, and that the second field is not null. diff --git a/lang/en.php b/lang/en.php index 0c9e5a1..ba062b0 100644 --- a/lang/en.php +++ b/lang/en.php @@ -34,5 +34,7 @@ return array( 'lengthMax' => "must not exceed %d characters", 'instanceOf' => "must be an instance of '%s'", 'containsUnique' => "must contain unique elements only", + 'requiredWith' => "is required", + 'requiredWithout'=> "is required", 'subset' => "contains an item that is not in the list", ); diff --git a/src/Valitron/Validator.php b/src/Valitron/Validator.php index 93c06be..738d21a 100644 --- a/src/Valitron/Validator.php +++ b/src/Valitron/Validator.php @@ -897,6 +897,74 @@ class Validator return $isInstanceOf; } + /** + * Validates whether or not a field is required based on whether or not other fields are present. + * + * @param string $field name of the field in the data array + * @param mixed $value value of this field + * @param array $params parameters for this rule + * @param array $fields full list of data to be validated + * @return bool + */ + protected function validateRequiredWith($field, $value, $params, $fields) + { + $conditionallyReq = false; + // correct for fields as comma list of params or array of params + // this will allow either to work + $params = isset($params[0]) && is_array($params[0]) ? $params[0] : $params; + // if we actually have conditionally required with fields to check against + if (count($params)) { + foreach ($params as $requiredField) { + // check the field is set, not null, and not the empty string + if (isset($fields[$requiredField]) && (!is_null($fields[$requiredField]) + || (is_string($fields[$requiredField]) && trim($fields[$requiredField]) !== ''))) { + $conditionallyReq = true; + break; + } + } + } + // if we have conditionally required fields + if ($conditionallyReq && (is_null($value) || + is_string($value) && trim($value) === '')) { + return false; + } + return true; + } + + /** + * Validates whether or not a field is required based on whether or not other fields are present. + * + * @param string $field name of the field in the data array + * @param mixed $value value of this field + * @param array $params parameters for this rule + * @param array $fields full list of data to be validated + * @return bool + */ + protected function validateRequiredWithout($field, $value, $params, $fields) + { + $conditionallyReq = false; + // correct for fields as comma list of params or array of params + // this will allow either to work + $params = isset($params[0]) && is_array($params[0]) ? $params[0] : $params; + // if we actually have conditionally required with fields to check against + if (count($params)) { + foreach ($params as $requiredField) { + // check the field is NOT set, null, or the empty string, in which case we are requiring this value be present + if (!isset($fields[$requiredField]) || (is_null($fields[$requiredField]) + || (is_string($fields[$requiredField]) && trim($fields[$requiredField]) === ''))) { + $conditionallyReq = true; + break; + } + } + } + // if we have conditionally required fields + if ($conditionallyReq && (is_null($value) || + is_string($value) && trim($value) === '')) { + return false; + } + return true; + } + /** * Validate optional field * @@ -1051,8 +1119,9 @@ class Validator foreach ($v['fields'] as $field) { list($values, $multiple) = $this->getPart($this->_fields, explode('.', $field), false); - // Don't validate if the field is not required and the value is empty - if ($this->hasRule('optional', $field) && isset($values)) { + // Don't validate if the field is not required and the value is empty and we don't have a conditionally required rule present on the field + if (($this->hasRule('optional', $field) && isset($values)) + || ($this->hasRule('requiredWith', $field) || $this->hasRule('requiredWithout', $field))) { //Continue with execution below if statement } elseif ( $v['rule'] !== 'required' && !$this->hasRule('required', $field) && diff --git a/tests/Valitron/ValidateTest.php b/tests/Valitron/ValidateTest.php index 169c0e6..d1cfab1 100644 --- a/tests/Valitron/ValidateTest.php +++ b/tests/Valitron/ValidateTest.php @@ -2220,6 +2220,310 @@ class ValidateTest extends BaseTestCase $this->assertFalse($v->validate()); } + public function testRequiredWithValid() + { + $v = new Validator(array('username' => 'tester', 'password' => 'mypassword')); + $v->rule('requiredWith', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidEmptyString() + { + $v = new Validator(array('username' => '', 'password' => 'mypassword')); + $v->rule('requiredWith', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidZeroValue() + { + $v = new Validator(array('username' => 0, 'password' => 'mypassword')); + $v->rule('requiredWith', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidNullValue() + { + $v = new Validator(array('username' => null, 'password' => 'mypassword')); + $v->rule('requiredWith', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidAltSyntax() + { + $v = new Validator(array('username' => 'tester', 'password' => 'mypassword')); + $v->rules(array( + 'requiredWith' => array( + array('password', 'username') + ) + )); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidList() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com', 'password' => 'mypassword')); + $v->rule('requiredWith', 'password', 'username', 'email'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidListAltSyntax() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com', 'password' => 'mypassword')); + $v->rules(array( + 'requiredWith' => array( + array('password', 'username', 'email') + ) + )); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidListArray() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com', 'password' => 'mypassword')); + $v->rule('requiredWith', 'password', array('username', 'email')); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithValidListArrayAltSyntax() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com', 'password' => 'mypassword')); + $v->rules(array( + 'requiredWith' => array( + array('password', array('username', 'email')) + ) + )); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithInvalid() + { + $v = new Validator(array('username' => 'tester')); + $v->rule('requiredWith', 'password', 'username'); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithInvalidAltSyntax() + { + $v = new Validator(array('username' => 'tester')); + $v->rules(array( + 'requiredWith' => array( + array('password', 'username') + ) + )); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithInvalidList() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com')); + $v->rule('requiredWith', 'password', 'username', 'email'); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithInvalidListAltSyntax() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com')); + $v->rules(array( + 'requiredWith' => array( + array('password', 'username', 'email') + ) + )); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithInvalidListArray() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com')); + $v->rule('requiredWith', 'password', array('username', 'email')); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithInvalidListArrayAltSyntax() + { + $v = new Validator(array('username' => 'tester', 'email' => 'test@test.com')); + $v->rules(array( + 'requiredWith' => array( + array('password', array('username', 'email')) + ) + )); + $this->assertFalse($v->validate()); + } + + // required without tests + + public function testRequiredWithoutValid() + { + $v = new Validator(array('password' => 'mypassword')); + $v->rule('requiredWithout', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidEmptyString() + { + $v = new Validator(array('username' => '', 'password' => 'mypassword')); + $v->rule('requiredWithout', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidZeroValue() + { + $v = new Validator(array('username' => 0, 'password' => 'mypassword')); + $v->rule('requiredWithout', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidNullValue() + { + $v = new Validator(array('username' => null, 'password' => 'mypassword')); + $v->rule('requiredWithout', 'password', 'username'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidAltSyntax() + { + $v = new Validator(array('password' => 'mypassword')); + $v->rules(array( + 'requiredWithout' => array( + array('password', 'username') + ) + )); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidList() + { + $v = new Validator(array('password' => 'mypassword')); + $v->rule('requiredWithout', 'password', 'username', 'email'); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidListAltSyntax() + { + $v = new Validator(array('password' => 'mypassword')); + $v->rules(array( + 'requiredWithout' => array( + array('password', 'username', 'email') + ) + )); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidListArray() + { + $v = new Validator(array('password' => 'mypassword')); + $v->rule('requiredWithout', 'password', array('username', 'email')); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutValidListArrayAltSyntax() + { + $v = new Validator(array('password' => 'mypassword')); + $v->rules(array( + 'requiredWithout' => array( + array('password', array('username', 'email')) + ) + )); + $this->assertTrue($v->validate()); + } + + public function testRequiredWithoutInvalid() + { + $v = new Validator(array('username' => 'tester')); + $v->rule('requiredWithout', 'password', 'username', 'email'); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithoutInvalidAltSyntax() + { + $v = new Validator(array('username' => 'tester')); + $v->rules(array( + 'requiredWithout' => array( + array('password', 'username', 'email') + ) + )); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithoutInvalidList() + { + $v = new Validator(array()); + $v->rule('requiredWithout', 'password', 'username', 'email'); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithoutInvalidListAltSyntax() + { + $v = new Validator(array('email' => 'test@test.com')); + $v->rules(array( + 'requiredWithout' => array( + array('password', 'username', 'email') + ) + )); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithoutInvalidListArray() + { + $v = new Validator(array('username' => 'tester')); + $v->rule('requiredWithout', 'password', array('username', 'email')); + $this->assertFalse($v->validate()); + } + + public function testRequiredWithoutInvalidListArrayAltSyntax() + { + $v = new Validator(array('email' => 'test@test.com')); + $v->rules(array( + 'requiredWithout' => array( + array('password', array('username', 'email')) + ) + )); + $this->assertFalse($v->validate()); + } + + public function testConditionallyRequiredAuthSampleToken() + { + $v = new Validator(array('token' => 'ajkdhieyf2834fsuhf8934y89')); + $v->rule('requiredWithout', 'token', array('email', 'password')); + $v->rule('requiredWith', 'password', 'email'); + $this->assertTrue($v->validate()); + } + + public function testConditionallyRequiredAuthSampleTokenAltSyntax() + { + $v = new Validator(array('token' => 'ajkdhieyf2834fsuhf8934y89')); + $v->rules(array( + 'requiredWithout' => array( + array('token', array('email', 'password')) + ), + 'requiredWith' => array( + array('password', array('email')) + ) + )); + $this->assertTrue($v->validate()); + } + + public function testConditionallyRequiredAuthSampleEmailPassword() + { + $v = new Validator(array('email' => 'test@test.com', 'password' => 'mypassword')); + $v->rule('requiredWithout', 'token', array('email', 'password')); + $v->rule('requiredWith', 'password', 'email'); + $this->assertTrue($v->validate()); + } + + public function testConditionallyRequiredAuthSampleEmailPasswordAltSyntax() + { + $v = new Validator(array('email' => 'test@test.com', 'password' => 'mypassword')); + $v->rules(array( + 'requiredWithout' => array( + array('token', array('email', 'password')) + ), + 'requiredWith' => array( + array('password', array('email')) + ) + )); + $this->assertTrue($v->validate()); + } + /** * @dataProvider dataProviderFor_testError */