component_library-1.0.x-dev/src/Form/ComponentLibraryPatternForm.php
src/Form/ComponentLibraryPatternForm.php
<?php
declare(strict_types=1);
namespace Drupal\component_library\Form;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Utility\Tags;
use Drupal\component_library\ComponentLibraryVariantInterface;
use Drupal\component_library\Entity\ComponentLibraryPattern;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
/**
* Component library pattern form.
*
* @property \Drupal\component_library\ComponentLibraryPatternInterface $entity
*/
final class ComponentLibraryPatternForm extends EntityForm {
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state): array {
$form = parent::form($form, $form_state);
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $this->entity->label(),
'#description' => $this->t('Label for the component library pattern.'),
'#required' => TRUE,
];
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $this->entity->id(),
'#maxlength' => 31,
'#machine_name' => [
'exists' => \sprintf('%s::load', ComponentLibraryPattern::class),
],
'#disabled' => !$this->entity->isNew(),
];
$form['description'] = [
'#type' => 'textarea',
'#title' => $this->t('Description'),
'#default_value' => $this->entity->get('description'),
'#description' => $this->t('Description of the component library pattern.'),
'#rows' => 3,
];
$form['tags'] = [
'#type' => 'textfield',
'#title' => $this->t('Administrative tags'),
'#description' => $this->t('Enter a comma-separated list of words to describe your pattern.'),
'#default_value' => Tags::implode($this->entity->get('tags')),
'#autocomplete_route_name' => 'component_library.tags_autocomplete',
];
$code_mirror_settings = [
'autoCloseTags' => TRUE,
'buttons' => [
'undo',
'redo',
'enlarge',
'shrink',
],
'foldGutter' => TRUE,
'lineNumbers' => TRUE,
'lineWrapping' => TRUE,
'styleActiveLine' => TRUE,
'toolbar' => TRUE,
'lineSeparator' => "\n",
'mode' => 'yaml',
];
$form['data'] = [
'#type' => 'codemirror',
'#codemirror' => $code_mirror_settings,
'#title' => $this->t('Preview data'),
'#default_value' => $this->entity->get('data'),
'#element_validate' => [[self::class, 'yamlValidate']],
'#suffix' => '<output id="component-library-variant"></output>',
];
$rows = [];
foreach ($this->entity->getVariants() as $variation) {
$operations = [
'#type' => 'operations',
'#links' => $this->getVariationOperationLinks($variation),
];
$rows[] = [
'id' => $variation->id(),
'label' => $variation->label(),
'operations' => [
'data' => $operations,
],
];
}
if (!$this->entity->isNew()) {
$form['variants'] = [
'#type' => 'table',
'#caption' => $this->t('Variants'),
'#empty' => $this->t('No variants have been created yet. <a href=":url">Add new variant</a>.', [
':url' => Url::fromRoute('entity.component_library_variant.add_form', [], [
'query' => [
'destination' => Url::fromRoute('<current>')->toString(),
'pattern' => $this->entity->id(),
],
])->toString(),
]),
'#header' => [
'id' => $this->t('ID'),
'label' => $this->t('Label'),
'operations' => $this->t('Operations'),
],
'#rows' => $rows,
];
}
return $form;
}
private function getVariationOperationLinks(ComponentLibraryVariantInterface $variant): array {
$operations = [];
if ($variant->access('update')) {
$operations['edit'] = [
'title' => $this->t('Edit'),
'url' => $variant->toUrl('edit-form'),
];
}
if ($variant->access('view')) {
$operations['view'] = [
'title' => $this->t('View'),
'url' => $variant->toUrl(),
];
}
if ($variant->access('delete')) {
$operations['delete'] = [
'title' => $this->t('Delete'),
'url' => $variant->toUrl('delete-form'),
];
}
if ($variant->access('duplicate')) {
$operations['delete'] = [
'title' => $this->t('Duplicate'),
'url' => $variant->toUrl('duplicate-form'),
];
}
return $operations;
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state): int {
$result = parent::save($form, $form_state);
$message_args = ['%label' => $this->entity->label()];
$message = $result == \SAVED_NEW
? $this->t('Created new component library pattern %label.', $message_args)
: $this->t('Updated component library pattern %label.', $message_args);
$this->messenger()->addStatus($message);
$form_state->setRedirectUrl($this->entity->toUrl('collection'));
return $result;
}
/**
* {@inheritdoc}
*/
protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state): void {
// Convert config into line feed endings so YAML config is well formed.
$data = \is_array($form_state->getValue('data')) ? $form_state->getValue('data') : \implode("\n", \preg_split('(\r\n|\r)', $form_state->getValue('data')));
$form_state->setValue('data', $data);
$tags = \is_array($form_state->getValue('tags')) ? $form_state->getValue('tags') : Tags::explode($form_state->getValue('tags'), '');
$form_state->setValue('tags', $tags);
parent::copyFormValuesToEntity($entity, $form, $form_state);
}
/**
* Use element validator to make sure that YAML is valid.
*/
public static function yamlValidate(array $element, FormStateInterface $form_state): void {
$yaml_string = $form_state->getValue($element['#parents']);
if ($yaml_string === '') {
return;
}
try {
$data = Yaml::decode($yaml_string);
if (!\is_array($data)) {
$form_state->setError($element, new TranslatableMarkup('The preview data should be defined as array.'));
}
else {
// @see \Drupal\ui_patterns\TypedData\PatternDataDefinition::reserved
$reserved_keys = [
'id',
'type',
'theme',
'context',
'use',
'attributes',
];
foreach (\array_intersect($reserved_keys, \array_keys($data)) as $name) {
$form_state->setError($element, new TranslatableMarkup('The name "@name" is a reserved keyword and cannot be used as Twig variable.', ['@name' => $name]));
}
}
}
catch (InvalidDataTypeException $exception) {
$form_state->setError($element, new TranslatableMarkup('This value should be valid YAML.'));
}
}
}
