metadata = $metadata; $this->fieldUtil = $fieldUtil; $this->factory = $factory; } /** * Process validation. * * @param Entity $entity An entity. * @param ?StdClass $data Raw request payload data. * @param ?FieldValidationParams $params Validation additional parameters. * * @throws BadRequest If data is not valid. */ public function process(Entity $entity, ?StdClass $data = null, ?FieldValidationParams $params = null): void { $dataIsSet = $data !== null; if (!$data) { $data = $data ?? (object) []; } if (!$params) { $params = new FieldValidationParams(); } $fieldList = $this->fieldUtil->getEntityTypeFieldList($entity->getEntityType()); $skipFieldList = $params->getSkipFieldList(); foreach ($fieldList as $field) { if (in_array($field, $skipFieldList)) { continue; } if ( !$entity->isNew() && $dataIsSet && !$this->isFieldSetInData($entity->getEntityType(), $field, $data) ) { continue; } $this->processField($entity, $field, $params, $data); } } /** * Check a specific field for a specific validation type. */ public function check(Entity $entity, string $field, string $type, ?StdClass $data = null): bool { if (!$data) { $data = $data ?? (object) []; } $entityType = $entity->getEntityType(); $fieldType = $this->fieldUtil->getEntityTypeFieldParam($entityType, $field, 'type'); $validationValue = $this->fieldUtil->getEntityTypeFieldParam($entityType, $field, $type); $mandatoryValidationList = $this->metadata->get(['fields', $fieldType, 'mandatoryValidationList'], []); if (!in_array($type, $mandatoryValidationList)) { if (is_null($validationValue) || $validationValue === false) { return true; } } $result = $this->processFieldCheck($entityType, $type, $entity, $field, $validationValue); if (!$result) { return false; } $resultRaw = $this->processFieldRawCheck($entityType, $type, $data, $field, $validationValue); if (!$resultRaw) { return false; } return true; } private function processField(Entity $entity, string $field, FieldValidationParams $params, StdClass $data): void { $entityType = $entity->getEntityType(); $fieldType = $this->fieldUtil->getEntityTypeFieldParam($entityType, $field, 'type'); $validationList = $this->metadata->get(['fields', $fieldType, 'validationList'], []); $mandatoryValidationList = $this->metadata->get(['fields', $fieldType, 'mandatoryValidationList'], []); foreach ($validationList as $type) { $value = $this->fieldUtil->getEntityTypeFieldParam($entityType, $field, $type); if (is_null($value) && !in_array($type, $mandatoryValidationList)) { continue; } if (in_array($field, $params->getTypeSkipFieldList($type))) { continue; } $result = $this->check($entity, $field, $type, $data); if (!$result) { throw new BadRequest("Not valid data. Field: {$field}, validation type: {$type}."); } } } private function processFieldCheck( string $entityType, string $type, Entity $entity, string $field, $validationValue ): bool { $checker = $this->getFieldTypeChecker($entityType, $field); if (!$checker) { return true; } $methodName = 'check' . ucfirst($type); if (!method_exists($checker, $methodName)) { return true; } return $checker->$methodName($entity, $field, $validationValue); } private function processFieldRawCheck( string $entityType, string $type, StdClass $data, string $field, $validationValue ): bool { $checker = $this->getFieldTypeChecker($entityType, $field); if (!$checker) { return true; } $methodName = 'rawCheck' . ucfirst($type); if (!method_exists($checker, $methodName)) { return true; } return $checker->$methodName($data, $field, $validationValue); } private function getFieldTypeChecker(string $entityType, string $field): ?object { $key = $entityType . '_' . $field; if (!array_key_exists($key, $this->checkerCache)) { $this->loadFieldTypeChecker($entityType, $field); } return $this->checkerCache[$key]; } private function loadFieldTypeChecker(string $entityType, string $field): void { $key = $entityType . '_' . $field; if (!$this->factory->isCreatable($entityType, $field)) { $this->checkerCache[$key] = null; return; } $this->checkerCache[$key] = $this->factory->create($entityType, $field); } private function isFieldSetInData(string $entityType, string $field, StdClass $data): bool { $attributeList = $this->fieldUtil->getActualAttributeList($entityType, $field); $isSet = false; foreach ($attributeList as $attribute) { if (property_exists($data, $attribute)) { $isSet = true; break; } } return $isSet; } }