-
-
Notifications
You must be signed in to change notification settings - Fork 10
Validation
Each type generated by php-fhir contains a method called
_getValidationErrors
. This method recurses through all set fields on the type, running any / all defined validation
rules for each field.
This library generates a number of validator rules, but they are limited in scope to only the rules defined by the FHIR schema itself. See Generated Validators for a list.
Lets take a simple example with the R4 string-primitive type
As you can see, the default validation rule on this type requires that, if defined, its value must have a minimum length of 1:
<?php
class FHIRStringPrimitive implements PrimitiveTypeInterface
{
/* ... */
private const _FHIR_VALIDATION_RULES = [
self::FIELD_VALUE => [
ValueMinLengthRule::NAME => 1,
],
];
/* ... */
}
When we put this into practice:
<?php
use DCarbone\PHPFHIRGenerated\Versions\R4\Types\FHIRStringPrimitive;
$stringPrimitive = new FHIRStringPrimitive(value: '');
$errs = $stringPrimitive->_getValidationErrors();
echo "value: '{$stringPrimitive}'\n";
var_export($errs);
/*
value: ''
array (
'value' =>
array (
'value_min_length' => 'Field "value" on type "string-primitive" must be at least 1 characters long, but it is empty',
),
)
*/
Above, we created a string primitive with an empty value. The FHIR specification requires that all string values, if defined, must have at least one character in their value. The error map is telling us tha the "value" of the string primitive is not valid, as it is less than 1 characters long.
Lets try again with a "valid" value:
<?php
use DCarbone\PHPFHIRGenerated\Versions\R4\Types\FHIRStringPrimitive;
$stringPrimitive = new FHIRStringPrimitive(value: 'Steve');
$errs = $stringPrimitive->_getValidationErrors();
echo "value: '{$stringPrimitive}'\n";
var_export($errs);
/*
value: 'Steve'
array (
)
*/
As you can see, setting the value to "Steve" and running validations again results in an empty error map as the value "Steve" is at least one character long.
The above is a very simple example, executed directly on a primitive type. Let's see what it looks like with a more complex type:
<?php
use DCarbone\PHPFHIRGenerated\Versions\R4\Types\FHIRElement\FHIRContactDetail;
use DCarbone\PHPFHIRGenerated\Versions\R4\Types\FHIRResource\FHIRDomainResource\FHIRResearchDefinition;
$researchDefinition = new FHIRResearchDefinition(
contact: [
new FHIRContactDetail(name: 'Steve')
]
);
$errs = $researchDefinition->_getValidationErrors();
echo "value: '{$researchDefinition->getContact()[0]->getName()}'\n";
var_export($errs);
/*
value: 'Steve'
array (
)
*/
Here, we create a ResearchDefinition resource with a singular contact with the name "Steve". Steve is a non-empty value, thus it passes validation.
Let's try with an "empty" value for the contact's name:
use DCarbone\PHPFHIRGenerated\Versions\R4\Types\FHIRElement\FHIRContactDetail;
use DCarbone\PHPFHIRGenerated\Versions\R4\Types\FHIRResource\FHIRDomainResource\FHIRResearchDefinition;
$researchDefinition = new FHIRResearchDefinition(
contact: [
new FHIRContactDetail(name: '')
]
);
$errs = $researchDefinition->_getValidationErrors();
echo "value: '{$researchDefinition->getContact()[0]->getName()}'\n";
var_export($errs);
/*
value: ''
array (
'contact.0.name.value.value' =>
array (
'value_min_length' => 'Field "value" on type "string-primitive" must be at least 1 characters long, but it is empty',
),
)
*/
As you can see, we now have a non-empty error map. The key is the path to the specific field with the value that failed validation, the validator rule that tripped, and a message describing the issue.
This page is manually updated, and may be out of date depending on when you read this.. You can always find a comprehensive list of validators in the Validation/Rules namespace.
This method returns a map with field path keys, and a map of validator names to error messages per field path. There are a number of validators that are generated with the library, and you may define or override those as you see fit.
Asserts that a given collection field has no more than the specified number of elements.
This rule expects the constraint to either be -1, indicating it may occur an unlimited number of times, or an integer greater or equal to 1.
Asserts that a given collection field is of a specific minimum length.
This rule expects the constraint to be an integer greater than or equal to 1.
Asserts that a given string value is no more than x characters long.
This rule expects the constraint to either be -1, indicating it has no maximum length, or an integer greater than or equal to 1.
Asserts that a given string value is at least x characters long.
This rule expects the constraint to be an integer greater than or equal to 1.
Asserts that a given value is within the expected list of values.
This rule expects the constraint to be an array of allowed string values. Comparison is case-sensitive.
Asserts that a given string value matches the specified pattern.
This rule expects the constraint to be a PHP PCRE Expression String.
As described in issue #150, the regular expression pattern
defined by the R4 and
R4B base64Binary
type is not particularly efficient. It results in a stack
overflow when attempting to parse "large" values.
The expression was updated to a more performant one with the R5 specification, but for compatibility reasons this library retains version-specific type validation constraints.
To create or override a validation rule, you simply need to define a class that implements the RuleInterface.
Once created, you must "register" the rule with the
Validator
class by calling the Validator::setRule($rule);
method.
Lets create a useless validation rule that allows us to limit string values to either === or !== Steve
:
<?php
use DCarbone\PHPFHIRGenerated\Types\TypeInterface;
use DCarbone\PHPFHIRGenerated\Validation\RuleInterface;
use DCarbone\PHPFHIRGenerated\Validation\Validator;
use DCarbone\PHPFHIRGenerated\Versions\R4\Types\FHIRStringPrimitive;
// First, create rule class
class OnlySteves implements RuleInterface
{
public const NAME = 'only_steves';
public function getName(): string
{
return self::NAME;
}
public function getDescription(): string
{
return 'Only Steves allowed.';
}
public function assert(TypeInterface $type, string $field, mixed $constraint, mixed $value): null|string
{
$isSteve = $value === 'Steve';
if ($isSteve && !$constraint) {
return sprintf('Field "%s" on type "%s" must not be Steve.', $field, $type->_getFHIRTypeName());
} else if (!$isSteve && $constraint) {
return sprintf('Field "%s" on type "%s" must be Steve.', $field, $type->_getFHIRTypeName());
}
return null;
}
}
// Next, register with validator
Validator::setRule(new OnlySteves());
// Then, during type initialization, register a new rule for the "value" field with a `true` constraint
// to require that the "value" field only ever equal "Steve":
$stringPrimitive = new FHIRStringPrimitive(value: 'Steve');
$stringPrimitive->_setFieldValidationRule('value', OnlySteves::NAME, true);
$errs = $stringPrimitive->_getValidationErrors();
echo "value: {$stringPrimitive}\n";
var_export($errs);
echo "\n\n";
// Since we created our rule with support for inversion, we can re-use it to require that the value must be
// anything other than "Steve" by passing `false` to the constraint:
$stringPrimitive = new FHIRStringPrimitive(value: 'Steve');
$stringPrimitive->_setFieldValidationRule('value', OnlySteves::NAME, false);
$errs = $stringPrimitive->_getValidationErrors();
echo "value: {$stringPrimitive}\n";
var_export($errs);