Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW Show more information in ValidationException message #11562

Open
wants to merge 1 commit into
base: 6.0
Choose a base branch
from

Conversation

emteknetnz
Copy link
Member

@emteknetnz emteknetnz commented Jan 16, 2025

Issue #11423

Also added in a bunch of strong typing while was doing it, removed nullable params and instead use blank string to denote "nothing", and changing a string|bool param param to just string

@emteknetnz emteknetnz force-pushed the pulls/6.0/validation-values branch from e148ac2 to 2a63e66 Compare January 16, 2025 05:05
@emteknetnz emteknetnz changed the title ENH Pass invalid value to ValidationResult NEW Show more information in ValidationException message Jan 16, 2025
@emteknetnz emteknetnz force-pushed the pulls/6.0/validation-values branch from 2a63e66 to 89727e1 Compare January 16, 2025 22:46
@emteknetnz emteknetnz closed this Jan 16, 2025
@emteknetnz emteknetnz reopened this Jan 16, 2025
@emteknetnz emteknetnz force-pushed the pulls/6.0/validation-values branch 5 times, most recently from 6b90450 to 53569d9 Compare January 17, 2025 02:59
@emteknetnz emteknetnz force-pushed the pulls/6.0/validation-values branch from 53569d9 to dd52956 Compare January 17, 2025 04:15
@emteknetnz emteknetnz force-pushed the pulls/6.0/validation-values branch 2 times, most recently from c433cf9 to 263d324 Compare January 30, 2025 03:16
@emteknetnz emteknetnz force-pushed the pulls/6.0/validation-values branch from 263d324 to adf3ca1 Compare January 30, 2025 23:17
@emteknetnz emteknetnz marked this pull request as ready for review January 31, 2025 04:03
Copy link
Member

@GuySartorelli GuySartorelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't tested locally yet - but the below stick out to me

Comment on lines +20 to +22
* @param $value The value to validate
* @param Constraint|Constraint[] $constraints a constraint or array of constraints to validate against
* @param string $fieldName The field name the value relates to, if relevant
* @param $fieldName The field name the value relates to, if relevant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@param must always include the type. IDEs (and probably our API docs) don't know what to do with a non-typed param and they treat the param name as the type and the first word of the description as the name.

So @param $value The value to validate makes IDEs think $value is the type, and The is the name.

Either remove the @param entirely or revert the change. Personally I'd just remove the $value and $fieldName @params as they're self-explanatory, and only keep the $constraints @param.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to any other PHPDoc changes in this PR.

Comment on lines +55 to +64
if ($this->doShowAdditionalInfo()) {
if ($message['fieldName']) {
$exceptionMessage .= ' - fieldName: ' . $message['fieldName'];
}
$dataClass = $result->getDataClass();
if (is_subclass_of($dataClass, DataObject::class, true)) {
$exceptionMessage .= ', recordID: ' . $result->getRecordID();
$exceptionMessage .= ', dataClass: ' . $dataClass;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there ever a reason not to include this additional info in the exception message? e.g. where ValidationException gets thrown for a CMS form submission and $result is a ValidationResult, the actual message isn't parsed by the UI - instead the validation result's individual messages are included in JSON in the response and are used appropriately.

$exceptionMessage .= ', recordID: ' . $result->getRecordID();
$exceptionMessage .= ', dataClass: ' . $dataClass;
}
}
break;
}
} elseif (is_string($result)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, where only a string is provided and no ValidationResult, seems to be where the conditional $this->doShowAdditionalInfo() should be used.

We should definitely be adding this extra context here when it's appropriate.

Comment on lines +100 to +103
// e.g. dev/build
if (is_a(Controller::curr(), DevelopmentAdmin::class)) {
return true;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why specifically that controller? I feel like it makes more sense to have a configurable deny list, and say "If the controller is in the deny list, return false" (e.g. LeftAndMain and maybe even up to FormSchemaController should be in the deny list, but if the current controller is front-end or custom, we should assume the extra context is needed unless developers say otherwise)

@@ -16,6 +17,7 @@
class ValidationResult
{
use Injectable;
use Configurable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add configurable here?

/**
* The record ID of the object being validated.
*/
private int $recordID = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In various places we allow mixed for ID (e.g. see the recent updated to SS_List and related, which have mixed $id for the byID() method). We should do the same here - there's no guarantee that all projects use numeric IDs for all of their models.

Someone might have a model (not a subclass of DataObject) that integrates with a third-party service and uses a GUID - and they may want to use a ValidationResult with that model.

Suggested change
private int $recordID = 0;
private mixed $recordID = null;

Please also update the constructor, setter, and getter.

protected array $messages = [];

/**
* The class of the DataObject being validated.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* The class of the DataObject being validated.
* The class of the model being validated.

Comment on lines +276 to +285
if ($thisDataClass === '' && $otherDataClass !== '') {
$this->setDataClass($otherDataClass);
} elseif ($thisDataClass !== '' && $otherDataClass === '') {
$other->setDataClass($thisDataClass);
}
if ($thisRecordID === 0 && $otherRecordID !== 0) {
$this->setRecordID($otherRecordID);
} elseif ($thisRecordID !== 0 && $otherRecordID === 0) {
$other->setRecordID($thisRecordID);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be altering $other.

Suggested change
if ($thisDataClass === '' && $otherDataClass !== '') {
$this->setDataClass($otherDataClass);
} elseif ($thisDataClass !== '' && $otherDataClass === '') {
$other->setDataClass($thisDataClass);
}
if ($thisRecordID === 0 && $otherRecordID !== 0) {
$this->setRecordID($otherRecordID);
} elseif ($thisRecordID !== 0 && $otherRecordID === 0) {
$other->setRecordID($thisRecordID);
}
if ($thisDataClass === '' && $otherDataClass !== '') {
$this->setDataClass($otherDataClass);
}
if ($thisRecordID === 0 && $otherRecordID !== 0) {
$this->setRecordID($otherRecordID);
}

@@ -1257,7 +1257,7 @@ public function forceChange()
*/
public function validate(): ValidationResult
{
$result = ValidationResult::create();
$result = ValidationResult::create(get_class($this), $this->ID);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the only place this gets set in core right now?

What about if I call validate() on a form, and the form belongs to a specific model? E.g. I could conceivably set up a PHP Form for some record that can be submitted from CLI using the fancy symfony console IO functionality.
Personally I think that scenario is likely to contain all the relevant context already, but want to get your thoughts and make sure that sort of thing has been considered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants