From 6606af71ff7eec8ee9030055cfb98587f8e31b71 Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Thu, 29 Jun 2023 10:41:51 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B06414=20Validator=20refactoring=20New=20A?= =?UTF-8?q?bstractValidator=20class,=20with=20new=20method=20Validate=20Al?= =?UTF-8?q?l=20existing=20validators=20are=20now=20children=20of=20Abstrac?= =?UTF-8?q?tRegexpValidator=20Handle=20validators=20JS=20counterparts=20in?= =?UTF-8?q?=20renderers=20:=20only=20regexp=20validators=20are=20implement?= =?UTF-8?q?ed=20client=20side?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/attributedef.class.inc.php | 15 +-- lib/autoload.php | 5 + lib/composer/ClassLoader.php | 2 +- lib/composer/autoload_classmap.php | 5 +- lib/composer/autoload_files.php | 14 +-- lib/composer/autoload_namespaces.php | 2 +- lib/composer/autoload_psr4.php | 2 +- lib/composer/autoload_real.php | 30 +++--- lib/composer/autoload_static.php | 15 +-- lib/composer/include_paths.php | 2 +- sources/Form/Field/Field.php | 99 ++++++++++--------- sources/Form/Field/SelectObjectField.php | 30 +----- .../Validator/AbstractRegexpValidator.php | 56 +++++++++++ sources/Form/Validator/AbstractValidator.php | 58 +++++++++++ .../Form/Validator/CustomRegexpValidator.php | 22 +++++ sources/Form/Validator/IntegerValidator.php | 9 +- sources/Form/Validator/LinkedSetValidator.php | 10 +- sources/Form/Validator/MandatoryValidator.php | 8 +- .../Validator/NotEmptyExtKeyValidator.php | 9 +- sources/Form/Validator/Validator.php | 95 ++---------------- .../FieldRenderer/BsSimpleFieldRenderer.php | 11 ++- .../ConsoleSelectObjectFieldRenderer.php | 15 ++- .../ConsoleSimpleFieldRenderer.php | 13 ++- sources/Renderer/FieldRenderer.php | 8 +- .../sources/Form/{ => Field}/FieldTest.php | 32 ++++-- .../sources/Form/Validator/ValidatorTest.php | 28 ++++++ 26 files changed, 348 insertions(+), 247 deletions(-) create mode 100644 sources/Form/Validator/AbstractRegexpValidator.php create mode 100644 sources/Form/Validator/AbstractValidator.php create mode 100644 sources/Form/Validator/CustomRegexpValidator.php rename tests/php-unit-tests/unitary-tests/sources/Form/{ => Field}/FieldTest.php (58%) create mode 100644 tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 6f316b2e3..1f7662548 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -9,9 +9,8 @@ use Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty; use Combodo\iTop\Form\Field\LabelField; use Combodo\iTop\Form\Field\TextAreaField; use Combodo\iTop\Form\Form; +use Combodo\iTop\Form\Validator\CustomRegexpValidator; use Combodo\iTop\Form\Validator\LinkedSetValidator; -use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator; -use Combodo\iTop\Form\Validator\Validator; use Combodo\iTop\Renderer\BlockRenderer; use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer; use Combodo\iTop\Service\Links\LinkSetModel; @@ -1123,7 +1122,7 @@ abstract class AttributeDefinition // Validation pattern if ($this->GetValidationPattern() !== '') { - $oFormField->AddValidator(new Validator($this->GetValidationPattern())); + $oFormField->AddValidator(new CustomRegexpValidator($this->GetValidationPattern())); } // Description @@ -7295,6 +7294,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid public function MakeFormField(DBObject $oObject, $oFormField = null) { + /** @var \Combodo\iTop\Form\Field\Field $oFormField */ if ($oFormField === null) { // Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value $sFormFieldClass = static::GetFormFieldClass(); @@ -7325,19 +7325,12 @@ class AttributeExternalKey extends AttributeDBFieldVoid } }); } - else - { + else { $oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression()); $oSearch->SetInternalParams(array('this' => $oObject)); $oFormField->SetSearch($oSearch); } - // If ExtKey is mandatory, we add a validator to ensure that the value 0 is not selected - if ($oObject->GetAttributeFlags($this->GetCode()) & OPT_ATT_MANDATORY) - { - $oFormField->AddValidator(new NotEmptyExtKeyValidator()); - } - parent::MakeFormField($oObject, $oFormField); return $oFormField; diff --git a/lib/autoload.php b/lib/autoload.php index 460e67535..f1eeef5ab 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -2,6 +2,11 @@ // autoload.php @generated by Composer +if (PHP_VERSION_ID < 50600) { + echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; + exit(1); +} + require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f::getLoader(); diff --git a/lib/composer/ClassLoader.php b/lib/composer/ClassLoader.php index 0cd6055d1..afef3fa2a 100644 --- a/lib/composer/ClassLoader.php +++ b/lib/composer/ClassLoader.php @@ -149,7 +149,7 @@ class ClassLoader /** * @return string[] Array of classname => path - * @psalm-var array + * @psalm-return array */ public function getClassMap() { diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 79f445f92..70da42bf4 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -2,7 +2,7 @@ // autoload_classmap.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( @@ -415,6 +415,9 @@ return array( 'Combodo\\iTop\\Form\\Field\\UrlField' => $baseDir . '/sources/Form/Field/UrlField.php', 'Combodo\\iTop\\Form\\Form' => $baseDir . '/sources/Form/Form.php', 'Combodo\\iTop\\Form\\FormManager' => $baseDir . '/sources/Form/FormManager.php', + 'Combodo\\iTop\\Form\\Validator\\AbstractRegexpValidator' => $baseDir . '/sources/Form/Validator/AbstractRegexpValidator.php', + 'Combodo\\iTop\\Form\\Validator\\AbstractValidator' => $baseDir . '/sources/Form/Validator/AbstractValidator.php', + 'Combodo\\iTop\\Form\\Validator\\CustomRegexpValidator' => $baseDir . '/sources/Form/Validator/CustomRegexpValidator.php', 'Combodo\\iTop\\Form\\Validator\\IntegerValidator' => $baseDir . '/sources/Form/Validator/IntegerValidator.php', 'Combodo\\iTop\\Form\\Validator\\LinkedSetValidator' => $baseDir . '/sources/Form/Validator/LinkedSetValidator.php', 'Combodo\\iTop\\Form\\Validator\\MandatoryValidator' => $baseDir . '/sources/Form/Validator/MandatoryValidator.php', diff --git a/lib/composer/autoload_files.php b/lib/composer/autoload_files.php index 2dfdef1d3..50c5aa314 100644 --- a/lib/composer/autoload_files.php +++ b/lib/composer/autoload_files.php @@ -2,23 +2,23 @@ // autoload_files.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( - '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', - '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php', '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', - 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', - 'c9d07b32a2e02bc0fc582d4f0c1b56cc' => $vendorDir . '/laminas/laminas-servicemanager/src/autoload.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', - '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', - 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', + 'c9d07b32a2e02bc0fc582d4f0c1b56cc' => $vendorDir . '/laminas/laminas-servicemanager/src/autoload.php', + '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', ); diff --git a/lib/composer/autoload_namespaces.php b/lib/composer/autoload_namespaces.php index 1db5bf646..6629b7e09 100644 --- a/lib/composer/autoload_namespaces.php +++ b/lib/composer/autoload_namespaces.php @@ -2,7 +2,7 @@ // autoload_namespaces.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index eb2c95ace..3b30be01d 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -2,7 +2,7 @@ // autoload_psr4.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_real.php b/lib/composer/autoload_real.php index cc554d8d1..dda4f956d 100644 --- a/lib/composer/autoload_real.php +++ b/lib/composer/autoload_real.php @@ -25,33 +25,20 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f require __DIR__ . '/platform_check.php'; spl_autoload_register(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); spl_autoload_unregister(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader')); $includePaths = require __DIR__ . '/include_paths.php'; $includePaths[] = get_include_path(); set_include_path(implode(PATH_SEPARATOR, $includePaths)); - $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); - if ($useStaticLoader) { - require __DIR__ . '/autoload_static.php'; - - call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader)); - } else { - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - } + require __DIR__ . '/autoload_static.php'; + call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files; - } else { - $includeFiles = require __DIR__ . '/autoload_files.php'; - } + $includeFiles = \Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files; foreach ($includeFiles as $fileIdentifier => $file) { composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file); } @@ -60,11 +47,16 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f } } +/** + * @param string $fileIdentifier + * @param string $file + * @return void + */ function composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; } } diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index f6c6e6728..47275be29 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -7,21 +7,21 @@ namespace Composer\Autoload; class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f { public static $files = array ( - '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', + '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', - '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', + '0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php', '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', - 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', - 'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', - '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', - 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', + 'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php', + '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', + 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', ); public static $prefixLengthsPsr4 = array ( @@ -779,6 +779,9 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Form\\Field\\UrlField' => __DIR__ . '/../..' . '/sources/Form/Field/UrlField.php', 'Combodo\\iTop\\Form\\Form' => __DIR__ . '/../..' . '/sources/Form/Form.php', 'Combodo\\iTop\\Form\\FormManager' => __DIR__ . '/../..' . '/sources/Form/FormManager.php', + 'Combodo\\iTop\\Form\\Validator\\AbstractRegexpValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/AbstractRegexpValidator.php', + 'Combodo\\iTop\\Form\\Validator\\AbstractValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/AbstractValidator.php', + 'Combodo\\iTop\\Form\\Validator\\CustomRegexpValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/CustomRegexpValidator.php', 'Combodo\\iTop\\Form\\Validator\\IntegerValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/IntegerValidator.php', 'Combodo\\iTop\\Form\\Validator\\LinkedSetValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/LinkedSetValidator.php', 'Combodo\\iTop\\Form\\Validator\\MandatoryValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/MandatoryValidator.php', diff --git a/lib/composer/include_paths.php b/lib/composer/include_paths.php index d4fb96718..af33c1491 100644 --- a/lib/composer/include_paths.php +++ b/lib/composer/include_paths.php @@ -2,7 +2,7 @@ // include_paths.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( diff --git a/sources/Form/Field/Field.php b/sources/Form/Field/Field.php index 66bea9c3e..3e683c3d2 100644 --- a/sources/Form/Field/Field.php +++ b/sources/Form/Field/Field.php @@ -8,8 +8,8 @@ namespace Combodo\iTop\Form\Field; use Closure; +use Combodo\iTop\Form\Validator\AbstractValidator; use Combodo\iTop\Form\Validator\MandatoryValidator; -use Combodo\iTop\Form\Validator\Validator; /** * Description of Field @@ -68,7 +68,7 @@ abstract class Field protected $bMandatory; /** @var string */ protected $sDisplayMode; - /** @var array */ + /** @var AbstractValidator[] */ protected $aValidators; /** * @var bool @@ -228,10 +228,6 @@ abstract class Field return $this->sDisplayMode; } - /** - * - * @return array - */ public function GetValidators() { return $this->aValidators; @@ -352,38 +348,46 @@ abstract class Field * Setting the value will automatically add/remove a MandatoryValidator to the Field * * @param boolean $bMandatory + * * @return $this */ public function SetMandatory(bool $bMandatory) { // Before changing the property, we check if it was already mandatory. If not, we had the mandatory validator - if ($bMandatory && !$this->bMandatory) - { - $this->AddValidator(new MandatoryValidator()); + if ($bMandatory && !$this->bMandatory) { + $this->AddValidator($this->GetMandatoryValidatorInstance()); } - if (!$bMandatory) - { - foreach ($this->aValidators as $iKey => $oValue) - { - if ($oValue::Getname() === MandatoryValidator::GetName()) - { - unset($this->aValidators[$iKey]); - } - } - } + if (false === $bMandatory) { + foreach ($this->aValidators as $iKey => $oValue) { + if ($oValue instanceof MandatoryValidator) { + unset($this->aValidators[$iKey]); + } + } + } - $this->bMandatory = $bMandatory; - return $this; - } + $this->bMandatory = $bMandatory; - /** - * Sets if the field is must change or not. - * Note: This not implemented yet! Just a pre-conception for CaseLogField - * - * @todo Implement - * @param boolean $bMustChange + return $this; + } + + /** + * @return AbstractValidator + * @since 3.1.0 N°6414 + */ + protected function GetMandatoryValidatorInstance(): AbstractValidator + { + return new MandatoryValidator(); + } + + /** + * Sets if the field is must change or not. + * Note: This not implemented yet! Just a pre-conception for CaseLogField + * + * @param boolean $bMustChange + * * @return $this + * @todo Implement */ public function SetMustChange(bool $bMustChange) { @@ -476,31 +480,28 @@ abstract class Field return $this; } - /** - * - * @param \Combodo\iTop\Form\Validator\Validator $oValidator - * @return $this - */ - public function AddValidator(Validator $oValidator) + /** + * @param AbstractValidator $oValidator + * @return $this + */ + public function AddValidator(AbstractValidator $oValidator) { $this->aValidators[] = $oValidator; + return $this; } /** - * - * @param \Combodo\iTop\Form\Validator\Validator $oValidator * @return $this */ - public function RemoveValidator(Validator $oValidator) + public function RemoveValidator(AbstractValidator $oValidator) { - foreach ($this->aValidators as $iKey => $oValue) - { - if ($oValue === $oValidator) - { + foreach ($this->aValidators as $iKey => $oValue) { + if ($oValue === $oValidator) { unset($this->aValidators[$iKey]); } } + return $this; } @@ -574,10 +575,13 @@ abstract class Field } /** - * Checks the validators to see if the field's current value is valid. - * Then sets $bValid and $aErrorMessages. + * Validates the field using the validators set. * - * @return boolean + * Before overriding this method in children classes, try to add a custom validator ! + * + * @uses GetValidators() + * @uses SetValid() + * @uses AddErrorMessage() */ public function Validate() { @@ -592,9 +596,12 @@ abstract class Field if (!$bEmpty || $this->GetMandatory()) { foreach ($this->GetValidators() as $oValidator) { - if (!preg_match($oValidator->GetRegExp(true), $this->GetCurrentValue())) { + [$bIsFieldValid, $sValidationErrorMessage] = $oValidator->Validate($this->GetCurrentValue()); + + /** @var bool $bIsFieldValid */ + if (false === $bIsFieldValid) { $this->SetValid(false); - $this->AddErrorMessage($oValidator->GetErrorMessage()); + $this->AddErrorMessage($sValidationErrorMessage); } } } diff --git a/sources/Form/Field/SelectObjectField.php b/sources/Form/Field/SelectObjectField.php index 58e954189..62aaadf5e 100644 --- a/sources/Form/Field/SelectObjectField.php +++ b/sources/Form/Field/SelectObjectField.php @@ -22,6 +22,7 @@ namespace Combodo\iTop\Form\Field; use BinaryExpression; use Closure; +use Combodo\iTop\Form\Validator\AbstractValidator; use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator; use ContextTag; use DBObjectSet; @@ -168,38 +169,15 @@ class SelectObjectField extends Field return $this; } - /** - * @inheritDoc - */ - public function SetMandatory(bool $bMandatory) + protected function GetMandatoryValidatorInstance(): AbstractValidator { - // Before changing the property, we check if it was already mandatory. If not, we had the mandatory validator - if ($bMandatory && !$this->bMandatory) - { - $this->AddValidator(new NotEmptyExtKeyValidator()); - } - - if (!$bMandatory) - { - foreach ($this->aValidators as $iKey => $oValue) - { - if ($oValue::Getname() === NotEmptyExtKeyValidator::GetName()) - { - unset($this->aValidators[$iKey]); - } - } - } - - $this->bMandatory = $bMandatory; - - return $this; + return new NotEmptyExtKeyValidator(); } /** * @return \DBSearch */ - public function GetSearch() - { + public function GetSearch() { return $this->oSearch; } diff --git a/sources/Form/Validator/AbstractRegexpValidator.php b/sources/Form/Validator/AbstractRegexpValidator.php new file mode 100644 index 000000000..a3434f54d --- /dev/null +++ b/sources/Form/Validator/AbstractRegexpValidator.php @@ -0,0 +1,56 @@ +sRegExp = static::DEFAULT_REGEXP; + parent::__construct($sErrorMessage); + } + + public function Validate($value): array + { + if (is_null($value)) { + $value = ''; // calling preg_match with null as subject is deprecated since PHP 8.1 + } + if (preg_match($this->GetRegExp(true), $value)) { + return [true, null]; + } + + return [false, $this->sErrorMessage]; + } + + /** + * Returns the regular expression of the validator. + * + * @param boolean $bWithSlashes If true, surrounds $sRegExp with '/'. Used with preg_match & co + * + * @return string + */ + public function GetRegExp($bWithSlashes = false) + { + if ($bWithSlashes) { + $sRet = '/'.str_replace('/', '\\/', $this->sRegExp).'/'; + } else { + $sRet = $this->sRegExp; + } + + return $sRet; + } +} \ No newline at end of file diff --git a/sources/Form/Validator/AbstractValidator.php b/sources/Form/Validator/AbstractValidator.php new file mode 100644 index 000000000..da0780e8a --- /dev/null +++ b/sources/Form/Validator/AbstractValidator.php @@ -0,0 +1,58 @@ +sErrorMessage = $sErrorMessage; + } else { + $this->sErrorMessage = self::DEFAULT_ERROR_MESSAGE; + } + } + + /** + * @param mixed $value + * + * @return array boolean valid for valid / invalid, and error message if invalid + */ + abstract public function Validate($value): array; + + /** + * Name to use for JS counterparts + * + * @return string + */ + public static function GetName() + { + return static::VALIDATOR_NAME; + } + + /** + * Still used in \Combodo\iTop\Renderer\Console\FieldRenderer\ConsoleSelectObjectFieldRenderer::Render :( + * + * @return string + */ + public function GetErrorMessage() + { + return $this->sErrorMessage; + } +} \ No newline at end of file diff --git a/sources/Form/Validator/CustomRegexpValidator.php b/sources/Form/Validator/CustomRegexpValidator.php new file mode 100644 index 000000000..a4852e9dc --- /dev/null +++ b/sources/Form/Validator/CustomRegexpValidator.php @@ -0,0 +1,22 @@ +sRegExp = $sRegExp; // must be done after parent constructor call ! + } +} \ No newline at end of file diff --git a/sources/Form/Validator/IntegerValidator.php b/sources/Form/Validator/IntegerValidator.php index 2c58418af..858a7eda2 100644 --- a/sources/Form/Validator/IntegerValidator.php +++ b/sources/Form/Validator/IntegerValidator.php @@ -24,10 +24,9 @@ namespace Combodo\iTop\Form\Validator; * * @author Guillaume Lajarige */ -class IntegerValidator extends Validator +class IntegerValidator extends AbstractRegexpValidator { - const VALIDATOR_NAME = 'integer'; - const DEFAULT_REGEXP = '^[0-9]+$'; - const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustBeInteger'; - + public const VALIDATOR_NAME = 'integer'; + public const DEFAULT_REGEXP = '^[0-9]+$'; + public const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustBeInteger'; } diff --git a/sources/Form/Validator/LinkedSetValidator.php b/sources/Form/Validator/LinkedSetValidator.php index 69f7c7e25..5ac6fbd2a 100644 --- a/sources/Form/Validator/LinkedSetValidator.php +++ b/sources/Form/Validator/LinkedSetValidator.php @@ -24,13 +24,7 @@ namespace Combodo\iTop\Form\Validator; * * @since 3.1 */ -class LinkedSetValidator extends Validator +class LinkedSetValidator extends AbstractRegexpValidator { - const VALIDATOR_NAME = 'LinkedSetValidator'; - - /** @inheritdoc */ - public static function GetName() - { - return static::VALIDATOR_NAME; - } + public const VALIDATOR_NAME = 'LinkedSetValidator'; } diff --git a/sources/Form/Validator/MandatoryValidator.php b/sources/Form/Validator/MandatoryValidator.php index fd9162066..e08c4471e 100644 --- a/sources/Form/Validator/MandatoryValidator.php +++ b/sources/Form/Validator/MandatoryValidator.php @@ -26,10 +26,10 @@ namespace Combodo\iTop\Form\Validator; * * @author Guillaume Lajarige */ -class MandatoryValidator extends Validator +class MandatoryValidator extends AbstractRegexpValidator { - const VALIDATOR_NAME = 'mandatory'; - const DEFAULT_REGEXP = '.*\S.*'; - const DEFAULT_ERROR_MESSAGE = 'Core:Validator:Mandatory'; + public const VALIDATOR_NAME = 'mandatory'; + public const DEFAULT_REGEXP = '.*\S.*'; + public const DEFAULT_ERROR_MESSAGE = 'Core:Validator:Mandatory'; } diff --git a/sources/Form/Validator/NotEmptyExtKeyValidator.php b/sources/Form/Validator/NotEmptyExtKeyValidator.php index aa1965280..f89b61278 100644 --- a/sources/Form/Validator/NotEmptyExtKeyValidator.php +++ b/sources/Form/Validator/NotEmptyExtKeyValidator.php @@ -24,10 +24,9 @@ namespace Combodo\iTop\Form\Validator; * * @author Guillaume Lajarige */ -class NotEmptyExtKeyValidator extends Validator +class NotEmptyExtKeyValidator extends MandatoryValidator { - const VALIDATOR_NAME = 'notemptyextkey'; - const DEFAULT_REGEXP = '^[0-9]*[1-9][0-9]*$'; - const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustSelectOne'; - + public const VALIDATOR_NAME = 'notemptyextkey'; + public const DEFAULT_REGEXP = '^[0-9]*[1-9][0-9]*$'; + public const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustSelectOne'; } diff --git a/sources/Form/Validator/Validator.php b/sources/Form/Validator/Validator.php index 3d25a569a..8f526be88 100644 --- a/sources/Form/Validator/Validator.php +++ b/sources/Form/Validator/Validator.php @@ -19,99 +19,22 @@ namespace Combodo\iTop\Form\Validator; +use DeprecatedCallsLog; + + /** * Description of Validator * - * @author Guillaume Lajarige + * @deprecated 3.1.0 N°6414 use {@see \Combodo\iTop\Form\Validator\CustomRegexpValidator} instead */ -class Validator +class Validator extends CustomRegexpValidator { - const VALIDATOR_NAME = 'expression'; - const DEFAULT_REGEXP = ''; - const DEFAULT_ERROR_MESSAGE = 'Core:Validator:Default'; - - protected $sRegExp; - protected $sErrorMessage; - - public static function GetName() - { - return static::VALIDATOR_NAME; - } - - /** - * - * @param string $sRegExp - * @param string $sErrorMessage - */ public function __construct($sRegExp = null, $sErrorMessage = null) { - $this->sRegExp = ($sRegExp === null) ? static::DEFAULT_REGEXP : $sRegExp; - $this->sErrorMessage = ($sErrorMessage === null) ? static::DEFAULT_ERROR_MESSAGE : $sErrorMessage; - $this->ComputeConstraints(); - } + // cannot use DeprecatedCallsLog::NotifyDeprecatedFile as it would trigger an exception on dev env + // because all autoloader files are loaded during MetaModel::Startup (calling \Combodo\iTop\Service\Events\EventService::InitService calling \utils::GetClassesForInterface) + DeprecatedCallsLog::NotifyDeprecatedPhpMethod('3.1.0 N°6414 use '.CustomRegexpValidator::class.' instead'); - /** - * Returns the regular expression of the validator. - * - * @param boolean $bWithSlashes If true, surrounds $sRegExp with '/'. Used with preg_match & co - * @return string - */ - public function GetRegExp($bWithSlashes = false) - { - if ($bWithSlashes) - { - $sRet = '/' . str_replace('/', '\\/', $this->sRegExp) . '/'; - } - else - { - $sRet = $this->sRegExp; - } - return $sRet; + parent::__construct($sRegExp, $sErrorMessage); } - - public function GetErrorMessage() - { - return $this->sErrorMessage; - } - - public function SetRegExp($sRegExp) - { - $this->sRegExp = $sRegExp; - $this->ComputeConstraints(); - return $this; - } - - public function SetErrorMessage($sErrorMessage) - { - $this->sErrorMessage = $sErrorMessage; - $this->ComputeConstraints(); - return $this; - } - - /** - * Computes the regular expression and error message when changing constraints on the validator. - * Should be called in the validator's setters. - */ - public function ComputeConstraints() - { - $this->ComputeRegularExpression(); - $this->ComputeErrorMessage(); - } - - /** - * Computes the regular expression when changing constraints on the validator. - */ - public function ComputeRegularExpression() - { - // Overload when necessary - } - - /** - * Computes the error message when changing constraints on the validator. - */ - public function ComputeErrorMessage() - { - // Overload when necessary - } - } diff --git a/sources/Renderer/Bootstrap/FieldRenderer/BsSimpleFieldRenderer.php b/sources/Renderer/Bootstrap/FieldRenderer/BsSimpleFieldRenderer.php index e2df1cb1a..e523c2d03 100644 --- a/sources/Renderer/Bootstrap/FieldRenderer/BsSimpleFieldRenderer.php +++ b/sources/Renderer/Bootstrap/FieldRenderer/BsSimpleFieldRenderer.php @@ -28,8 +28,8 @@ use Combodo\iTop\Form\Field\DateTimeField; use Combodo\iTop\Form\Field\Field; use Combodo\iTop\Form\Field\MultipleChoicesField; use Combodo\iTop\Form\Field\TextAreaField; +use Combodo\iTop\Form\Validator\AbstractRegexpValidator; use Combodo\iTop\Form\Validator\MandatoryValidator; -use Combodo\iTop\Form\Validator\Validator; use Combodo\iTop\Renderer\RenderingOutput; use Dict; use InlineImage; @@ -458,9 +458,14 @@ EOF // JS Form field widget construct $aValidators = array(); foreach ($this->oField->GetValidators() as $oValidator) { + if (false === ($oValidator instanceof AbstractRegexpValidator)) { + // no JS counterpart, so skipping ! + continue; + } + $aValidators[$oValidator::GetName()] = array( 'reg_exp' => $oValidator->GetRegExp(), - 'message' => Dict::S($oValidator->GetErrorMessage()) + 'message' => Dict::S($oValidator->GetErrorMessage()), ); } @@ -758,7 +763,7 @@ JS foreach ($oField->GetValidators() as $oValidator) { // Validator - if (get_class($oValidator) === Validator::class) { + if ($oValidator instanceof AbstractRegexpValidator) { if (!($oField instanceof DateField || $oField instanceof DateTimeField)) { // unrecognized regular expression $sTags .= ' pattern="'.$oValidator->GetRegExp().'" '; } diff --git a/sources/Renderer/Console/FieldRenderer/ConsoleSelectObjectFieldRenderer.php b/sources/Renderer/Console/FieldRenderer/ConsoleSelectObjectFieldRenderer.php index d8f2bb350..c679ac93e 100644 --- a/sources/Renderer/Console/FieldRenderer/ConsoleSelectObjectFieldRenderer.php +++ b/sources/Renderer/Console/FieldRenderer/ConsoleSelectObjectFieldRenderer.php @@ -27,7 +27,9 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockF use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; use Combodo\iTop\Form\Field\SelectObjectField; +use Combodo\iTop\Form\Validator\AbstractRegexpValidator; use Combodo\iTop\Form\Validator\MandatoryValidator; +use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator; use Combodo\iTop\Renderer\BlockRenderer; use Combodo\iTop\Renderer\FieldRenderer; use DBObjectSet; @@ -245,16 +247,19 @@ JS $oOutput->AddHtml((BlockRenderer::RenderBlockTemplates($oBlock))); // JS Form field widget construct $aValidators = array(); - foreach ($this->oField->GetValidators() as $oValidator) - { - if ($oValidator::GetName() == 'notemptyextkey') - { + foreach ($this->oField->GetValidators() as $oValidator) { + if (false === ($oValidator instanceof AbstractRegexpValidator)) { + // no JS counterpart, so skipping ! + continue; + } + + if ($oValidator instanceof NotEmptyExtKeyValidator) { // The autocomplete widget returns an empty string if the value is undefined (and the select has been aligned with this behavior) $oValidator = new MandatoryValidator(); } $aValidators[$oValidator::GetName()] = array( 'reg_exp' => $oValidator->GetRegExp(), - 'message' => Dict::S($oValidator->GetErrorMessage()) + 'message' => Dict::S($oValidator->GetErrorMessage()), ); } $sValidators = json_encode($aValidators); diff --git a/sources/Renderer/Console/FieldRenderer/ConsoleSimpleFieldRenderer.php b/sources/Renderer/Console/FieldRenderer/ConsoleSimpleFieldRenderer.php index 3ad09f162..a259ad3ef 100644 --- a/sources/Renderer/Console/FieldRenderer/ConsoleSimpleFieldRenderer.php +++ b/sources/Renderer/Console/FieldRenderer/ConsoleSimpleFieldRenderer.php @@ -33,6 +33,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\TextArea; use Combodo\iTop\Application\UI\Base\Component\Text\Text; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; use Combodo\iTop\Form\Field\TextAreaField; +use Combodo\iTop\Form\Validator\AbstractRegexpValidator; use Combodo\iTop\Renderer\BlockRenderer; use Combodo\iTop\Renderer\FieldRenderer; use DateTimeFormat; @@ -433,16 +434,20 @@ EOF // JS Form field widget construct $aValidators = array(); - foreach ($this->oField->GetValidators() as $oValidator) - { + foreach ($this->oField->GetValidators() as $oValidator) { + if (false === ($oValidator instanceof AbstractRegexpValidator)) { + // no JS counterpart, so skipping ! + continue; + } + $aValidators[$oValidator::GetName()] = array( 'reg_exp' => $oValidator->GetRegExp(), - 'message' => Dict::S($oValidator->GetErrorMessage()) + 'message' => Dict::S($oValidator->GetErrorMessage()), ); } $sValidators = json_encode($aValidators); $sFormFieldOptions = -<<oField->GetValidators() as $oValidator) { + if (false === ($oValidator instanceof AbstractRegexpValidator)) { + // no JS counterpart, so skipping ! + continue; + } + $aValidators[$oValidator::GetName()] = array( 'reg_exp' => $oValidator->GetRegExp(), - 'message' => Dict::S($oValidator->GetErrorMessage()) + 'message' => Dict::S($oValidator->GetErrorMessage()), ); } // - Formatting options diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/FieldTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/Field/FieldTest.php similarity index 58% rename from tests/php-unit-tests/unitary-tests/sources/Form/FieldTest.php rename to tests/php-unit-tests/unitary-tests/sources/Form/Field/FieldTest.php index c0fa04ba7..0654cf4bc 100644 --- a/tests/php-unit-tests/unitary-tests/sources/Form/FieldTest.php +++ b/tests/php-unit-tests/unitary-tests/sources/Form/Field/FieldTest.php @@ -4,29 +4,32 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ -namespace Combodo\iTop\Test\UnitTest\Sources\Form; +namespace Combodo\iTop\Test\UnitTest\Sources\Form\Field; use Combodo\iTop\Form\Field\StringField; +use Combodo\iTop\Form\Validator\CustomRegexpValidator; use Combodo\iTop\Form\Validator\IntegerValidator; use Combodo\iTop\Form\Validator\MandatoryValidator; -use Combodo\iTop\Form\Validator\Validator; use Combodo\iTop\Test\UnitTest\ItopTestCase; class FieldTest extends ItopTestCase { - public function testIsValidationDisabled(): void + public function testIsValidationDisabled(): void { $oField = new StringField('test'); $oField->SetCurrentValue('toto@johny.invalid'); - $oDumbEmailValidator = new Validator('\d+@\d+\.\d{2,3}'); + $sDumbEmailValidatorErrorMessage = 'dumb email validator error message'; + $oDumbEmailValidator = new CustomRegexpValidator('\d+@\d+\.\d{2,3}', $sDumbEmailValidatorErrorMessage); $oField->AddValidator($oDumbEmailValidator); $bIsFieldValid = $oField->Validate(); $this->assertFalse($bIsFieldValid); + $this->assertCount(1, $oField->GetErrorMessages()); + $this->assertSame($sDumbEmailValidatorErrorMessage, $oField->GetErrorMessages()[0]); + /** @noinspection PhpRedundantOptionalArgumentInspection */ $oField->SetValidationDisabled(true); - $bIsFieldValidWithValidationDisabled = $oField->Validate(); - $this->assertTrue($bIsFieldValidWithValidationDisabled); + $this->assertTrue($oField->Validate()); } public function testSetMandatory(): void @@ -60,4 +63,21 @@ class FieldTest extends ItopTestCase $this->assertFalse($oField->Validate()); $this->assertCount(1, $oField->GetErrorMessages()); } + + public function testValidateWithTwoValidatorsFirstWrong(): void + { + $oField = new StringField('test'); + $oField->SetCurrentValue('string with spaces'); + + $sFirstValidatorInvalidResultErrorMsg = 'dumb email validator error message'; + $oFirstValidatorInvalidResult = new CustomRegexpValidator('^[a-z]+$', $sFirstValidatorInvalidResultErrorMsg); + $oField->AddValidator($oFirstValidatorInvalidResult); + + $oSecondValidatorValidResult = new CustomRegexpValidator('^.+$', 'valid'); + $oField->AddValidator($oSecondValidatorValidResult); + + $this->assertFalse($oField->Validate()); + $this->assertCount(1, $oField->GetErrorMessages()); + $this->assertSame($sFirstValidatorInvalidResultErrorMsg, $oField->GetErrorMessages()[0]); + } } \ No newline at end of file diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php new file mode 100644 index 000000000..c7c066f38 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php @@ -0,0 +1,28 @@ +SetMandatory(true); + $oField->SetCurrentValue('there is a value !'); + + $bIsMandatoryFieldValidWithExistingValue = $oField->Validate(); + $this->assertTrue($bIsMandatoryFieldValidWithExistingValue); + + $oField->SetCurrentValue(null); + $bIsMandatoryFieldValidWithNoValue = $oField->Validate(); + $this->assertFalse($bIsMandatoryFieldValidWithNoValue); + $this->assertNotEmpty($oField->GetErrorMessages()); + } +} \ No newline at end of file