Skip to content

Commit 4e87f40

Browse files
committed
Add OR validation
1 parent 857b365 commit 4e87f40

File tree

6 files changed

+102
-32
lines changed

6 files changed

+102
-32
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Tests\Fixtures\Validation;
4+
5+
class LogicalOr extends \PHPFUI\ORM\Validator
6+
{
7+
public static array $validators = [
8+
'alpha' => ['alpha|starts_with:/', 'required', 'ends_with:4'],
9+
];
10+
}

Tests/Unit/ValidationTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,24 @@ public function testNumber() : void
811811
$this->assertNotEmpty($validator->getErrors());
812812
}
813813

814+
public function testOr() : void
815+
{
816+
$crud = new \Tests\Fixtures\Record\Alpha();
817+
$validator = new \Tests\Fixtures\Validation\LogicalOr($crud);
818+
819+
$crud->alpha = '/1234';
820+
$validator->validate();
821+
$errors = $validator->getErrors();
822+
$this->assertEmpty($errors);
823+
824+
$crud->alpha = '1234';
825+
$validator->validate();
826+
$errors = $validator->getErrors();
827+
$this->assertNotEmpty($errors);
828+
$this->assertContains('1234 is not characters only', $errors['alpha']);
829+
$this->assertContains('1234 does not start with one of (/) of the same case', $errors['alpha']);
830+
}
831+
814832
public function testRequired() : void
815833
{
816834
$crud = new \Tests\Fixtures\Record\Required();

src/PHPFUI/ORM/Schema/Field.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ public function __construct(\PHPFUI\ORM\PDOInstance $pdo, array $fields, bool $a
3030
return;
3131
}
3232
// SQLite
33-
$this->name = $fields['name']; /** @phpstan-ignore-line */
34-
$this->type = \strtolower($fields['type']); /** @phpstan-ignore-line */
35-
$this->nullable = ! (bool)$fields['notnull']; /** @phpstan-ignore-line */
36-
$this->defaultValue = $fields['dflt_value']; /** @phpstan-ignore-line */
37-
$this->primaryKey = (bool)$fields['pk']; /** @phpstan-ignore-line */
38-
$this->autoIncrement = $autoIncrement && $this->primaryKey; /** @phpstan-ignore-line */
33+
$this->name = $fields['name'];
34+
$this->type = \strtolower($fields['type']);
35+
$this->nullable = ! (bool)$fields['notnull'];
36+
$this->defaultValue = $fields['dflt_value'];
37+
$this->primaryKey = (bool)$fields['pk'];
38+
$this->autoIncrement = $autoIncrement && $this->primaryKey;
3939
}
4040
}

src/PHPFUI/ORM/Schema/Index.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ public function __construct(\PHPFUI\ORM\PDOInstance $pdo, array $fields)
2020
}
2121
else
2222
{
23-
$this->name = $fields['name']; /** @phpstan-ignore-line */
24-
$this->extra = $fields['sql'] ?? ''; /** @phpstan-ignore-line */
25-
$this->primaryKey = false; /** @phpstan-ignore-line */
23+
$this->name = $fields['name'];
24+
$this->extra = $fields['sql'] ?? '';
25+
$this->primaryKey = false;
2626
}
2727
}
2828
}

src/PHPFUI/ORM/Tool/Generate/CRUD.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,6 @@ protected function getLine(\PHPFUI\ORM\Schema\Field $field) : string
185185
break;
186186
}
187187

188-
if ('boolean' == \gettype($field->defaultValue))
189-
{
190-
$defaultValue = (int)$field->defaultValue;
191-
}
192-
193188
$retVal .= $this->line($allowNulls ? 'true' : 'false');
194189

195190
if (null !== $defaultValue)

src/PHPFUI/ORM/Validator.php

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@
9292
* You want the name to be unique for a specific type in the division: *unique:type,shoes,division*
9393
* You want the name to be unique for a specific type and division: *unique:type,shoes,division,10*
9494
*
95+
* ## OR Operator
96+
* You can validate a field if any one of the validators passes. Use the vertical bar (|) to separate validators. If one of the validators passes, then the the field is valid.
97+
*
98+
* **Example:**
99+
* website|starts_with:/ will validate a fully qualified http url, or a root relative url.
100+
*
95101
* ## Optional Validation
96102
* You may need to do additional checks for a specific record type. A second parameter can be passed to the contructor which would represent the original values of the record.
97103
*
@@ -167,9 +173,9 @@ public function validate(string $optionalMethod = '') : bool
167173
/**
168174
* Gets the errors for a value with the record definition and associated validators
169175
*
170-
* @param array<int, array<mixed>> $fieldDefinitions
176+
* @param array<int, array<string>> $fieldDefinitions
171177
*
172-
* @return array of errors of translated text
178+
* @return array<string> of errors of translated text
173179
*/
174180
private function getFieldErrors(mixed $value, array $validators, array $fieldDefinitions) : array
175181
{
@@ -203,35 +209,40 @@ private function getFieldErrors(mixed $value, array $validators, array $fieldDef
203209
return $errors;
204210
}
205211

212+
$orErrors = [];
213+
206214
foreach ($validators as $validator)
207215
{
208-
$parts = \explode(':', (string)$validator);
209-
210-
$parameters = [];
216+
// Implements OR logic, any rule passes, the whole rule passes
217+
$orValidators = \explode('|', (string)$validator);
211218

212-
if (\count($parts) > 1)
219+
if (\count($orValidators) > 1)
213220
{
214-
$parameters = \explode(',', $parts[1]);
215-
}
216-
$validator = $parts[0];
217-
$method = 'validate_' . $validator;
218-
219-
if (\method_exists($this, $method))
220-
{
221-
$error = $this->{$method}($value, $parameters, $fieldDefinitions);
221+
$orErrors = [];
222222

223-
if ($error)
223+
foreach ($orValidators as $validator)
224224
{
225-
$errors[] = $error;
225+
$error = $this->validateRule($validator, $value, $fieldDefinitions);
226+
227+
if ($error)
228+
{
229+
$orErrors = \array_merge($orErrors, $error);
230+
}
231+
else
232+
{
233+
$orErrors = [];
234+
235+
break;
236+
}
226237
}
227238
}
228239
else
229240
{
230-
throw new \Exception("Validator {$validator} (validate_{$validator} method) not found in class " . self::class);
241+
$errors = \array_merge($errors, $this->validateRule($validator, $value, $fieldDefinitions));
231242
}
232243
}
233244

234-
return $errors;
245+
return \array_merge($errors, $orErrors);
235246
}
236247

237248
/**
@@ -873,4 +884,40 @@ private function validate_year_month(mixed $value, array $parameters, array $fie
873884

874885
return \checkdate((int)($parts[$month] ?? 0), $day, (int)($parts[$year] ?? 0)) ? '' : \PHPFUI\ORM::trans('.validator.year_month', ['value' => $value]);
875886
}
887+
888+
/**
889+
* Validate one rule.
890+
*
891+
* @return array<string> of errors of translated text
892+
*/
893+
private function validateRule(string $validator, mixed $value, array $fieldDefinitions) : array
894+
{
895+
$parts = \explode(':', (string)$validator);
896+
897+
$parameters = $errors = [];
898+
899+
if (\count($parts) > 1)
900+
{
901+
$parameters = \explode(',', $parts[1]);
902+
}
903+
$validator = $parts[0];
904+
905+
$method = 'validate_' . $validator;
906+
907+
if (\method_exists($this, $method))
908+
{
909+
$error = $this->{$method}($value, $parameters, $fieldDefinitions);
910+
911+
if ($error)
912+
{
913+
$errors[] = $error;
914+
}
915+
}
916+
else
917+
{
918+
throw new \Exception("Validator {$validator} (validate_{$validator} method) not found in class " . self::class);
919+
}
920+
921+
return $errors;
922+
}
876923
}

0 commit comments

Comments
 (0)