protected function EntityReferenceLayoutWidget::formMultipleElements in Entity Reference with Layout 8
Builds the main widget form array container/wrapper.
Form elements for individual items are built by formElement().
Overrides WidgetBase::formMultipleElements
File
- src/
Plugin/ Field/ FieldWidget/ EntityReferenceLayoutWidget.php, line 239
Class
- EntityReferenceLayoutWidget
- Entity Reference with Layout field widget.
Namespace
Drupal\entity_reference_layout\Plugin\Field\FieldWidgetCode
protected function formMultipleElements(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
$parents = $form['#parents'];
$widget_state = static::getWidgetState($parents, $this->fieldName, $form_state);
$this->wrapperId = Html::getId(implode('-', $parents) . $this->fieldName . '-wrapper');
$this->itemFormWrapperId = Html::getId(implode('-', $parents) . $this->fieldName . '-form');
$handler_settings = $items
->getSetting('handler_settings');
$layout_bundles = $handler_settings['layout_bundles'] ?? [];
$target_bundles = $handler_settings['target_bundles'] ?? [];
if (!empty($handler_settings['negate'])) {
$target_bundles_options = array_keys($handler_settings['target_bundles_drag_drop']);
$target_bundles = array_diff($target_bundles_options, $target_bundles);
}
$title = $this->fieldDefinition
->getLabel();
$description = FieldFilteredMarkup::create(\Drupal::token()
->replace($this->fieldDefinition
->getDescription()));
/** @var \Drupal\Core\Entity\ContentEntityInterface $host */
$host = $items
->getEntity();
// Detect if we are translating.
$this
->initIsTranslating($form_state, $host);
// Save items to widget state when the form first loads.
if (empty($widget_state['items'])) {
$widget_state['items'] = [];
foreach ($items as $delta => $item) {
if ($item->entity instanceof ParagraphInterface) {
$langcode = $form_state
->get('langcode');
if (!$this->isTranslating) {
// Set the langcode if we are not translating.
$langcode_key = $item->entity
->getEntityType()
->getKey('langcode');
if ($item->entity
->get($langcode_key)->value != $langcode) {
// If a translation in the given language already exists,
// switch to that. If there is none yet, update the language.
if ($item->entity
->hasTranslation($langcode)) {
$item->entity = $item->entity
->getTranslation($langcode);
}
else {
$item->entity
->set($langcode_key, $langcode);
}
}
}
else {
// Add translation if missing for the target language.
if (!$item->entity
->hasTranslation($langcode)) {
// Get the selected translation of the paragraph entity.
$entity_langcode = $item->entity
->language()
->getId();
$source = $form_state
->get([
'content_translation',
'source',
]);
$source_langcode = $source ? $source
->getId() : $entity_langcode;
// Make sure the source language version is used if available.
// Fetching the translation without this check could lead valid
// scenario to have no paragraphs items in the source version of
// to an exception.
if ($item->entity
->hasTranslation($source_langcode)) {
$entity = $item->entity
->getTranslation($source_langcode);
}
// The paragraphs entity has no content translation source field
// if no paragraph entity field is translatable,
// even if the host is.
if ($item->entity
->hasField('content_translation_source')) {
// Initialise the translation with source language values.
$item->entity
->addTranslation($langcode, $entity
->toArray());
$translation = $item->entity
->getTranslation($langcode);
$manager = \Drupal::service('content_translation.manager');
$manager
->getTranslationMetadata($translation)
->setSource($item->entity
->language()
->getId());
}
}
// If any paragraphs type is translatable do not switch.
if ($item->entity
->hasField('content_translation_source')) {
// Switch the paragraph to the translation.
$item->entity = $item->entity
->getTranslation($langcode);
}
}
}
$widget_state['items'][$delta] = [
'entity' => $item->entity,
'layout' => $item->layout,
'config' => $item->config,
'options' => $item->options,
'new_region' => NULL,
'parent_weight' => NULL,
];
}
}
// Handle asymmetric translation if field is translatable
// by duplicating items for enabled languages.
if ($items
->getFieldDefinition()
->isTranslatable()) {
$langcode = $this->languageManager
->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
->getId();
foreach ($widget_state['items'] as $delta => $item) {
if (empty($item['entity']) || $item['entity']
->get('langcode')->value == $langcode) {
continue;
}
$duplicate = $item['entity']
->createDuplicate();
/** @var \Drupal\Core\Entity\EntityInterface $duplicate */
$duplicate
->set('langcode', $langcode);
$widget_state['items'][$delta]['entity'] = $duplicate;
}
}
static::setWidgetState($parents, $this->fieldName, $form_state, $widget_state);
$elements = parent::formMultipleElements($items, $form, $form_state);
if (isset($elements['#prefix'])) {
unset($elements['#prefix']);
}
if (isset($elements['#suffix'])) {
unset($elements['#suffix']);
}
$elements += [
'#title' => $title,
'#description' => $description,
];
$elements['#theme'] = 'entity_reference_layout_widget';
$elements['#id'] = $this->wrapperId;
// Add logic for new elements Add, if not in a translation context.
if ($this
->allowReferenceChanges()) {
// Button to add new section and other paragraphs.
$elements['add_more'] = [
'actions' => [
'#attributes' => [
'class' => [
'js-hide',
],
],
'#type' => 'container',
],
];
$bundle_info = $this->entityTypeBundleInfo
->getBundleInfo('paragraph');
foreach ($layout_bundles as $bundle_id) {
$elements['add_more']['actions']['section'] = [
'#type' => 'submit',
'#bundle_id' => $bundle_id,
'#host' => $items
->getEntity(),
'#value' => $this
->t('Add @label', [
'@label' => $bundle_info[$bundle_id]['label'],
]),
'#modal_label' => $this
->t('Add new @label', [
'@label' => $bundle_info[$bundle_id]['label'],
]),
'#name' => implode('_', $parents) . '_add_' . $bundle_id,
'#submit' => [
[
$this,
'newItemSubmit',
],
],
'#attributes' => [
'class' => [
'erl-add-section',
],
],
'#limit_validation_errors' => [
array_merge($parents, [
$this->fieldName,
]),
],
'#ajax' => [
'callback' => [
$this,
'elementAjax',
],
'wrapper' => $this->wrapperId,
],
'#element_parents' => $parents,
];
}
// Add other paragraph types.
$options = [];
try {
$types = [];
$bundle_ids = array_diff($target_bundles, $layout_bundles);
$target_type = $items
->getSetting('target_type');
$definition = $this->entityTypeManager
->getDefinition($target_type);
$storage = $this->entityTypeManager
->getStorage($definition
->getBundleEntityType());
foreach ($bundle_ids as $bundle_id) {
$type = $storage
->load($bundle_id);
$path = '';
// Get the icon and pass to Javascript.
if (method_exists($type, 'getIconFile')) {
try {
/** @var \Drupal\file\FileInterface $icon */
if ($icon = $type
->getIconFile()) {
$path = $icon
->toUrl();
}
} catch (\Exception $e) {
watchdog_exception('Erl, Paragraph Type Icon Generation', $e);
}
}
$options[$bundle_id] = $bundle_info[$bundle_id]['label'];
$types[] = [
'id' => $bundle_id,
'name' => $bundle_info[$bundle_id]['label'],
'image' => $path,
];
}
} catch (\Exception $e) {
watchdog_exception('Erl, add Paragraph Type', $e);
}
$elements['add_more']['actions']['type'] = [
'#title' => $this
->t('Choose type'),
'#type' => 'select',
'#options' => $options,
'#attributes' => [
'class' => [
'erl-item-type',
],
],
];
$elements['add_more']['actions']['item'] = [
'#type' => 'submit',
'#host' => $items
->getEntity(),
'#value' => $this
->t('Create New'),
'#submit' => [
[
$this,
'newItemSubmit',
],
],
'#limit_validation_errors' => [
array_merge($parents, [
$this->fieldName,
]),
],
'#attributes' => [
'class' => [
'erl-add-item',
],
],
'#ajax' => [
'callback' => [
$this,
'elementAjax',
],
'wrapper' => $this->wrapperId,
],
'#name' => implode('_', $parents) . '_add_item',
'#element_parents' => $parents,
];
// Add region and parent_delta hidden items only in this is a new entity.
// Prefix with underscore to prevent namespace collisions.
$elements['add_more']['actions']['_region'] = [
'#type' => 'hidden',
'#attributes' => [
'class' => [
'erl-new-item-region',
],
],
];
$elements['add_more']['actions']['_parent_weight'] = [
'#type' => 'hidden',
'#attributes' => [
'class' => [
'erl-new-item-parent',
],
],
];
// Template for javascript behaviors.
$elements['add_more']['menu'] = [
'#type' => 'inline_template',
'#template' => '
<div class="erl-add-more-menu hidden">
<h4 class="visually-hidden">Add Item</h4>
<div class="erl-add-more-menu__search hidden">
<input type="text" placeholder="{{ search_text }}" />
</div>
<div class="erl-add-more-menu__group">
{% for type in types %}
<div class="erl-add-more-menu__item">
<a data-type="{{ type.id }}" href="#{{ type.id }}">
{% if type.image %}
<img src="{{ type.image }}" alt ="" />
{% endif %}
<div>{{ type.name }}</div>
</a>
</div>
{% endfor %}
</div>
</div>',
'#context' => [
'types' => $types,
'search_text' => $this
->t('Search'),
],
];
}
else {
// Add the #isTranslating attribute, if in a translation context.
$elements['add_more'] = [
'actions' => [
'#isTranslating' => TRUE,
],
];
}
return $elements;
}