compose-8.x-1.x-dev/src/Plugin/Field/FieldWidget/ComposeWidget.php
src/Plugin/Field/FieldWidget/ComposeWidget.php
<?php
namespace Drupal\compose\Plugin\Field\FieldWidget;
use Drupal\Core\Render\Element;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InsertCommand;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Ajax\CloseDialogCommand;
use Drupal\compose\Ajax\ComposeOpenModalDialog;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Ajax\PrependCommand;
/**
* Plugin implementation of the 'compose_widget' widget.
*
* @FieldWidget(
* id = "compose_widget",
* label = @Translation("Compose Widget"),
* field_types = {
* "entity_reference",
* "entity_reference_revisions",
* },
* multiple_values = true
* )
*/
class ComposeWidget extends InlineEntityFormComplex implements ContainerFactoryPluginInterface {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$element['#type'] = 'container';
$element['_compose_field_title'] = [
'#theme' => 'form_element_label',
'#weight' => -99,
] + array_intersect_key($element, array_flip([
'#id',
'#required',
'#title',
'#title_display',
]));
$settings = $this->getSettings();
$ief_id = $element['#ief_id'];
$field_name = $this->fieldDefinition->getName();
$parents = array_merge($form['#parents'], [$field_name, 'form']);
$labels = $element['#ief_labels'] = $this->getEntityTypeLabels();
// @todo Add bundle labels once added, hopefully in 8.8.x
// @see https://www.drupal.org/project/drupal/issues/2765065
// Get the target bundles that can be referenced from this field.
// $bundles = $items->getFieldDefinition()->getSetting('handler_settings')['target_bundles']);
// $entity = $items->getEntity();
//
// if (count($bundles) === 1) {
// $labels = [
// 'singular' => $entity->getSingularLabel(),
// 'plural' => $entity->getPluralLabel()
// ];
// }
// Pass along the IEF form id, field name and entity type.
$element['entities']['#form_id'] = $ief_id;
$element['entities']['#field_name'] = $items->getName();
// $element['entities']['#entity_type'] = $items->getEntity()->getEntityTypeId();
// Use our own #theme implementation.
$element['entities']['#theme'] = 'compose_entity_widget';
$element['#attached']['library'][] = 'compose/compose-entity-widget';
// Add a wrapper to target for the widget level dialog content and container
$dialog_container_selector = 'compose-widget-dialog-container-' . $ief_id;
$dialog_content_selector = 'compose-widget-dialog-content-' . $ief_id;
$element['compose_dialog_container'] = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => [
"id" => $dialog_container_selector,
'class' => [$dialog_container_selector],
],
'#value' => '',
];
// Make sure form gets output as a container if needed
if (!isset($element['form'])) {
$element['form'] = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => [
"id" => $dialog_content_selector,
],
'#value' => '',
];
}
else {
$element['form']['#attributes']['id'] = $dialog_content_selector;
}
// Start "Add New" form
//show button with custom callback and submit
$element['actions']['#attributes']['class'] = ['compose-actions'];
$element['actions']['compose_add'] = [
'#type' => 'submit',
'#value' => new FormattableMarkup('Add @label', ['@label' => $labels['singular']]),
'#name' => 'compose-' . $ief_id . '-add',
'#limit_validation_errors' => [array_merge($parents, ['actions'])],
'#attributes' => [
'class' => ['compose-add-button'],
],
'#element_validate' => [[$this, 'addNewEntityValidate']],
'#ajax' => [
'callback' => [$this, 'openDialogCallback'],
'wrapper' => $dialog_content_selector,
],
'#submit' => [[$this, 'addNewEntitySubmit']],
'#ief_id' => $ief_id,
'#ief_labels' => $labels,
];
// Remove actions from parent
foreach (['ief_add', 'bundle', 'ief_add_existing'] as $action) {
if (isset($element['actions'][$action])) {
unset($element['actions'][$action]);
}
}
$ief_form_action = $form_state->get(['inline_entity_form', $this->getIefId(), 'form']);
// Avoid ugly fieldset for ief add form
if ($ief_form_action == 'add') {
$element['form']['#type'] = 'container';
}
if ($ief_form_action == 'compose_add') {
$element['form']['compose_add_options'] = $this->buildAddNewEntityForm($parents);
$element['form']['compose_add_options']['type_filter'] = [
'#type' => 'textfield',
'#title' => 'Filter type list',
'#weight' => -100
];
if ($settings['allow_existing']) {
$element['form']['tabs'] =[
'#type' => 'html_tag',
'#tag' => 'ul',
'#attributes' => [
'class' => ['compose-tabs'],
],
'#weight' => -99,
'new' => [
'#type' => 'html_tag',
'#tag' => 'li',
'link' => [
'#type' => 'html_tag',
'#tag' => 'a',
'#attributes' => [
'href' => '#compose-widget-add-new-' . $ief_id,
],
'#value' => $this->t('New'),
]
],
'existing' => [
'#type' => 'html_tag',
'#tag' => 'li',
'link' => [
'#type' => 'html_tag',
'#tag' => 'a',
'#attributes' => [
'href' => '#compose-widget-add-existing-' . $ief_id,
],
'#value' => $this->t('Existing'),
]
],
];
$element['form']['#attributes']['class'][] = 'compose-dialog-tabs';
$element['form']['compose_add_existing'] = $this->buildAddExistingEntityForm($form_state, $parents);
}
}
// Edit, Duplicate, Remove
// $entities = $form_state->get(['inline_entity_form', $this->getIefId(), 'entities']);
foreach (Element::children($element['entities']) as $key) {
$set_attributes = FALSE;
foreach (['edit', 'duplicate', 'remove'] as $op) {
if (isset($element['entities'][$key]['actions']["ief_entity_$op"])) {
$element['entities'][$key]['actions']["ief_entity_$op"]['#ajax']['callback'] = [$this, 'openEntityDialogCallback'];
if (!$set_attributes) {
$element['entities'][$key]['#attributes']['data-compose-row-delta'] = $element['entities'][$key]['actions']["ief_entity_$op"]['#ief_row_delta'];
$element['entities'][$key]['#attributes']['data-compose-ief-id'] = $ief_id;
$set_attributes = TRUE;
}
}
}
if (isset($element['entities'][$key]['form']['inline_entity_form']['#ief_row_delta'])) {
$element['entities'][$key]['#attributes']['data-compose-row-delta'] = $element['entities'][$key]['form']['inline_entity_form']['#ief_row_delta'];
$element['entities'][$key]['#attributes']['data-compose-ief-id'] = $ief_id;
}
$element['entities'][$key]['compose_dialog_container'] = [
'#type' => 'html_tag',
'#tag' => 'div',
'#attributes' => [
'id' => 'compose-dialog-container-' . $ief_id . '-' . $key,
'class' => ['compose-dialog-container'],
],
'#value' => '',
'#weight' => 9999,
];
}
return $element;
}
/**
* Validation callback for adding a new entity.
*
* @param array $form
* The full form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function addNewEntityValidate($form, FormStateInterface $form_state) {
$target_type = $this->getFieldSetting('target_type');
$bundle_info = $this->entityTypeBundleInfo->getBundleInfo($target_type);
if (!$bundle_info || empty($bundle_info)){
//bundled_entity_has_no_bundles
$form_state->setError($form['actions']['compose_add'], 'Please add a bundle.');
}
}
/**
* Submit handler to open the compose add (new or existing) dialog.
*
* @param array $form
* The full form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public function addNewEntitySubmit($form, FormStateInterface $form_state) {
$trigger = $form_state->getTriggeringElement();
$ief_id = $trigger['#ief_id'];
$target_type = $this->getFieldSetting('target_type');
$bundle_info = $this->entityTypeBundleInfo->getBundleInfo($target_type);
$create_bundles = $this->getCreateBundles();
$bundles = [];
foreach ($bundle_info as $bundle_name => $bundle_label) {
if (in_array($bundle_name, $create_bundles)) {
$bundles[$bundle_name] = $bundle_label['label'];
}
}
if (count($bundles) > 1) {
//bundled_entity_has_multiple_bundles
$form_state->set(['inline_entity_form', $ief_id, 'form'], 'compose_add');
}
else if (count($bundles) == 1) {
//bundled_entity_has_one_bundle
$form_state->set(['inline_entity_form', $ief_id, 'form'], 'add');
$form_state->set(['inline_entity_form', $ief_id, 'form settings'], [
'bundle' => array_keys($bundles),
]);
}
$form_state->setRebuild();
}
/**
* Submit handler for most cancel actions.
*
* @param array $form
* The full form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public static function composeCancelSubmit($form, FormStateInterface $form_state) {
$trigger = $form_state->getTriggeringElement();
$ief_id = $trigger['#ief_id'];
$form_state->set(['inline_entity_form', $ief_id, 'form'], NULL);
$form_state->setRebuild();
}
/**
* Submit handler for selecting a bundle from the add new entity dialog.
*
* @param array $form
* The full form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public static function addNewEntityRowSubmit($form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$trigger = $form_state->getTriggeringElement();
$ief_id = $element['#ief_id'];
$form_state->set(['inline_entity_form', $ief_id, 'form'], 'add');
$form_state->set(['inline_entity_form', $ief_id, 'form settings'], [
'bundle' => $trigger['#compose_bundle'],
'bundle_label' => $trigger['#compose_bundle_label'],
]);
$form_state->setRebuild();
}
/**
* Ajax callback for after a new entity is added.
*
* @param array $form
* The full form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An ajax-renderable response.
*/
public static function addNewEntityDialogCallback($form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$ief_id = $element['#ief_id'];
$container = '#compose-widget-dialog-container-' . $ief_id;
$selector = '#compose-widget-dialog-content-' . $ief_id;
$classes = [];
$settings = $form_state->get(['inline_entity_form', $ief_id, 'form settings']);
$title = t('Add new @bundle', ['@bundle' => $settings['bundle_label']]);
$classes = ['compose-entity-dialog'];
$response = new AjaxResponse();
$response->addCommand(new CloseDialogCommand($container));
$response->addCommand(new InsertCommand(NULL, $element));
$response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, NULL, $classes));
return $response;
}
/**
* #ajax callback to close the 'Add existing' form/modal dialog.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An ajax-renderable response.
*/
public static function closeAddExistingDialogCallback(array $form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$ief_id = $element['#ief_id'];
$container = '#compose-widget-dialog-container-' . $ief_id;
$selector = '#compose-widget-dialog-content-' . $ief_id;
$response = new AjaxResponse();
$response->addCommand(new CloseDialogCommand($container));
$response->addCommand(new InsertCommand(NULL, $element));
return $response;
}
/**
* Opens the widget-level dialog.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @returns \Drupal\Core\Ajax\AjaxResponse
* An ajax-renderable response.
*/
public static function openDialogCallback(array $form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$ief_id = $element['#ief_id'];
$container = '#compose-widget-dialog-container-' . $ief_id;
$selector = '#compose-widget-dialog-content-' . $ief_id;
$append = '#inline-entity-form-' . $ief_id;
list($title, $insert_element) = self::getRenderableForm($element, $ief_id, $form_state);
$response = new AjaxResponse();
$response->addCommand(new InsertCommand(NULL, $insert_element));
$response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, $append, ['compose-entity-dialog']));
return $response;
}
/**
* Opens an entity row for dialog.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @returns \Drupal\Core\Ajax\AjaxResponse
* An ajax-renderable response.
*/
public function openEntityDialogCallback($form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$trigger = $form_state->getTriggeringElement();
$ief_id = $element['#ief_id'];
$ief_row_delta = $trigger['#ief_row_delta'];
$append = sprintf('[data-compose-row-delta="%s"][data-compose-ief-id="%s"]', $ief_row_delta, $ief_id);
$selector = $append . ' > .ief-form';
$container = $append . ' > .compose-dialog-container';
$label = $form_state->get(['inline_entity_form', $ief_id, 'entities'])[$ief_row_delta]['entity']->label();
$classes = [];
switch ($trigger['#ief_row_form']) {
case 'edit':
$title = $this->t('Edit %label', ['%label' => $label]);
$classes = ['compose-entity-dialog'];
break;
case 'remove':
$title = $this->t('Remove %label', ['%label' => $label]);
break;
}
$insert_element = $element['entities'][$ief_row_delta]['form'];
$response = new AjaxResponse();
$response->addCommand(new AppendCommand($append, $insert_element));
$response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, $append, $classes));
return $response;
}
/**
* Gets the tile and renderable form from the IEF element based on the
* current action.
*
* @TODO: probably needs to be refactored out of existence now.
*
* @param array $element
* The IEF element as a renderable array.
* @param string $ief_id
* The IEF ID
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* An an array with two items: the title of the dialog and the renderable
* content.
*/
public static function getRenderableForm($element, $ief_id, FormStateInterface $form_state) {
$trigger = $form_state->getTriggeringElement();
$entity_label = $trigger['#ief_labels']['singular'];
switch ($form_state->get(['inline_entity_form', $ief_id, 'form'])) {
case 'add':
$title = 'Add New ' . $entity_label;
break;
case 'ief_add_existing':
$title = 'Add Existing ' . $entity_label;
break;
case 'compose_add':
$title = 'Add ' . $entity_label;
break;
}
$insert_element = $element['form'];
return [$title, $insert_element];
}
/**
* Opens an entity row for dialog.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
public static function closeDialogCallback($form, FormStateInterface $form_state) {
$trigger = $form_state->getTriggeringElement();
$element = inline_entity_form_get_element($form, $form_state);
$ief_id = $element['#ief_id'];
$selector = isset($trigger['#ief_row_delta']) ?
sprintf('[data-compose-row-delta="%s"][data-compose-ief-id="%s"] > .compose-dialog-container', $trigger['#ief_row_delta'], $ief_id) :
'#compose-widget-dialog-container-' . $ief_id;
$response = new AjaxResponse();
$response->addCommand(new CloseDialogCommand($selector, TRUE));
$response->addCommand(new InsertCommand(NULL, $element));
return $response;
}
/**
* Callback to handle the save button on entity forms (widget or row).
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @returns \Drupal\Core\Ajax\AjaxResponse
* An ajax-renderable response.
*/
public static function saveCallback($form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$trigger = $form_state->getTriggeringElement();
$ief_id = $element['#ief_id'];
if ($form_state->getErrors()) {
if (isset($trigger['#ief_row_delta'])) {
$append = sprintf('[data-compose-row-delta="%s"][data-compose-ief-id="%s"]', $trigger['#ief_row_delta'], $ief_id);
$selector = $append . ' > .ief-form';
$container = $append . ' > .compose-dialog-container';
}
else {
$selector = '#compose-widget-dialog-content-' . $ief_id;
$container = '#compose-widget-dialog-container-' . $ief_id;
}
$title = "Edit " . $element['#ief_labels']['singular'];
$messages = ['messages' => ['#type'=> 'status_messages']];
# This is an ugly brute force alternative to refreshing the modal content of the dialog.
$response = new AjaxResponse();
$response->addCommand(new CloseDialogCommand($container));
$response->addCommand(new InsertCommand('#inline-entity-form-' . $ief_id, $element));
$response->addCommand(new PrependCommand($selector, \Drupal::service('renderer')->renderRoot($messages)));
$response->addCommand(new ComposeOpenModalDialog($selector, $title, $container, $append, ['compose-entity-dialog']));
return $response;
}
else {
return self::closeDialogCallback($form, $form_state);
}
}
/**
* Callback to handle cancel.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @returns \Drupal\Core\Ajax\AjaxResponse
* An ajax-renderable response.
*/
public static function cancelCallback($form, FormStateInterface $form_state) {
return self::closeDialogCallback($form, $form_state);
}
/**
* {@inheritdoc}
*/
public static function buildEntityFormActions($element) {
$element = parent::buildEntityFormActions($element);
$element['actions']['#type'] = 'actions';
// Cancel
$element['actions']['ief_' . $element['#op'] . '_cancel']['#ajax']['callback'] = [get_called_class(), 'cancelCallback'];
$element['actions']['ief_' . $element['#op'] . '_cancel']['#ajax']['progress'] = 'fullscreen';
$element['actions']['ief_' . $element['#op'] . '_cancel']['#attributes']['data-compose-cancel'] = '';
// Save
$element['actions']['ief_' . $element['#op'] . '_save']['#ajax']['callback'] = [get_called_class(), 'saveCallback'];
$element['actions']['ief_' . $element['#op'] . '_save']['#ajax']['progress'] = 'fullscreen';
return $element;
}
/**
* {@inheritdoc}
*/
protected function buildRemoveForm(&$form) {
parent::buildRemoveForm($form);
$form['actions']['#type'] = 'actions';
// Cancel
$form['actions']['ief_remove_cancel']['#ajax']['callback'] = [get_called_class(), 'cancelCallback'];
$form['actions']['ief_remove_cancel']['#ajax']['progress'] = 'fullscreen';
$form['actions']['ief_remove_cancel']['#attributes']['data-compose-cancel'] = '';
// Remove
$form['actions']['ief_remove_confirm']['#ajax']['callback'] = [get_called_class(), 'saveCallback'];
}
/**
* Builds the add new entity screen.
*
* @param array $parents
* The form array parents for where this item will reside.
*
* @return array
* The add new entity form as a renderable array.
*/
public function buildAddNewEntityForm($parents) {
$ief_id = $this->getIefId();
$labels = $this->getEntityTypeLabels();
$form = [
'#type' => 'container',
'#attributes' => [
'class' => ['add-new-container'],
'id' => 'compose-widget-add-new-' . $ief_id
],
];
// Get bundles.
$bundles = [];
$target_type = $this->getFieldSetting('target_type');
$create_bundles = $this->getCreateBundles();
foreach ($this->entityTypeBundleInfo->getBundleInfo($target_type) as $bundle_name => $bundle_info) {
if (in_array($bundle_name, $create_bundles)) {
$bundles[$bundle_name] = $bundle_info['label'];
}
}
asort($bundles);
// Loop through bundles and build Add New Entity rows
foreach ($bundles as $bundle_id => $bundle_label) {
$handler = \Drupal::entityTypeManager()->getHandler($target_type, 'compose_widget');
$add = $handler->addNew($bundle_id);
$button = [
'#type' => 'submit',
'#value' => 'Select',
'#name' => 'compose-' . $ief_id . '-add-' . $bundle_id,
'#limit_validation_errors' => [$parents],
'#ajax' => [
'callback' => [$this, 'addNewEntityDialogCallback'],
'wrapper' => 'inline-entity-form-' . $ief_id,
'progress' => 'fullscreen',
],
'#submit' => [[$this, 'addNewEntityRowSubmit']],
'#compose_bundle' => $bundle_id,
'#compose_bundle_label' => $bundle_label,
'#attributes' => [
'class' => ['btn-primary'],
],
'#ief_labels' => $labels,
];
$handler->addNewSelectButton($add, $button);
$form[] = $add;
} // End "Add New" form
// Cancel
$form['actions'] = [
'#type' => 'actions',
'#weight' => 100,
];
$form['actions']['cancel'] = [
'#type' => 'submit',
'#value' => 'Cancel',
'#name' => 'compose-add-new-entity-cancel-' . $ief_id,
'#limit_validation_errors' => [],
'#ajax' => [
'callback' => [$this, 'closeDialogCallback'],
'wrapper' => 'inline-entity-form-' . $ief_id,
'progress' => 'fullscreen',
],
'#submit' => [[$this, 'composeCancelSubmit']],
'#attributes' => [
'data-compose-cancel' => '',
],
'#ief_id' => $ief_id,
];
return $form;
}
/**
* Creates the content for the Add Existing Entity form.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the parent form.
* @param array $parents
* The array parents of this IEF for convenience.
*
* @return array
* A form array for the add existing form.
*/
public function buildAddExistingEntityForm($form_state, $parents) {
$ief_id = $this->getIefId();
$entity_type = $this->getFieldSetting('target_type');
$handler = $this->entityTypeManager->getHandler($entity_type, 'compose_widget');
$target_bundles = $this->getTargetBundles();
$labels = $this->getEntityTypeLabels();
$limit = 10;
$form = [
'#type' => 'container',
'#attributes' => [
'class' => ['add-existing-container'],
'id' => 'compose-widget-add-existing-' . $ief_id
],
"content" => [
'#type' => 'container',
// '#tree' => FALSE,
'#attributes' => [
'class' => ['add-existing-container-content'],
'id' => 'compose-widget-add-existing-content-' . $ief_id
],
]
];
// Add filter elements to the form.
$filters = $handler->getAddExistingFilters($target_bundles);
if ($filters) {
$form['content']['filters'] = [
'#type' => 'container',
'#attributes' => [
'class' => ['form--inline', 'form-inline', 'compose-filters'],
],
];
foreach ($filters as $filter => $info) {
$form['content']['filters'][$filter] = [
'#type' => isset($info['options']) ? 'select' : 'textfield',
'#title' => $info['label'],
];
if (isset($info['options'])) {
$form['content']['filters'][$filter]['#options'] = array_merge(['' => $this->t('- Any -')], $info['options']);
}
}
$form['content']['filters']['actions'] = [
'#type' => 'container',
'#attributes' => [
'class' => ['compose-filter-actions']
],
];
$form['content']['filters']['actions']['filter_submit'] = [
'#type' => 'submit',
'#value' => $this->t('Apply'),
'#limit_validation_errors' => [ array_merge($parents, ['compose_add_existing','content','filters']) ],
'#submit' => [[$this, 'rebuildSubmit']],
'#ajax' => [
'callback' => [$this, 'addExistingContentCallback'],
'wrapper' => 'compose-widget-add-existing-content-' . $ief_id,
'progress' => 'fullscreen',
],
];
}
// Set the current page from form state
\Drupal::request()->query->set('page', $form_state->getValue(array_merge($parents, ['compose_add_existing', 'content', 'page']), 0) . ',0');
$query = \Drupal::entityQuery($entity_type);
$query->condition('type', $target_bundles, 'IN');
if ($existing_ids = array_filter(array_map(
function ($entity) {
return $entity['entity']->id();
},
$form_state->get(['inline_entity_form', $ief_id, 'entities'])
))) {
$query->condition('id', $existing_ids, 'NOT IN');
}
// Add filters to the query.
foreach ($filters as $filter => $info) {
$value = $form_state->getValue(array_merge($parents, ['compose_add_existing', 'content', 'filters', $filter]));
if ($value) {
$query->condition($info['field'], $value, $info['operator']);
}
}
$query = $handler->getAddExistingSort($query);
$query->pager($limit, 0);
$entity_ids = $query->execute();
$results = $this->entityTypeManager
->getStorage($entity_type)
->loadMultiple($entity_ids);
$header = [
t('Title'),
t('Type'),
];
$types = \Drupal::moduleHandler()->invokeAll('compose_add_existing_row_data');
foreach ($types as $type => $data) {
if ($type == $entity_type) {
foreach ($data as $value) {
$header[] = $value['label'];
}
}
}
$header[] = '';
$form['content']['entity_table'] = [
'#type' => 'table',
'#header' => $header,
'#sticky' => FALSE,
'#responsive' => TRUE,
'#empty' => $this->t('There are no available @labels to add.', ['@labels' => $labels['plural']]),
];
foreach ($results as $entity_id => $result) {
$row = $handler->addExistingRow($result);
$row['button'] = [
'#type' => 'submit',
'#value' => t('Select'),
'#name' => 'ief-reference-add-existing-' . $entity_id,
'#compose_parents' => $parents,
'#limit_validation_errors' => [$parents],
'#id' => 'entity-id-' . $entity_id,
'#attributes' => [
'class' => ['ief-entity-submit'],
'data-entity-id' => [$entity_id],
],
'#ief_entity_id' => $entity_id,
'#ajax' => [
'callback' => [$this, 'closeDialogCallback'],
'wrapper' => 'inline-entity-form-' . $ief_id,
'event' => 'click',
'progress' => [
'type' => 'throbber',
'message' => NULL,
],
],
];
InlineEntityFormComplex::addSubmitCallbacks($row['button']);
$row['button']['#submit'][] = [$this, 'addExistingSubmit'];
$form['content']['entity_table'][] = $row;
}
$form['content']['pager'] = [
'#type' => 'pager',
'#element' => 0,
'#prefix' => '<div data-compose-pager data-compose-ief-id="' . $ief_id . '">',
'#suffix' => '</div>',
];
$form['content']['page'] = [
'#type' => 'textfield',
'#default_value' => 0,
'#label' => 'Page',
'#attributes' => [
'data-compose-pager-update' => '',
'data-compose-ief-id' => $ief_id,
'class' => ['visually-hidden'],
],
];
$form['content']['update_page'] = [
'#type' => 'submit',
'#value' => t('Change page'),
'#submit' => [[$this, 'rebuildSubmit']],
'#limit_validation_errors' => [array_merge($parents, ['compose_add_existing','content','update_page']), array_merge($parents, ['compose_add_existing','content','page']), array_merge($parents, ['compose_add_existing','content','filters'])],
'#ajax' => [
'callback' => [$this, 'addExistingContentCallback'],
'wrapper' => 'compose-widget-add-existing-content-' . $ief_id,
'progress' => 'fullscreen',
],
'#attributes' => [
'data-compose-pager-submit' => '',
'data-compose-ief-id' => $ief_id,
'class' => ['visually-hidden'],
],
];
$form['#attached']['library'][] = 'core/jquery.form';
$form['#attached']['library'][] = 'core/drupal.ajax';
return $form;
}
/**
* Validates the form for adding existing entities.
*
* @param array &$element
* The element being validated.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state of the parent form.
*/
public static function addExistingValidate(&$form, FormStateInterface $form_state) {
$trigger = $form_state->getTriggeringElement();
$entity_id = $trigger['#ief_entity_id'];
$form_state->setValue(array_merge($trigger['#compose_parents'], ['entity_id']), $entity_id);
}
/**
* Button #submit callback: Add existing Compose buttons.
*
* @param $form
* The complete parent form.
* @param $form_state
* The form state of the parent form.
*/
public function addExistingSubmit($form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$ief_id = $element['#ief_id'];
$trigger = $form_state->getTriggeringElement();
$entity_id = $trigger['#ief_entity_id'];
$instance = $form_state->get(['inline_entity_form', $ief_id, 'instance']);
$entity_type = $instance->getSetting('target_type');
if ($entity_id !== NULL && $entity_type !== NULL) {
$storage = \Drupal::entityTypeManager()->getStorage($entity_type);
$entity = $storage->load($entity_id);
$entities = &$form_state->get(['inline_entity_form', $ief_id, 'entities']);
// Determine the correct weight of the new element.
$weight = 0;
if ($entities) {
$weight = max(array_keys($entities)) + 1;
}
$entities[] = [
'attributes' => [
'data-compose-ief-id' => $ief_id,
],
'entity' => $entity,
'weight' => $weight,
'form' => NULL,
'needs_save' => FALSE,
];
}
}
/**
* AJAX Callback for the add existing Select buttons.
*
* @param $form
* The complete parent form.
* @param $form_state
* The form state of the parent form.
*/
public static function addExistingCallback(&$form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
$ief_id = $element['#ief_id'];
$trigger = $form_state->getTriggeringElement();
$entity_id = $trigger['#ief_entity_id'];
$instance = $form_state->get(['inline_entity_form', $ief_id, 'instance']);
$entity_type = $instance->getSetting('target_type');
if ($entity_id !== NULL && $entity_type !== NULL) {
$storage = \Drupal::entityTypeManager()->getStorage($entity_type);
$entity = $storage->load($entity_id);
$entities = $form_state->get(['inline_entity_form', $ief_id, 'entities']);
// Determine the correct weight of the new element.
$weight = 0;
if ($entities) {
$weight = max(array_keys($entities)) + 1;
}
$element['entities'][] = [
'#entity' => $entity,
'#label' => $entity->label(),
'#weight' => $weight,
'form' => NULL,
'actions' => NULL,
'#needs_save' => FALSE,
];
$form['#entity'] = $entity;
// Pass along the IEF form id, field name and entity type.
$element['entities']['#form_id'] = $ief_id;
}
return $element;
}
/**
* Submit handler that just sets the form to rebuild.
*
* @param $form
* The complete parent form.
* @param $form_state
* The form state of the parent form.
*/
public function rebuildSubmit($form, FormStateInterface $form_state) {
$form_state->setRebuild();
}
/**
* AJAX callback for returning the content of the add existing dialog after
* a pager or filter submit.
*
* @param $form
* The complete parent form.
* @param $form_state
* The form state of the parent form.
*/
public function addExistingContentCallback(&$form, FormStateInterface $form_state) {
$element = inline_entity_form_get_element($form, $form_state);
return $element['form']['compose_add_existing']['content'];
}
}
