class YamlFormEntityElementsValidator in YAML Form 8
Defines a class to validate form elements.
Hierarchy
- class \Drupal\yamlform\YamlFormEntityElementsValidator uses StringTranslationTrait
Expanded class hierarchy of YamlFormEntityElementsValidator
3 files declare their use of YamlFormEntityElementsValidator
- YamlFormEntityElementsValidationUnitTest.php in src/
Tests/ YamlFormEntityElementsValidationUnitTest.php - YamlFormUiElementDeleteForm.php in modules/
yamlform_ui/ src/ Form/ YamlFormUiElementDeleteForm.php - YamlFormUiElementFormBase.php in modules/
yamlform_ui/ src/ Form/ YamlFormUiElementFormBase.php
1 string reference to 'YamlFormEntityElementsValidator'
1 service uses YamlFormEntityElementsValidator
File
- src/
YamlFormEntityElementsValidator.php, line 16
Namespace
Drupal\yamlformView source
class YamlFormEntityElementsValidator {
use StringTranslationTrait;
/**
* The form being validated.
*
* @var \Drupal\yamlform\YamlFormInterface
*/
protected $yamlform;
/**
* The raw elements value.
*
* @var string
*/
protected $elementsRaw;
/**
* The raw original elements value.
*
* @var string
*/
protected $originalElementsRaw;
/**
* The parsed elements array.
*
* @var array
*/
protected $elements;
/**
* The parsed original elements array.
*
* @var array
*/
protected $originalElements;
/**
* Validate form elements.
*
* @param \Drupal\yamlform\YamlFormInterface $yamlform
* A form.
*
* @return array|null
* An array of error messages or NULL is the elements are valid.
*/
public function validate(YamlFormInterface $yamlform) {
$this->yamlform = $yamlform;
$this->elementsRaw = $yamlform
->getElementsRaw();
$this->originalElementsRaw = $yamlform
->getElementsOriginalRaw();
// Validate required.
if ($message = $this
->validateRequired()) {
return [
$message,
];
}
// Validate contain valid YAML.
if ($message = $this
->validateYaml()) {
return [
$message,
];
}
$this->elements = Yaml::decode($this->elementsRaw);
$this->originalElements = Yaml::decode($this->originalElementsRaw);
// Validate elements are an array.
if ($message = $this
->validateArray()) {
return [
$message,
];
}
// Validate duplicate element name.
if ($messages = $this
->validateDuplicateNames()) {
return $messages;
}
// Validate ignored properties.
if ($messages = $this
->validateProperties()) {
return $messages;
}
// Validate submission data.
if ($messages = $this
->validateSubmissions()) {
return $messages;
}
// Validate hierarchy.
if ($messages = $this
->validateHierarchy()) {
return $messages;
}
// Validate rendering.
if ($message = $this
->validateRendering()) {
return [
$message,
];
}
return NULL;
}
/**
* Validate elements are required.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup|null
* If not valid an error message.
*/
protected function validateRequired() {
return empty($this->elementsRaw) ? $this
->t('Elements are required') : NULL;
}
/**
* Validate elements is validate YAML.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup|null
* If not valid an error message.
*/
protected function validateYaml() {
try {
Yaml::decode($this->elementsRaw);
return NULL;
} catch (\Exception $exception) {
return $this
->t('Elements are not valid. @message', [
'@message' => $exception
->getMessage(),
]);
}
}
/**
* Validate elements are an array of elements.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup|null
* If not valid an error message.
*/
protected function validateArray() {
if (!is_array($this->elements)) {
return $this
->t('Elements are not valid. YAML must contain an associative array of elements.');
}
return NULL;
}
/**
* Validate elements does not contain duplicate names.
*
* @return array|null
* If not valid, an array of error messages.
*/
protected function validateDuplicateNames() {
$duplicate_names = [];
$this
->getDuplicateNamesRecursive($this->elements, $duplicate_names);
if ($duplicate_names = array_filter($duplicate_names)) {
$messages = [];
foreach ($duplicate_names as $duplicate_name => $duplicate_count) {
$line_numbers = $this
->getLineNumbers('/^\\s*(["\']?)' . preg_quote($duplicate_name, '/') . '\\1\\s*:/');
$t_args = [
'%name' => $duplicate_name,
'@lines' => $this
->formatPlural(count($line_numbers), $this
->t('line'), $this
->t('lines')),
'@line_numbers' => YamlFormArrayHelper::toString($line_numbers),
];
$messages[] = $this
->t('Elements contain a duplicate element name %name found on @lines @line_numbers.', $t_args);
}
return $messages;
}
return NULL;
}
/**
* Recurse through elements and collect an associative array keyed by name and number of duplicate instances.
*
* @param array $elements
* An array of elements.
* @param array $names
* An associative array keyed by name and number of duplicate instances.
*/
protected function getDuplicateNamesRecursive(array $elements, array &$names) {
foreach ($elements as $key => &$element) {
if (Element::property($key) || !is_array($element)) {
continue;
}
if (isset($element['#type'])) {
if (!isset($names[$key])) {
$names[$key] = 0;
}
else {
++$names[$key];
}
}
$this
->getDuplicateNamesRecursive($element, $names);
}
}
/**
* Validate that elements are not using ignored properties.
*
* @return array|null
* If not valid, an array of error messages.
*/
protected function validateProperties() {
$ignored_properties = YamlFormElementHelper::getIgnoredProperties($this->elements);
if ($ignored_properties) {
$messages = [];
foreach ($ignored_properties as $ignored_property) {
$line_numbers = $this
->getLineNumbers('/^\\s*(["\']?)' . preg_quote($ignored_property, '/') . '\\1\\s*:/');
$t_args = [
'%property' => $ignored_property,
'@lines' => $this
->formatPlural(count($line_numbers), $this
->t('line'), $this
->t('lines')),
'@line_numbers' => YamlFormArrayHelper::toString($line_numbers),
];
$messages[] = $this
->t('Elements contain an unsupported %property property found on @lines @line_numbers.', $t_args);
}
return $messages;
}
return NULL;
}
/**
* Validate that element are not deleted when the form has submissions.
*
* @return array|null
* If not valid, an array of error messages.
*/
protected function validateSubmissions() {
if (!$this->yamlform
->hasSubmissions()) {
return NULL;
}
$element_keys = [];
if ($this->elements) {
$this
->getElementKeysRecursive($this->elements, $element_keys);
}
$original_element_keys = [];
if ($this->originalElements) {
$this
->getElementKeysRecursive($this->originalElements, $original_element_keys);
}
if ($missing_element_keys = array_diff_key($original_element_keys, $element_keys)) {
$messages = [];
foreach ($missing_element_keys as $missing_element_key) {
// Display an error message with 3 possible approaches to safely
// deleting or hiding an element.
$items = [];
$items[] = $this
->t('<a href=":href">Delete all submissions</a> to this form.', [
':href' => $this->yamlform
->toUrl('results-clear')
->toString(),
]);
if (\Drupal::moduleHandler()
->moduleExists('yamlform_ui')) {
$items[] = $this
->t('<a href=":href">Delete this individual element</a> using the form UI.', [
':href' => Url::fromRoute('entity.yamlform_ui.element.delete_form', [
'yamlform' => $this->yamlform
->id(),
'key' => $missing_element_key,
])
->toString(),
]);
}
else {
$items[] = $this
->t('<a href=":href">Enable the YAML Form UI module</a> and safely delete this element.', [
':href' => Url::fromRoute('system.modules_list')
->toString(),
]);
}
$items[] = $this
->t("Hide this element by setting its <code>'#access'</code> property to <code>false</code>.");
$build = [
'message' => [
'#markup' => $this
->t('The %key element can not be removed because the %title form has <a href=":href">results</a>.', [
'%title' => $this->yamlform
->label(),
'%key' => $missing_element_key,
':href' => $this->yamlform
->toUrl('results-submissions')
->toString(),
]),
],
'items' => [
'#theme' => 'item_list',
'#items' => $items,
],
];
$messages[] = \Drupal::service('renderer')
->renderPlain($build);
}
return $messages;
}
return NULL;
}
/**
* Recurse through elements and collect an associative array of deleted element names.
*
* @param array $elements
* An array of elements.
* @param array $names
* An array tracking deleted element names.
*/
protected function getElementKeysRecursive(array $elements, array &$names) {
foreach ($elements as $key => &$element) {
if (Element::property($key) || !is_array($element)) {
continue;
}
if (isset($element['#type'])) {
$names[$key] = $key;
}
$this
->getElementKeysRecursive($element, $names);
}
}
/**
* Validate element hierarchy.
*
* @return array|null
* If not valid, an array of error messages.
*/
protected function validateHierarchy() {
$elements = $this->yamlform
->getElementsInitializedAndFlattened();
$messages = [];
foreach ($elements as $key => $element) {
/** @var \Drupal\yamlform\YamlFormElementManagerInterface $element_manager */
$element_manager = \Drupal::service('plugin.manager.yamlform.element');
$plugin_id = $element_manager
->getElementPluginId($element);
/** @var \Drupal\yamlform\YamlFormElementInterface $yamlform_element */
$yamlform_element = $element_manager
->createInstance($plugin_id, $element);
$t_args = [
'%title' => !empty($element['#title']) ? $element['#title'] : $key,
'@type' => $yamlform_element
->getTypeName(),
];
if ($yamlform_element
->isRoot() && !empty($element['#yamlform_parent_key'])) {
$messages[] = $this
->t('The %title (@type) is a root element that can not be used as child to another element', $t_args);
}
elseif (!$yamlform_element
->isContainer($element) && !empty($element['#yamlform_children'])) {
$messages[] = $this
->t('The %title (@type) is a form element that can not have any child elements.', $t_args);
}
}
return $messages;
}
/**
* Validate that elements are a valid render array.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup|string|null
* If not valid an error message.
*
* @see \Drupal\Core\Entity\EntityFormBuilder
* @see \Drupal\yamlform\Entity\YamlForm::getSubmissionForm()
*/
protected function validateRendering() {
set_error_handler('_yamlform_entity_element_validate_rendering_error_handler');
try {
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = \Drupal::service('entity_type.manager');
/** @var \Drupal\Core\Form\FormBuilderInterface $form_builder */
$form_builder = \Drupal::service('form_builder');
/** @var \Drupal\yamlform\YamlFormSubmissionInterface $yamlform_submission */
$yamlform_submission = $entity_type_manager
->getStorage('yamlform_submission')
->create([
'yamlform' => $this->yamlform,
]);
$form_object = $entity_type_manager
->getFormObject('yamlform_submission', 'default');
$form_object
->setEntity($yamlform_submission);
$form_state = (new FormState())
->setFormState([]);
$form_builder
->buildForm($form_object, $form_state);
$message = NULL;
} catch (\Exception $exception) {
$message = $exception
->getMessage();
}
set_error_handler('_drupal_error_handler');
if ($message) {
$build = [
'title' => [
'#markup' => $this
->t('Unable to render elements, please view the below message(s) and the error log.'),
],
'items' => [
'#theme' => 'item_list',
'#items' => [
$message,
],
],
];
return \Drupal::service('renderer')
->renderPlain($build);
}
return $message;
}
/**
* Get the line numbers for given pattern in the form's elements string.
*
* @param string $pattern
* A regular expression.
*
* @return array
* An array of line numbers.
*/
protected function getLineNumbers($pattern) {
$lines = explode(PHP_EOL, $this->elementsRaw);
$line_numbers = [];
foreach ($lines as $index => $line) {
if (preg_match($pattern, $line)) {
$line_numbers[] = $index + 1;
}
}
return $line_numbers;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
YamlFormEntityElementsValidator:: |
protected | property | The parsed elements array. | |
YamlFormEntityElementsValidator:: |
protected | property | The raw elements value. | |
YamlFormEntityElementsValidator:: |
protected | property | The parsed original elements array. | |
YamlFormEntityElementsValidator:: |
protected | property | The raw original elements value. | |
YamlFormEntityElementsValidator:: |
protected | property | The form being validated. | |
YamlFormEntityElementsValidator:: |
protected | function | Recurse through elements and collect an associative array keyed by name and number of duplicate instances. | |
YamlFormEntityElementsValidator:: |
protected | function | Recurse through elements and collect an associative array of deleted element names. | |
YamlFormEntityElementsValidator:: |
protected | function | Get the line numbers for given pattern in the form's elements string. | |
YamlFormEntityElementsValidator:: |
public | function | Validate form elements. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate elements are an array of elements. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate elements does not contain duplicate names. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate element hierarchy. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate that elements are not using ignored properties. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate that elements are a valid render array. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate elements are required. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate that element are not deleted when the form has submissions. | |
YamlFormEntityElementsValidator:: |
protected | function | Validate elements is validate YAML. |