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 <?php
declare(strict_types=1);
namespace DigiComp\SettingValidator; namespace DigiComp\SettingValidator;
/* /*
@ -16,27 +14,32 @@ namespace DigiComp\SettingValidator;
use Neos\Flow\Configuration\ConfigurationManager; use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Core\Bootstrap; 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. * 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); parent::boot($bootstrap);
$dispatcher = $bootstrap->getSignalSlotDispatcher(); $dispatcher = $bootstrap->getSignalSlotDispatcher();
$dispatcher->connect( $dispatcher->connect(
ConfigurationManager::class, ConfigurationManager::class,
'configurationManagerReady', 'configurationManagerReady',
function (ConfigurationManager $configurationManager): void { function (ConfigurationManager $configurationManager) {
$configurationManager->registerConfigurationType('Validation'); $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 <?php
declare(strict_types=1);
namespace DigiComp\SettingValidator\Validation\Validator; namespace DigiComp\SettingValidator\Validation\Validator;
/* /*
@ -15,11 +13,13 @@ namespace DigiComp\SettingValidator\Validation\Validator;
*/ */
use Neos\Flow\Annotations as Flow; 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\InvalidValidationConfigurationException;
use Neos\Flow\Validation\Exception\InvalidValidationOptionsException; use Neos\Flow\Validation\Exception\InvalidValidationOptionsException;
use Neos\Flow\Validation\Exception\NoSuchValidatorException;
use Neos\Flow\Validation\Validator\AbstractValidator; use Neos\Flow\Validation\Validator\AbstractValidator;
use Neos\Flow\Validation\ValidatorResolver; use Neos\Flow\Validation\ValidatorResolver;
use Neos\Utility\ObjectAccess;
use Neos\Utility\TypeHandling; use Neos\Utility\TypeHandling;
/** /**
@ -34,77 +34,67 @@ class SettingsValidator extends AbstractValidator
protected $validatorResolver; protected $validatorResolver;
/** /**
* @Flow\InjectConfiguration(type="Validation") * @var ConfigurationManager
* @var array
*/ */
protected array $validations; protected $configurationManager;
/** /**
* @inheritDoc * @Flow\Inject
* @deprecated
* @var ReflectionService
*/
protected $reflectionService;
/**
* @var array
*/ */
protected $supportedOptions = [ protected $supportedOptions = [
'name' => ['', 'Name of the setting array to use', 'string'], 'name' => ['', 'Set the name of the setting-array to use', 'string', false],
'validationGroups' => [['Default'], 'Same as "Validation Groups" of Flow Framework', 'array'], '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 InvalidValidationOptionsException
* @throws InvalidValidationConfigurationException * @throws InvalidValidationConfigurationException
* @throws NoSuchValidatorException
*/ */
protected function isValid($value): void protected function isValid($value)
{ {
$validations = $this->validations; $name = $this->options['name'] ? $this->options['name'] : TypeHandling::getTypeForValue($value);
if (! isset($this->validations[$name])) {
// 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])) {
throw new InvalidValidationOptionsException( throw new InvalidValidationOptionsException(
'The name "' . $name . '" has not been defined in Validation.yaml!', 'The name ' . $name . ' has not been defined in Validation.yaml!',
1397821438 1397821438
); );
} }
// @deprecated - converts old "self" to new structure $config = $this->getConfigForName($name);
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;
}
unset($validations[$name]['self']); foreach ($config as $validatorConfig) {
} if (! $this->doesValidationGroupsMatch($validatorConfig)) {
// @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)) {
continue; continue;
} }
@ -115,33 +105,77 @@ class SettingsValidator extends AbstractValidator
$validatorConfig['options'] $validatorConfig['options']
); );
if ($validator === null) { if (! $validator) {
throw new InvalidValidationConfigurationException( throw new InvalidValidationConfigurationException(
\sprintf( \sprintf(
'Validator "%s" could not be resolved. Check your Validation.yaml', 'Validator could not be resolved: "%s" Check your Validation.yaml',
$validatorConfig['validator'] $validatorConfig['validator']
), ),
1402326139 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. * Check whether at least one configured group does match, if any is configured.
* *
* @param array $validatorConfig * @param array $validatorConfig
*
* @return bool * @return bool
*/ */
protected function doesValidationGroupsMatch(array $validatorConfig): bool protected function doesValidationGroupsMatch(array &$validatorConfig)
{ {
return !isset($validatorConfig['options']['validationGroups']) if (
|| \array_intersect( isset($validatorConfig['options']['validationGroups'])
$validatorConfig['options']['validationGroups'], && \array_intersect($validatorConfig['options']['validationGroups'], $this->options['validationGroups']) === []
$this->options['validationGroups'] ) {
) !== []; return false;
}
return true;
} }
/** /**
@ -149,12 +183,12 @@ class SettingsValidator extends AbstractValidator
* *
* @param array $validatorConfig * @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'])) { if (isset($validatorConfig['options']['validationGroups']) && $validatorConfig['validator'] !== 'DigiComp.SettingValidator:Settings') {
$validatorConfig['options']['validationGroups'] = $this->options['validationGroups'];
} elseif (isset($validatorConfig['options']['validationGroups'])) {
unset($validatorConfig['options']['validationGroups']); 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 expectedValue: false
Grumble: ~ Grumble: ~
TrueValidator:
self:
BooleanValue:
expectedValue: true
DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsCustomObject: DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsCustomObject:
self: self:
DigiComp.SettingValidator:Settings: DigiComp.SettingValidator:Settings:
name: "GroupValidatorCustom" name: 'GroupValidatorCustom'
DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsDefaultObject: DigiComp\SettingValidator\Tests\Functional\Fixtures\TestValidationGroupsDefaultObject:
self: self:
DigiComp.SettingValidator:Settings: DigiComp.SettingValidator:Settings:
name: "GroupValidatorDefault" name: 'GroupValidatorDefault'
GroupValidatorDefault: GroupValidatorDefault:
properties: properties:
@ -30,9 +35,4 @@ GroupValidatorCustom:
BooleanValue: BooleanValue:
expectedValue: false expectedValue: false
validationGroups: validationGroups:
- "Custom" - 'Custom'
TrueValidator:
self:
BooleanValue:
expectedValue: true

View file

@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.

View file

@ -1,30 +1,37 @@
<?php <?php
declare(strict_types=1);
namespace Neos\Flow\Core\Migrations; 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 class Version20170603120900 extends AbstractMigration
{ {
/** public function getIdentifier()
* @return string
*/
public function getIdentifier(): string
{ {
return 'DigiComp.SettingValidator-20170603120900'; return 'DigiComp.SettingValidator-20170603120900';
} }
/** /**
* @inheritDoc * @return void
*/ */
public function up(): void public function up()
{ {
$this->processConfiguration( $this->processConfiguration(
'Validation', Package::CONFIGURATION_TYPE_VALIDATION,
function (array &$configuration): void { function (&$configuration) {
foreach ($configuration as $validatorName => &$validators) { foreach ($configuration as $validatorName => &$validators) {
// guard that protects configuration, which has already the new format: // guard that protects configuration, which has already the new format:
if (isset($validators['properties']) || isset($validators['self'])) { if (isset($validators['properties']) || isset($validators['self'])) {
@ -32,17 +39,15 @@ class Version20170603120900 extends AbstractMigration
} }
$newConfiguration = ['properties' => [], 'self' => []]; $newConfiguration = ['properties' => [], 'self' => []];
foreach ($validators as $key => $validator) { foreach ($validators as $key => &$validator) {
if (!isset($validator['validator']) || !isset($validator['options'])) { if (! isset($validator['validator']) || ! isset($validator['options'])) {
$this->showWarning( $this->showWarning('The Validation.yaml files contained no validator or options for ' .
'The Validation.yaml files contained no validator or options for validation: ' 'validation: "' . $validatorName . '.' . $key . '". It was not migrated.');
. '"' . $validatorName . '.' . $key . '". It was not migrated.'
);
continue; continue;
} }
if (isset($validator['property'])) { if (isset($validator['property'])) {
$newConfiguration['properties'][$validator['property']][$validator['validator']] = $newConfiguration['properties'][$validator['property']][$validator['validator']]
$validator['options']; = $validator['options'];
} else { } else {
$newConfiguration['self'][$validator['validator']] = $validator['options']; $newConfiguration['self'][$validator['validator']] = $validator['options'];
} }

239
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 Lets imagine you had this action-method:
that should be applied to the value. It distinguishes between validators that are applied to the value itself and its
properties.
## 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, Then your Validation.yaml could look like this:
otherwise the type of the value is used, which is mainly useful for objects where the FQCN is used.
### 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
OrderCustomer:
-
property: firstName
validator: StringLength
options:
minimum: 3
maximum: 20
To resolve the validation configuration by name just use the option `name`.
```php As you see: Nesting is possible ;) That way you can easily construct flexible structures.
/**
* @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. 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.
```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: {}
```

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,55 +1,40 @@
{ {
"name": "digicomp/settingvalidator", "name": "digicomp/settingvalidator",
"description": "Just a Neos\\Flow Validator resolving other Validators with Configuration/Validation.yaml",
"type": "neos-package", "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": [ "keywords": [
"Neos", "Neos",
"Flow", "Flow",
"validation" "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": { "require": {
"php": ">=7.4.0", "neos/flow": "~4.1|^5.3"
"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"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"DigiComp\\SettingValidator\\": "Classes/" "DigiComp\\SettingValidator\\": "Classes"
} }
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"DigiComp\\SettingValidator\\Tests\\": "Tests/" "DigiComp\\SettingValidator\\Tests\\": "Tests"
} }
}, },
"config": {
"sort-packages": true,
"platform-check": true
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-develop": "3.2.x-dev", "dev-develop": "2.0.x-dev",
"dev-version/2.x-dev": "2.1.x-dev",
"dev-version/1.x-dev": "1.0.x-dev" "dev-version/1.x-dev": "1.0.x-dev"
}, },
"neos": {
"package-key": "DigiComp.SettingValidator"
},
"applied-flow-migrations": [ "applied-flow-migrations": [
"Inwebs.Basket-201409170938", "Inwebs.Basket-201409170938",
"TYPO3.FLOW3-201201261636", "TYPO3.FLOW3-201201261636",
@ -82,9 +67,7 @@
"Neos.Flow-20170125103800", "Neos.Flow-20170125103800",
"Neos.Flow-20170127183102", "Neos.Flow-20170127183102",
"DigiComp.SettingValidator-20170603120900", "DigiComp.SettingValidator-20170603120900",
"Neos.Flow-20180415105700", "Neos.Flow-20180415105700"
"Neos.Flow-20190425144900",
"Neos.Flow-20190515215000"
] ]
} }
} }