Compare commits

..

No commits in common. "master" and "version/2.x-dev" have entirely different histories.

18 changed files with 285 additions and 719 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.svn

View file

@ -1,32 +0,0 @@
workspace:
base: /woodpecker
path: package
matrix:
include:
- FLOW_VERSION: 6.3
PHP_VERSION: 7.4
- FLOW_VERSION: 7.3
PHP_VERSION: 7.4
- FLOW_VERSION: 7.3
PHP_VERSION: 8.2
- FLOW_VERSION: 8.2
PHP_VERSION: 8.2
pipeline:
functional-tests:
image: thecodingmachine/php:${PHP_VERSION}-v4-cli
environment:
# Enable the PDO_SQLITE extension
- "PHP_EXTENSION_PDO_SQLITE=1"
- "FLOW_VERSION=${FLOW_VERSION}"
- "NEOS_BUILD_DIR=/woodpecker/Build-${FLOW_VERSION}"
commands:
- "sudo mkdir $NEOS_BUILD_DIR"
- "sudo chown -R docker:docker $NEOS_BUILD_DIR"
- "cd $NEOS_BUILD_DIR"
- "composer create-project --no-install neos/flow-base-distribution:^$FLOW_VERSION ."
- "composer config repositories.repo-name path /woodpecker/package"
- "composer remove --dev --no-update neos/behat || composer remove --no-update neos/behat"
- "composer require digicomp/settingvalidator:@dev"
- "bin/phpunit --configuration Build/BuildEssentials/PhpUnit/FunctionalTests.xml Packages/Application/DigiComp.SettingValidator/Tests/Functional"

46
.woodpecker/test.yml Normal file
View file

@ -0,0 +1,46 @@
workspace:
base: /woodpecker
path: package
matrix:
FLOW_VERSION:
- 4.3
- 5.3
pipeline:
functional-tests:
image: thecodingmachine/php:7.4-v4-cli
environment:
# Enable the PDO_SQLITE extension
- "PHP_EXTENSION_PDO_SQLITE=1"
- "FLOW_VERSION=${FLOW_VERSION}"
- "NEOS_BUILD_DIR=/woodpecker/Build-${FLOW_VERSION}"
commands:
- "sudo mkdir $NEOS_BUILD_DIR"
- "sudo chown -R docker:docker $NEOS_BUILD_DIR"
- "cd $NEOS_BUILD_DIR"
- "if [ \"$FLOW_VERSION\" = \"4.3\" ]; then sudo composer self-update --1; fi"
- "composer create-project --no-install neos/flow-base-distribution:^$FLOW_VERSION ."
- "composer config repositories.repo-name path /woodpecker/package"
- "composer remove --dev neos/behat"
- "if [ \"$FLOW_VERSION\" = \"4.3\" ]; then composer require typo3fluid/fluid:2.4.3; fi"
- "composer require digicomp/settingvalidator:@dev"
- |
if [ "$FLOW_VERSION" = "4.3" ]; then
echo 'Neos:
Flow:
persistence:
backendOptions:
dbname: "flow_functional_testing"
error:
errorHandler:
exceptionalErrors:
0: "%E_USER_ERROR%"
1: "%E_RECOVERABLE_ERROR%"
2: ~
3: "%E_NOTICE%"
4: "%E_USER_WARNING%"
5: "%E_USER_NOTICE%"
6: "%E_STRICT%"' > Configuration/Testing/Settings.yaml
fi
- "bin/phpunit --configuration Build/BuildEssentials/PhpUnit/FunctionalTests.xml Packages/Application/DigiComp.SettingValidator/Tests/Functional"

View file

@ -1,14 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [3.0.1] - 2020-09-09
### Changed
- dependency to Flow ^6.3
## [3.0.0] - 2020-08-31
Start of the changelog.

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator;
/*
@ -16,27 +14,32 @@ namespace DigiComp\SettingValidator;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Core\Bootstrap;
use Neos\Flow\Package\Package as NeosFlowPackagePackage;
use Neos\Flow\Package\Package as BasePackage;
/**
* Package base class of the DigiComp.SettingValidator package.
*/
class Package extends NeosFlowPackagePackage
class Package extends BasePackage
{
const CONFIGURATION_TYPE_VALIDATION = 'Validation';
/**
* @inheritDoc
* @param Bootstrap $bootstrap
*/
public function boot(Bootstrap $bootstrap): void
public function boot(Bootstrap $bootstrap)
{
parent::boot($bootstrap);
$dispatcher = $bootstrap->getSignalSlotDispatcher();
$dispatcher->connect(
ConfigurationManager::class,
'configurationManagerReady',
function (ConfigurationManager $configurationManager): void {
$configurationManager->registerConfigurationType('Validation');
function (ConfigurationManager $configurationManager) {
$configurationManager->registerConfigurationType(
static::CONFIGURATION_TYPE_VALIDATION,
ConfigurationManager::CONFIGURATION_PROCESSING_TYPE_DEFAULT,
true
);
}
);
}

View file

@ -1,152 +0,0 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Validation\Validator;
/*
* This file is part of the DigiComp.SettingValidator package.
*
* (c) digital competence
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/
use Neos\Eel\EelEvaluatorInterface;
use Neos\Eel\Exception as NeosEelException;
use Neos\Eel\Utility;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\ObjectManagement\DependencyInjection\DependencyProxy;
use Neos\Flow\Validation\Exception\InvalidValidationConfigurationException;
use Neos\Flow\Validation\Exception\NoSuchValidatorException;
use Neos\Flow\Validation\Validator\AbstractValidator;
use Neos\Flow\Validation\ValidatorResolver;
class ConditionalValidator extends AbstractValidator
{
/**
* @Flow\Inject
* @var EelEvaluatorInterface
*/
protected $eelEvaluator;
/**
* @Flow\Inject
* @var ValidatorResolver
*/
protected $validatorResolver;
/**
* @inheritDoc
*/
protected $supportedOptions = [
'conditions' => [[], 'List of entries with "condition" (eel expression) and "validators" (list of validators).', 'array', true],
'fallbackValidators' => [[], 'List of validators that is used if no condition matched.', 'array'],
'validationGroups' => [['Default'], 'Same as "Validation Groups" of Flow Framework', 'array'],
];
/**
* @inheritDoc
* @throws InvalidValidationConfigurationException
* @throws NeosEelException
* @throws NoSuchValidatorException
*/
protected function isValid($value): void
{
$validatorConfigs = [];
$hasMatch = false;
foreach ($this->options['conditions'] as $condition) {
if ($this->eelEvaluator instanceof DependencyProxy) {
$this->eelEvaluator->_activateDependency();
}
if (!Utility::evaluateEelExpression($condition['condition'], $this->eelEvaluator, ['this' => $value])) {
continue;
}
$hasMatch = true;
foreach ($condition['validators'] as $validator => $options) {
if ($options === null) {
continue;
}
$validatorConfigs[] = [
'validator' => $validator,
'options' => $options,
];
}
}
if (!$hasMatch) {
foreach ($this->options['fallbackValidators'] as $validator => $options) {
if ($options === null) {
continue;
}
$validatorConfigs[] = [
'validator' => $validator,
'options' => $options,
];
}
}
foreach ($validatorConfigs as $validatorConfig) {
if (!$this->doesValidationGroupsMatch($validatorConfig)) {
continue;
}
$this->handleValidationGroups($validatorConfig);
$validator = $this->validatorResolver->createValidator(
$validatorConfig['validator'],
$validatorConfig['options']
);
if ($validator === null) {
throw new InvalidValidationConfigurationException(
\sprintf(
'Validator "%s" could not be resolved. Check your Validation.yaml',
$validatorConfig['validator']
),
1402326139
);
}
$this->getResult()->merge($validator->validate($value));
}
}
/**
* Check whether at least one configured group does match, if any is configured.
*
* @param array $validatorConfig
* @return bool
*/
protected function doesValidationGroupsMatch(array $validatorConfig): bool
{
return !isset($validatorConfig['options']['validationGroups'])
|| \array_intersect(
$validatorConfig['options']['validationGroups'],
$this->options['validationGroups']
) !== [];
}
/**
* Add validation groups for recursion if necessary.
*
* @param array $validatorConfig
*/
protected function handleValidationGroups(array &$validatorConfig): void
{
if (\in_array($validatorConfig['validator'], ['DigiComp.SettingValidator:Settings', 'DigiComp.SettingValidator:Conditional', 'DigiComp.SettingValidator:Properties', 'Neos.Flow:Collection'])) {
$validatorConfig['options']['validationGroups'] = $this->options['validationGroups'];
} elseif (isset($validatorConfig['options']['validationGroups'])) {
unset($validatorConfig['options']['validationGroups']);
}
}
}

View file

@ -1,119 +0,0 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Validation\Validator;
/*
* This file is part of the DigiComp.SettingValidator package.
*
* (c) digital competence
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Validation\Exception\InvalidValidationConfigurationException;
use Neos\Flow\Validation\Exception\NoSuchValidatorException;
use Neos\Flow\Validation\Validator\AbstractValidator;
use Neos\Flow\Validation\ValidatorResolver;
use Neos\Utility\ObjectAccess;
class PropertiesValidator extends AbstractValidator
{
/**
* @Flow\Inject
* @var ValidatorResolver
*/
protected $validatorResolver;
/**
* @inheritDoc
*/
protected $supportedOptions = [
'validatorsForProperties' => [[], 'List of validators for properties. ', 'array', true],
'validationGroups' => [['Default'], 'Same as "Validation Groups" of Flow Framework', 'array'],
];
/**
* @inheritDoc
* @throws InvalidValidationConfigurationException
* @throws NoSuchValidatorException
*/
protected function isValid($value): void
{
$validatorConfigs = [];
foreach ($this->options['validatorsForProperties'] as $property => $validators) {
foreach ($validators as $validator => $options) {
if ($options === null) {
continue;
}
$validatorConfigs[] = [
'validator' => $validator,
'options' => $options,
'property' => $property,
];
}
}
foreach ($validatorConfigs as $validatorConfig) {
if (!$this->doesValidationGroupsMatch($validatorConfig)) {
continue;
}
$this->handleValidationGroups($validatorConfig);
$validator = $this->validatorResolver->createValidator(
$validatorConfig['validator'],
$validatorConfig['options']
);
if ($validator === null) {
throw new InvalidValidationConfigurationException(
\sprintf(
'Validator "%s" could not be resolved. Check your Validation.yaml',
$validatorConfig['validator']
),
1402326139
);
}
$this->getResult()->forProperty($validatorConfig['property'])->merge(
$validator->validate(ObjectAccess::getPropertyPath($value, $validatorConfig['property']))
);
}
}
/**
* Check whether at least one configured group does match, if any is configured.
*
* @param array $validatorConfig
* @return bool
*/
protected function doesValidationGroupsMatch(array $validatorConfig): bool
{
return !isset($validatorConfig['options']['validationGroups'])
|| \array_intersect(
$validatorConfig['options']['validationGroups'],
$this->options['validationGroups']
) !== [];
}
/**
* Add validation groups for recursion if necessary.
*
* @param array $validatorConfig
*/
protected function handleValidationGroups(array &$validatorConfig): void
{
if (\in_array($validatorConfig['validator'], ['DigiComp.SettingValidator:Settings', 'DigiComp.SettingValidator:Conditional', 'DigiComp.SettingValidator:Properties', 'Neos.Flow:Collection'])) {
$validatorConfig['options']['validationGroups'] = $this->options['validationGroups'];
} elseif (isset($validatorConfig['options']['validationGroups'])) {
unset($validatorConfig['options']['validationGroups']);
}
}
}

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Validation\Validator;
/*
@ -15,11 +13,13 @@ namespace DigiComp\SettingValidator\Validation\Validator;
*/
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Reflection\ReflectionService;
use Neos\Flow\Validation\Exception\InvalidValidationConfigurationException;
use Neos\Flow\Validation\Exception\InvalidValidationOptionsException;
use Neos\Flow\Validation\Exception\NoSuchValidatorException;
use Neos\Flow\Validation\Validator\AbstractValidator;
use Neos\Flow\Validation\ValidatorResolver;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling;
/**
@ -34,77 +34,67 @@ class SettingsValidator extends AbstractValidator
protected $validatorResolver;
/**
* @Flow\InjectConfiguration(type="Validation")
* @var array
* @var ConfigurationManager
*/
protected array $validations;
protected $configurationManager;
/**
* @inheritDoc
* @Flow\Inject
* @deprecated
* @var ReflectionService
*/
protected $reflectionService;
/**
* @var array
*/
protected $supportedOptions = [
'name' => ['', 'Name of the setting array to use', 'string'],
'validationGroups' => [['Default'], 'Same as "Validation Groups" of Flow Framework', 'array'],
'name' => ['', 'Set the name of the setting-array to use', 'string', false],
'validationGroups' => [
['Default'],
'Same as "Validation Groups" of Flow Framework. Defines the groups to execute.',
'array',
false,
],
];
/**
* @inheritDoc
* @var array
*/
protected $validations;
/**
* @param ConfigurationManager $configurationManager
*/
public function injectConfigurationManager(ConfigurationManager $configurationManager)
{
$this->configurationManager = $configurationManager;
$this->validations = $this->configurationManager->getConfiguration('Validation');
}
/**
* Check if $value is valid. If it is not valid, needs to add an error
* to Result.
*
* @param mixed $value
*
* @throws InvalidValidationOptionsException
* @throws InvalidValidationConfigurationException
* @throws NoSuchValidatorException
*/
protected function isValid($value): void
protected function isValid($value)
{
$validations = $this->validations;
// TODO: feature idea - we could extend the library to automatically be part of the base conjunction validator
$name = $this->options['name'] !== '' ? $this->options['name'] : TypeHandling::getTypeForValue($value);
if (!isset($validations[$name])) {
$name = $this->options['name'] ? $this->options['name'] : TypeHandling::getTypeForValue($value);
if (! isset($this->validations[$name])) {
throw new InvalidValidationOptionsException(
'The name "' . $name . '" has not been defined in Validation.yaml!',
'The name ' . $name . ' has not been defined in Validation.yaml!',
1397821438
);
}
// @deprecated - converts old "self" to new structure
if (isset($validations[$name]['self'])) {
foreach ($validations[$name]['self'] as $validator => $options) {
if (isset($validations[$name][$validator])) {
throw new \RuntimeException('The validator "' . $validator . '" is already defined on parent level.', 1725000364);
}
$validations[$name][$validator] = $options;
}
$config = $this->getConfigForName($name);
unset($validations[$name]['self']);
}
// @deprecated - converts old "properties" to new structure
if (isset($validations[$name]['properties'])) {
if (isset($validations[$name]['DigiComp.SettingValidator:Properties'])) {
throw new \RuntimeException('The validator "DigiComp.SettingValidator:Properties" is already defined on parent level.', 1725000396);
}
$validations[$name]['DigiComp.SettingValidator:Properties'] = [
'validatorsForProperties' => $validations[$name]['properties'],
];
unset($validations[$name]['properties']);
}
$validatorConfigs = [];
foreach ($validations[$name] as $validator => $options) {
if ($options === null) {
continue;
}
$validatorConfigs[] = [
'validator' => $validator,
'options' => $options,
];
}
foreach ($validatorConfigs as $validatorConfig) {
if (!$this->doesValidationGroupsMatch($validatorConfig)) {
foreach ($config as $validatorConfig) {
if (! $this->doesValidationGroupsMatch($validatorConfig)) {
continue;
}
@ -115,33 +105,77 @@ class SettingsValidator extends AbstractValidator
$validatorConfig['options']
);
if ($validator === null) {
if (! $validator) {
throw new InvalidValidationConfigurationException(
\sprintf(
'Validator "%s" could not be resolved. Check your Validation.yaml',
'Validator could not be resolved: "%s" Check your Validation.yaml',
$validatorConfig['validator']
),
1402326139
);
}
$this->getResult()->merge($validator->validate($value));
if (isset($validatorConfig['property'])) {
$this->result->forProperty($validatorConfig['property'])->merge(
$validator->validate(ObjectAccess::getPropertyPath($value, $validatorConfig['property']))
);
} else {
$this->result->merge($validator->validate($value));
}
}
}
/**
* @param string $name
*
* @return array
*/
protected function getConfigForName($name): array
{
$config = [];
if (isset($this->validations[$name]['self'])) {
foreach ($this->validations[$name]['self'] as $validator => &$validation) {
if ($validation === null) {
continue;
}
$newValidation['options'] = $validation;
$newValidation['validator'] = $validator;
$config[] = $newValidation;
}
}
if (isset($this->validations[$name]['properties'])) {
foreach ($this->validations[$name]['properties'] as $propertyName => &$validation) {
foreach ($validation as $validator => &$options) {
if ($options === null) {
continue;
}
$newValidation['property'] = $propertyName;
$newValidation['validator'] = $validator;
$newValidation['options'] = $options;
$config[] = $newValidation;
}
}
}
return $config;
}
/**
* Check whether at least one configured group does match, if any is configured.
*
* @param array $validatorConfig
*
* @return bool
*/
protected function doesValidationGroupsMatch(array $validatorConfig): bool
protected function doesValidationGroupsMatch(array &$validatorConfig)
{
return !isset($validatorConfig['options']['validationGroups'])
|| \array_intersect(
$validatorConfig['options']['validationGroups'],
$this->options['validationGroups']
) !== [];
if (
isset($validatorConfig['options']['validationGroups'])
&& \array_intersect($validatorConfig['options']['validationGroups'], $this->options['validationGroups']) === []
) {
return false;
}
return true;
}
/**
@ -149,12 +183,12 @@ class SettingsValidator extends AbstractValidator
*
* @param array $validatorConfig
*/
protected function handleValidationGroups(array &$validatorConfig): void
protected function handleValidationGroups(array &$validatorConfig)
{
if (\in_array($validatorConfig['validator'], ['DigiComp.SettingValidator:Settings', 'DigiComp.SettingValidator:Conditional', 'DigiComp.SettingValidator:Properties', 'Neos.Flow:Collection'])) {
$validatorConfig['options']['validationGroups'] = $this->options['validationGroups'];
} elseif (isset($validatorConfig['options']['validationGroups'])) {
if (isset($validatorConfig['options']['validationGroups']) && $validatorConfig['validator'] !== 'DigiComp.SettingValidator:Settings') {
unset($validatorConfig['options']['validationGroups']);
} elseif ($validatorConfig['validator'] === 'DigiComp.SettingValidator:Settings') {
$validatorConfig['options']['validationGroups'] = $this->options['validationGroups'];
}
}
}

View file

@ -8,15 +8,20 @@ DigiComp\SettingValidator\Tests\Functional\Fixtures\TestObject:
expectedValue: false
Grumble: ~
TrueValidator:
self:
BooleanValue:
expectedValue: true
DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsCustomObject:
self:
DigiComp.SettingValidator:Settings:
name: "GroupValidatorCustom"
name: 'GroupValidatorCustom'
DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsDefaultObject:
self:
DigiComp.SettingValidator:Settings:
name: "GroupValidatorDefault"
name: 'GroupValidatorDefault'
GroupValidatorDefault:
properties:
@ -30,9 +35,4 @@ GroupValidatorCustom:
BooleanValue:
expectedValue: false
validationGroups:
- "Custom"
TrueValidator:
self:
BooleanValue:
expectedValue: true
- 'Custom'

View file

@ -1,30 +1,37 @@
<?php
declare(strict_types=1);
namespace Neos\Flow\Core\Migrations;
/*
* This file is part of the DigiComp.SettingValidator package.
*
* (c) digital competence
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/
use DigiComp\SettingValidator\Package;
/**
* Restructures all Validation.yaml to new format
* Restructures Validation.yamls to new format
*/
class Version20170603120900 extends AbstractMigration
{
/**
* @return string
*/
public function getIdentifier(): string
public function getIdentifier()
{
return 'DigiComp.SettingValidator-20170603120900';
}
/**
* @inheritDoc
* @return void
*/
public function up(): void
public function up()
{
$this->processConfiguration(
'Validation',
function (array &$configuration): void {
Package::CONFIGURATION_TYPE_VALIDATION,
function (&$configuration) {
foreach ($configuration as $validatorName => &$validators) {
// guard that protects configuration, which has already the new format:
if (isset($validators['properties']) || isset($validators['self'])) {
@ -32,17 +39,15 @@ class Version20170603120900 extends AbstractMigration
}
$newConfiguration = ['properties' => [], 'self' => []];
foreach ($validators as $key => $validator) {
if (!isset($validator['validator']) || !isset($validator['options'])) {
$this->showWarning(
'The Validation.yaml files contained no validator or options for validation: '
. '"' . $validatorName . '.' . $key . '". It was not migrated.'
);
foreach ($validators as $key => &$validator) {
if (! isset($validator['validator']) || ! isset($validator['options'])) {
$this->showWarning('The Validation.yaml files contained no validator or options for ' .
'validation: "' . $validatorName . '.' . $key . '". It was not migrated.');
continue;
}
if (isset($validator['property'])) {
$newConfiguration['properties'][$validator['property']][$validator['validator']] =
$validator['options'];
$newConfiguration['properties'][$validator['property']][$validator['validator']]
= $validator['options'];
} else {
$newConfiguration['self'][$validator['validator']] = $validator['options'];
}

237
README.md
View file

@ -1,211 +1,46 @@
# DigiComp.SettingValidator
DigiComp.SettingValidator
-------------------------
This package allows configuring validators with a new configuration type.
## Introduction
This Package allows to configure Validators for your Action-Arguments or domain model properties to be set by a new
Yaml-File in your Configuration directory.
This package provides the `SettingsValidator` which uses the configuration type `Validation` to resolve the validators
that should be applied to the value. It distinguishes between validators that are applied to the value itself and its
properties.
Lets imagine you had this action-method:
## Resolving the validation configuration
/**
* @param Order $order
* @Flow\Validate(type="DigiComp.SettingValidator:Settings")
*/
public function createOrder($order) {...}
The `SettingsValidator` has an option `name`. If it is set, the name is used to resolve the validation configuration,
otherwise the type of the value is used, which is mainly useful for objects where the FQCN is used.
Then your Validation.yaml could look like this:
### Resolving by option `name`
SuperVendor\SuperPackage\Domain\Model\Order:
-
property: price
validator: NumberRange
options:
maximum: 20
minimum: 10
-
validator: SuperVendor.SuperPackage:SomeOtherValidator #validates the complete object
options: []
-
property: customer
validator: DigiComp.SettingValidator:Settings
options:
name: OrderCustomer
To resolve the validation configuration by name just use the option `name`.
OrderCustomer:
-
property: firstName
validator: StringLength
options:
minimum: 3
maximum: 20
```php
/**
* @Flow\Validate(type="DigiComp.SettingValidator:Settings", options={"name"="MyNamedValidator"})
* @var MyObject
*/
protected MyObject $myObject;
```
The `SettingsValidator` will search for an entry inside the `Validation.yaml` with that name.
As you see: Nesting is possible ;) That way you can easily construct flexible structures.
```yaml
MyNamedValidator:
...
```
### Resolving by type
To resolve the validation configuration by type just do not set the option `name`.
```php
/**
* @Flow\Validate(type="DigiComp.SettingValidator:Settings")
* @var MyObject
*/
protected MyObject $myObject;
```
The `SettingsValidator` will search for an entry inside the `Validation.yaml` with the FQCN of `MyObject`.
```yaml
My\Package\Domain\Model\MyObject:
...
```
## The validation configuration
### Difference between `self` and `properties`
`self` contains a map of validators that are applied to the value itself. `properties` contains a map with property
names of the value you would like to validate and each entry contains a map of validators that are applied to that
property.
```yaml
MyNamedValidator:
self:
...
properties:
myProperty1:
...
myProperty2:
...
```
### Configuring a validator
To configure a validator you use the type of the validator as key and the options as entries of that key. If the
validator has no options or all the default values are used, set an empty map as options.
```yaml
MyNamedValidator:
self:
'My.Package:SomeValidator':
myOption: "myOptionValue"
properties:
myProperty1:
'My.Package:SomeOtherValidator': {}
myProperty2:
'My.Package:SomeOtherValidator': {}
```
### Disable a validator
To disable a validator you need to set the options to `null`.
```yaml
MyNamedValidator:
self:
'My.Package:SomeValidator': ~
```
## Using the `SettingsValidator`
The `SettingsValidator` can be used to reduce the number of `@Flow\Validate` annotations and gives you the possibility
of overwriting existing validation configurations in other packages.
### Using on properties
Old PHP code:
```php
/**
* @Flow\Validate(type="My.Package:SomeValidator", options={"myOption"="myOptionValue"})
* @Flow\Validate(type="My.Package:SomeOtherValidator")
* @var MyObject
*/
protected MyObject $myObject;
```
New PHP code:
```php
/**
* @Flow\Validate(type="DigiComp.SettingValidator:Settings", options={"name"="MyNamedValidator"})
* @var MyObject
*/
protected MyObject $myObject;
```
New validation configuration:
```yaml
MyNamedValidator:
self:
'My.Package:SomeValidator':
myOption: "myOptionValue"
'My.Package:SomeOtherValidator': {}
```
### Using on actions
Old PHP code:
```php
/**
* @Flow\Validate(argumentName="myObject", type="My.Package:SomeValidator", options={"myOption"="myOptionValue"})
* @Flow\Validate(argumentName="myObject", type="My.Package:SomeOtherValidator")
* @param MyObject $myObject
*/
public function myAction(MyObject $myObject)
{
...
}
```
New PHP code:
```php
/**
* @Flow\Validate(argumentName="myObject", type="DigiComp.SettingValidator:Settings", options={"name"="MyNamedValidator"})
* @param MyObject $myObject
*/
public function myAction(MyObject $myObject)
{
...
}
```
New validation configuration:
```yaml
MyNamedValidator:
self:
'My.Package:SomeValidator':
myOption: "myOptionValue"
'My.Package:SomeOtherValidator': {}
```
### Using inside validator configurations
You can use the `SettingsValidator` inside the validator configuration to easily construct flexible structures.
```yaml
MyNamedValidator:
properties:
myProperty1:
'DigiComp.SettingValidator:Settings':
name: "MyOtherNamedValidator"
MyOtherNamedValidator:
self:
'My.Package:SomeOtherValidator': {}
```
## Providing an empty validator
It can be useful to provide an empty validator in code that is used by many projects. By doing so you can make sure that
a different validation is possible in any project.
```php
/**
* @Flow\Validate(argumentName="myObject", type="DigiComp.SettingValidator:Settings", options={"name"="MyNamedValidator"})
* @param MyObject $myObject
*/
public function myAction(MyObject $myObject)
{
...
}
```
```yaml
MyNamedValidator: {}
```
The SettingsValidator has an optional option: "name" - If you don't give one, it assumes your validation value is an
object and searches in Validation.yaml for the FQCN.

View file

@ -1,11 +1,11 @@
type: "dictionary"
type: dictionary
additionalProperties:
type: "dictionary"
type: dictionary
additionalProperties: false
properties:
type: "dictionary"
type: dictionary
additionalProperties: false
properties:
type: "dictionary"
type: dictionary
self:
type: "dictionary"
type: dictionary

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Tests\Functional\Fixtures;
use Neos\Flow\Annotations as Flow;
@ -11,18 +9,18 @@ class TestObject
/**
* @var bool
*/
protected bool $shouldBeTrue = true;
protected $shouldBeTrue = true;
/**
* @var bool
*/
protected bool $shouldBeFalse = true;
protected $shouldBeFalse = true;
/**
* @Flow\Validate(type="DigiComp.SettingValidator:Settings", options={"name"="TrueValidator"})
* @var bool
*/
protected bool $shouldBeTrueAndValidatedByAnnotation = false;
protected $shouldBeTrueAndValidatedByAnnotation = false;
/**
* @return bool

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Tests\Functional\Fixtures;
class TestValidationGroupsCustomObject
@ -9,12 +7,12 @@ class TestValidationGroupsCustomObject
/**
* @var bool
*/
protected bool $shouldBeTrue = false;
protected $shouldBeTrue = false;
/**
* @var bool
*/
protected bool $shouldBeFalse = true;
protected $shouldBeFalse = true;
/**
* @return bool

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Tests\Functional\Fixtures;
class TestValidationGroupsDefaultObject
@ -9,12 +7,12 @@ class TestValidationGroupsDefaultObject
/**
* @var bool
*/
protected bool $shouldBeTrue = false;
protected $shouldBeTrue = false;
/**
* @var bool
*/
protected bool $shouldBeFalse = true;
protected $shouldBeFalse = true;
/**
* @return bool

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Tests\Functional;
use DigiComp\SettingValidator\Tests\Functional\Fixtures\TestObject;
@ -9,71 +7,55 @@ use DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsCust
use DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsDefaultObject;
use DigiComp\SettingValidator\Validation\Validator\SettingsValidator;
use Neos\Flow\Tests\FunctionalTestCase;
use Neos\Flow\Validation\Exception\InvalidValidationConfigurationException;
use Neos\Flow\Validation\Exception\InvalidValidationOptionsException;
use Neos\Flow\Validation\Exception\NoSuchValidatorException;
use Neos\Flow\Validation\ValidatorResolver;
class SettingsValidatorTest extends FunctionalTestCase
{
/**
* @test
* @throws InvalidValidationOptionsException
*/
public function ifNoNameIsGivenClassNameIsUsed(): void
public function ifNoNameIsGivenClassNameIsUsed()
{
$result = $this->objectManager->get(SettingsValidator::class)->validate(new TestObject());
self::assertTrue($result->hasErrors());
self::assertCount(1, $result->getFlattenedErrors());
self::assertCount(1, $result->forProperty('shouldBeFalse')->getErrors());
$validator = $this->objectManager->get(SettingsValidator::class);
$result = $validator->validate(new TestObject());
$this->assertTrue($result->hasErrors());
$this->assertCount(1, $result->getFlattenedErrors());
$this->assertCount(1, $result->forProperty('shouldBeFalse')->getErrors());
}
/**
* @test
* @throws InvalidValidationConfigurationException
* @throws InvalidValidationOptionsException
* @throws NoSuchValidatorException
*/
public function conjunctionValidationWorksAsExpected(): void
public function conjunctionValidationWorksAsExpected()
{
$result = $this->objectManager
->get(ValidatorResolver::class)
->getBaseValidatorConjunction(TestObject::class)
->validate(new TestObject());
self::assertTrue($result->hasErrors());
self::assertCount(1, $result->getFlattenedErrors());
self::assertCount(1, $result->forProperty('shouldBeTrueAndValidatedByAnnotation')->getErrors());
$validatorResolver = $this->objectManager->get(ValidatorResolver::class);
$validator = $validatorResolver->getBaseValidatorConjunction(TestObject::class);
$result = $validator->validate(new TestObject());
$this->assertTrue($result->hasErrors());
$this->assertCount(1, $result->getFlattenedErrors());
}
/**
* @test
* @throws InvalidValidationOptionsException
*/
public function defaultValidationGroupWorks(): void
public function defaultValidationGroupWorks()
{
$result = $this->objectManager
->get(SettingsValidator::class, ['validationGroups' => ['Default']])
->validate(new TestValidationGroupsDefaultObject());
self::assertTrue($result->hasErrors(), 'No errors for validation group "Default"');
self::assertCount(1, $result->getFlattenedErrors(), 'Got a non expected number of errors for group "Default"');
self::assertCount(1, $result->forProperty('shouldBeTrue')->getErrors(), 'Got no error for property');
$validator = $this->objectManager->get(SettingsValidator::class, ['validationGroups' => ['Default']]);
$result = $validator->validate(new TestValidationGroupsDefaultObject());
$this->assertTrue($result->hasErrors(), 'No Errors for validation group "Default"');
$this->assertCount(1, $result->getFlattenedErrors(), 'Got a non expected number of errors for group "Default"');
$this->assertCount(1, $result->forProperty('shouldBeTrue')->getErrors(), 'Got no error for property');
}
/**
* @test
* @throws InvalidValidationOptionsException
*/
public function customValidationGroupWorks(): void
public function customValidationGroupWorks()
{
$result = $this->objectManager
->get(SettingsValidator::class, ['validationGroups' => ['Custom']])
->validate(new TestValidationGroupsCustomObject());
self::assertTrue($result->hasErrors(), 'No errors for validation group "Custom"');
self::assertCount(1, $result->getFlattenedErrors(), 'Got a non expected number of errors for group "Custom"');
self::assertCount(1, $result->forProperty('shouldBeFalse')->getErrors(), 'Got no error for property');
$validator = $this->objectManager->get(SettingsValidator::class, ['validationGroups' => ['Custom']]);
$result = $validator->validate(new TestValidationGroupsCustomObject());
$this->assertTrue($result->hasErrors(), 'No Errors for validation group "Custom"');
$this->assertCount(1, $result->getFlattenedErrors(), 'Got a non expected number of errors for group "Custom"');
$this->assertCount(1, $result->forProperty('shouldBeFalse')->getErrors(), 'Got no error for property');
}
}

View file

@ -1,55 +1,40 @@
{
"name": "digicomp/settingvalidator",
"description": "Just a Neos\\Flow Validator resolving other Validators with Configuration/Validation.yaml",
"type": "neos-package",
"description": "Just a Neos\\Flow Validator resolving other Validators with Configuration/Validation.yaml",
"authors": [
{
"name": "Ferdinand Kuhl",
"email": "f.kuhl@digital-competence.de",
"homepage": "http://www.digital-competence.de",
"role": "Developer"
}
],
"license": "MIT",
"homepage": "https://github.com/fcool/DigiComp.SettingValidator",
"keywords": [
"Neos",
"Flow",
"validation"
],
"homepage": "https://github.com/digital-competence/DigiComp.SettingValidator",
"license": "MIT",
"authors": [
{
"name": "Ferdinand Kuhl",
"email": "f.kuhl@digital-competence.de",
"homepage": "https://www.digital-competence.de",
"role": "Developer"
}
],
"require": {
"php": ">=7.4.0",
"neos/flow": "^6.3.5 || ^7.0 || ^8.0"
},
"require-dev": {
"mikey179/vfsstream": "^1.6.1",
"neos/buildessentials": "^7.0.0",
"phpunit/phpunit": "~8.5",
"vimeo/psalm": "~4.22.0"
"neos/flow": "~4.1|^5.3"
},
"autoload": {
"psr-4": {
"DigiComp\\SettingValidator\\": "Classes/"
"DigiComp\\SettingValidator\\": "Classes"
}
},
"autoload-dev": {
"psr-4": {
"DigiComp\\SettingValidator\\Tests\\": "Tests/"
"DigiComp\\SettingValidator\\Tests\\": "Tests"
}
},
"config": {
"sort-packages": true,
"platform-check": true
},
"extra": {
"branch-alias": {
"dev-develop": "3.2.x-dev",
"dev-version/2.x-dev": "2.1.x-dev",
"dev-develop": "2.0.x-dev",
"dev-version/1.x-dev": "1.0.x-dev"
},
"neos": {
"package-key": "DigiComp.SettingValidator"
},
"applied-flow-migrations": [
"Inwebs.Basket-201409170938",
"TYPO3.FLOW3-201201261636",
@ -82,9 +67,7 @@
"Neos.Flow-20170125103800",
"Neos.Flow-20170127183102",
"DigiComp.SettingValidator-20170603120900",
"Neos.Flow-20180415105700",
"Neos.Flow-20190425144900",
"Neos.Flow-20190515215000"
"Neos.Flow-20180415105700"
]
}
}