N°6414 Move existing AbstractSimpleField::Validate impl to custom validators

- LinkedSetField
- SelectObjectField
- MultipleChoicesField (warning this hierarchy contains non multiple value fields like SelectField !)

Also change AbstractValidator::Validate signature : now we are returning an array of error messages, so that we can return multiple ones
This commit is contained in:
Pierre Goiffon
2023-06-28 17:56:04 +02:00
parent d085f15b6d
commit 6cc2d49cd5
20 changed files with 526 additions and 172 deletions

View File

@@ -26,12 +26,13 @@ class AbstractSimpleField extends Field
if (!$bEmpty || $this->GetMandatory()) {
foreach ($this->GetValidators() as $oValidator) {
[$bIsFieldValid, $sValidationErrorMessage] = $oValidator->Validate($this->GetCurrentValue());
$aValidationErrorMessages = $oValidator->Validate($this->GetCurrentValue());
/** @var bool $bIsFieldValid */
if (false === $bIsFieldValid) {
if (count($aValidationErrorMessages) > 0) {
$this->SetValid(false);
$this->AddErrorMessage($sValidationErrorMessage);
foreach ($aValidationErrorMessages as $sErrorMessage) {
$this->AddErrorMessage($sErrorMessage);
}
}
}
}

View File

@@ -505,15 +505,33 @@ abstract class Field
return $this;
}
/**
* @param string $sValidatorClassName validator class name, should be one of {@see AbstractValidator} children
* @return $this
* @since 3.1.0 N°6414
*/
final public function RemoveValidatorsOfClass(string $sValidatorClassName)
{
foreach ($this->aValidators as $iKey => $oValue) {
if ($oValue instanceof $sValidatorClassName) {
unset($this->aValidators[$iKey]);
}
}
return $this;
}
/**
* Note : Function is protected as aErrorMessages should not be add from outside
*
* @param string $sErrorMessage
*
* @return $this
*/
protected function AddErrorMessage(string $sErrorMessage)
{
$this->aErrorMessages[] = $sErrorMessage;
return $this;
}

View File

@@ -21,8 +21,7 @@
namespace Combodo\iTop\Form\Field;
use Closure;
use Dict;
use ormLinkSet;
use Combodo\iTop\Form\Validator\LinkedSetValidator;
/**
* Description of LinkedSetField
@@ -55,7 +54,7 @@ class LinkedSetField extends AbstractSimpleField
protected $aLimitedAccessItemIDs;
/** @var array $aAttributesToDisplay */
protected $aAttributesToDisplay;
/** @var array $aLnkAttributesToDisplay */
/** @var array $aLnkAttributesToDisplay attcode as key */
protected $aLnkAttributesToDisplay;
/** @var string $sSearchEndpoint */
protected $sSearchEndpoint;
@@ -281,25 +280,25 @@ class LinkedSetField extends AbstractSimpleField
public function GetLnkAttributesToDisplay(bool $bAttCodesOnly = false)
{
return ($bAttCodesOnly) ? array_keys($this->aLnkAttributesToDisplay) : $this->aLnkAttributesToDisplay;
}
}
/**
*
* @since 3.1
*
* @param array $aAttributesToDisplay
*
* @return $this
*/
public function SetLnkAttributesToDisplay(array $aAttributesToDisplay)
{
$this->aLnkAttributesToDisplay = $aAttributesToDisplay;
/**
* @param array $aAttributesToDisplay
* @return $this
* @since 3.1.0 N°803
*/
public function SetLnkAttributesToDisplay(array $aAttributesToDisplay)
{
$this->aLnkAttributesToDisplay = $aAttributesToDisplay;
return $this;
}
$this->RemoveValidatorsOfClass(LinkedSetValidator::class);
$this->AddValidator(new LinkedSetValidator($aAttributesToDisplay));
/**
* @return string|null
return $this;
}
/**
* @return string|null
*/
public function GetSearchEndpoint()
{
@@ -349,39 +348,4 @@ class LinkedSetField extends AbstractSimpleField
{
return in_array($iItemID, $this->aLimitedAccessItemIDs, false);
}
/** @inheritdoc @since 3.1 */
public function Validate()
{
$bValid = parent::Validate();
/** @var ormLinkSet $oSet */
$oSet = $this->GetCurrentValue();
// retrieve displayed attributes
$aAttributesToDisplayCodes = $this->GetLnkAttributesToDisplay(true);
// validate each links...
/** @var \DBObject $oItem */
foreach ($oSet as $oItem) {
$aChanges = $oItem->ListChanges();
foreach ($aChanges as $sAttCode => $value) {
if (!in_array($sAttCode, $aAttributesToDisplayCodes)) {
continue;
}
$res = $oItem->CheckValue($sAttCode);
if ($res !== true) {
$sAttLabel = $this->GetLabel($sAttCode);
$sItem = $oItem->Get('friendlyname') != '' ? $oItem->Get('friendlyname') : Dict::S('UI:Links:NewItem');
$sIssue = Dict::Format('Core:CheckValueError', $sAttLabel, $sAttCode, $res);
$this->AddErrorMessage('<b>'.$sItem.' : </b>'.$sIssue);
$bValid = false;
}
}
}
$oSet->Rewind();
return $bValid;
}
}

View File

@@ -20,8 +20,7 @@
namespace Combodo\iTop\Form\Field;
use Closure;
use ContextTag;
use utils;
use Combodo\iTop\Form\Validator\MultipleChoicesValidator;
/**
* Description of MultipleChoicesField
@@ -51,6 +50,8 @@ abstract class MultipleChoicesField extends AbstractSimpleField
$this->bMultipleValuesEnabled = static::DEFAULT_MULTIPLE_VALUES_ENABLED;
$this->aChoices = array();
$this->currentValue = array();
$this->InitValidators();
}
/**
@@ -179,69 +180,68 @@ abstract class MultipleChoicesField extends AbstractSimpleField
public function SetChoices(array $aChoices)
{
$this->aChoices = $aChoices;
$this->InitValidators();
return $this;
}
/**
* @param string $sId
* @param null $choice
* @param null $choice choice value (eg label)
*
* @return $this
*/
public function AddChoice(string $sId, $choice = null)
{
if ($choice === null)
{
if ($choice === null) {
$choice = $sId;
}
$this->aChoices[$sId] = $choice;
return $this;
}
/**
* @param string $sId
*
* @return $this
*/
public function RemoveChoice(string $sId)
{
if (in_array($sId, $this->aChoices))
{
unset($this->aChoices[$sId]);
}
return $this;
}
$this->InitValidators();
public function Validate() {
$this->SetValid(true);
$this->EmptyErrorMessages();
return $this;
}
if ((ContextTag::Check(ContextTag::TAG_REST)) && ($this->GetReadOnly() === false)) {
// Only doing the check when coming from the REST API, as the user portal might send invalid values (see VerifyCurrentValue() method below)
// Also do not check read only fields, are they are send with a null value when submitting request template from the console
if (count($this->currentValue) > 0) {
foreach ($this->currentValue as $sCode => $value) {
if (utils::IsNullOrEmptyString($value)) {
continue;
}
if (false === array_key_exists($value, $this->aChoices)) {
$this->SetValid(false);
$this->AddErrorMessage("Value ({$value}) is not part of the field possible values list");
}
}
}
}
/**
* @param string $sId
*
* @return $this
*/
public function RemoveChoice(string $sId)
{
if (in_array($sId, $this->aChoices)) {
unset($this->aChoices[$sId]);
}
foreach ($this->GetValidators() as $oValidator) {
foreach ($this->currentValue as $value) {
if (!preg_match($oValidator->GetRegExp(true), $value)) {
$this->SetValid(false);
$this->AddErrorMessage($oValidator->GetErrorMessage());
}
}
}
return $this;
}
return $this->GetValid();
}
/**
* @param bool $bReadOnly
* @return MultipleChoicesField
* @since 3.1.0 N°6414
*/
public function SetReadOnly(bool $bReadOnly)
{
if ($bReadOnly) {
/** @noinspection PhpRedundantOptionalArgumentInspection */
$this->SetValidationDisabled(true);
} else {
$this->SetValidationDisabled(false);
}
return parent::SetReadOnly($bReadOnly);
}
/**
* @return void
* @since 3.1.0 N°6414
*/
protected function InitValidators(): void
{
$this->RemoveValidatorsOfClass(MultipleChoicesValidator::class);
$this->AddValidator(new MultipleChoicesValidator($this->aChoices));
}
}

View File

@@ -28,5 +28,4 @@ class MultipleSelectField extends SelectField
{
/** @inheritDoc */
const DEFAULT_MULTIPLE_VALUES_ENABLED = true;
}

View File

@@ -20,18 +20,14 @@
namespace Combodo\iTop\Form\Field;
use BinaryExpression;
use Closure;
use Combodo\iTop\Form\Helper\FieldHelper;
use Combodo\iTop\Form\Validator\AbstractValidator;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use ContextTag;
use DBObjectSet;
use Combodo\iTop\Form\Validator\SelectObjectValidator;
use DBSearch;
use DeprecatedCallsLog;
use FieldExpression;
use MetaModel;
use ScalarExpression;
use utils;
/**
* Description of SelectObjectField
@@ -95,6 +91,9 @@ class SelectObjectField extends AbstractSimpleField
{
$this->oSearch = $oSearch;
$this->RemoveValidatorsOfClass(SelectObjectValidator::class);
$this->AddValidator(new SelectObjectValidator($oSearch));
return $this;
}
@@ -229,27 +228,6 @@ class SelectObjectField extends AbstractSimpleField
return $this->sSearchEndpoint;
}
public function Validate() {
if ((ContextTag::Check(ContextTag::TAG_REST)) && ($this->GetReadOnly() === false)) {
// Only doing the check when coming from the REST API, as the user portal might send invalid values (see VerifyCurrentValue() method below)
// Also do not check read only fields, are they are send with a null value when submitting request template from the console
$sCurrentValueForExtKey = $this->currentValue;
if (utils::IsNotNullOrEmptyString($sCurrentValueForExtKey) && ($sCurrentValueForExtKey !== 0)) {
$oSetForExistingCurrentValue = $this->GetObjectsSet();
$iObjectsCount = $oSetForExistingCurrentValue->CountWithLimit(1);
if ($iObjectsCount === 0) {
$this->SetValid(false);
$this->AddErrorMessage("Value $sCurrentValueForExtKey does not match the corresponding filter set");
return $this->GetValid();
}
}
}
return parent::Validate();
}
/**
* Resets current value if not among allowed ones.
* By default, reset is done ONLY when the field is not read-only.
@@ -283,25 +261,11 @@ class SelectObjectField extends AbstractSimpleField
*/
public function ResetCurrentValueIfNotAmongAllowedValues(bool $bAlways = false) {
if (!$this->GetReadOnly() || $bAlways) {
$oValuesSet = $this->GetObjectsSet();
$oValuesSet = FieldHelper::GetObjectsSetFromSearchAndCurrentValueId($this->oSearch, $this->currentValue);
if ($oValuesSet->Count() === 0) {
$this->currentValue = null;
}
}
}
final protected function GetObjectsSet() {
$sCurrentValueForExtKey = $this->currentValue;
$oSearchForExistingCurrentValue = $this->oSearch->DeepClone();
$oCheckIdAgainstCurrentValueExpression = new BinaryExpression(
new FieldExpression('id', $oSearchForExistingCurrentValue->GetClassAlias()),
'=',
new ScalarExpression($sCurrentValueForExtKey)
);
$oSearchForExistingCurrentValue->AddConditionExpression($oCheckIdAgainstCurrentValueExpression);
return new DBObjectSet($oSearchForExistingCurrentValue);
}
}