contacts_events-8.x-1.x-dev/src/Plugin/Field/FieldWidget/OrderItemAdditionalChargeWidget.php
src/Plugin/Field/FieldWidget/OrderItemAdditionalChargeWidget.php
<?php
namespace Drupal\contacts_events\Plugin\Field\FieldWidget;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowBase;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormBase;
use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex;
/**
* Inline widget for additional charges.
*
* @FieldWidget(
* id = "inline_entity_form_order_item_additional_charges",
* label = @Translation("Booking Additional Charges"),
* field_types = {
* "entity_reference"
* },
* multiple_values = true
* )
*/
class OrderItemAdditionalChargeWidget extends InlineEntityFormComplex {
/**
* {@inheritdoc}
*/
protected function getTargetBundles() {
// Don't allow creation of any other order item type other than ticket.
return ['additional_charge'];
}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return ['form_mode' => 'booking'] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element = parent::settingsForm($form, $form_state);
$element['allow_new']['#access'] = FALSE;
$element['allow_existing']['#access'] = FALSE;
$element['match_operator']['#access'] = FALSE;
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
return InlineEntityFormBase::settingsSummary();
}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
// Disable the table drag as we don't want that.
$element['entities']['#cache']['contexts'][] = 'user.permissions';
$element['entities']['#disable_tabledrag'] = TRUE;
// Override the field title as we are only dealing with additional charges.
$element['#field_title'] = $this->t('Additional Charges');
if ($items->offsetExists($delta)) {
// If the item exists but doesn't have an entity then remove the item.
if (!$items[$delta]->entity) {
unset($items[$delta]);
unset($element['entities'][$delta]);
}
}
foreach (Element::children($element['entities']) as $delta) {
if (!isset($items[$delta]->entity) || $items[$delta]->entity->bundle() != 'additional_charge') {
unset($element['entities'][$delta]);
}
}
// Loop over the tickets and add the operations as required.
$entities = $form_state->get([
'inline_entity_form',
$this->getIefId(),
'entities',
]);
$workflow = NULL;
$has_form = (bool) $form_state
->get(['inline_entity_form', $this->getIefId(), 'form']);
foreach (Element::children($element['entities']) as $delta) {
$entity_element = &$element['entities'][$delta];
$entity_form = $entities[$delta]['form'] ?? FALSE;
// If we are showing the row, update the AJAX settings to hide the form
// actions.
if (!$entity_form) {
// Make each entity form action hide the submit buttons.
foreach (Element::children($entity_element['actions']) as $key) {
$type = $entity_element['actions'][$key]['#type'] ?? NULL;
if ($type == 'submit' && isset($entity_element['actions'][$key]['#ajax'])) {
// phpcs:ignore Drupal.Arrays.Array.LongLineDeclaration
$entity_element['actions'][$key]['#ajax']['callback'] = [static::class, 'ajaxCallback'];
}
}
}
// Otherwise hide the add new action so we don't have multiple open.
else {
$has_form = TRUE;
}
}
// If we have an expanded item, hide all other operations.
if ($has_form) {
// Use a process to disable the outer form actions.
$element['#process'][] = [static::class, 'disableActions'];
// Disable IEF actions such as 'Add'.
$element['actions']['#disabled'] = TRUE;
// Loop over the rows to disable row operations.
foreach (Element::children($element['entities']) as $delta) {
$element['entities'][$delta]['actions']['#disabled'] = TRUE;
}
}
// Set the IEF actions to use the ajax callback.
foreach (Element::children($element['actions']) as $key) {
// phpcs:ignore Drupal.Arrays.Array.LongLineDeclaration
$element['actions'][$key]['#ajax']['callback'] = [static::class, 'ajaxCallback'];
}
return $element;
}
/**
* Process callback to disable the outer form actions.
*/
public static function disableActions(array $element, FormStateInterface $form_state, &$complete_form) {
foreach (Element::children($complete_form['actions']) as $key) {
$complete_form['actions'][$key]['#attributes']['disabled'] = TRUE;
}
return $element;
}
/**
* {@inheritdoc}
*/
public static function buildEntityFormActions($element) {
$element = parent::buildEntityFormActions($element);
foreach (Element::children($element['actions']) as $key) {
// phpcs:ignore Drupal.Arrays.Array.LongLineDeclaration
$element['actions'][$key]['#ajax']['callback'] = [static::class, 'ajaxCallback'];
}
return $element;
}
/**
* {@inheritdoc}
*/
protected function getEntityTypeLabels() {
// The admin has specified the exact labels that should be used.
if ($this->getSetting('override_labels')) {
return [
'singular' => $this->getSetting('label_singular'),
'plural' => $this->getSetting('label_plural'),
];
}
else {
return [
'singular' => $this->t('additional charge'),
'plural' => $this->t('additional charges'),
];
}
}
/**
* {@inheritdoc}
*/
public static function isApplicable(FieldDefinitionInterface $field_definition) {
// Only allow for order items on the contacts booking bundle of orders.
return $field_definition->getTargetEntityTypeId() == 'commerce_order'
&& $field_definition->getName() == 'order_items';
}
/**
* {@inheritdoc}
*/
public static function submitSaveEntity($entity_form, FormStateInterface $form_state) {
/** @var \Drupal\commerce_order\Entity\OrderItemInterface $order_item */
$order_item = $entity_form['#entity'];
parent::submitSaveEntity($entity_form, $form_state);
$form_object = $form_state->getFormObject();
// The form object may be a CheckoutFlowBase object which does not extend
// FormBase so needs another method for accessing the order.
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $form_object instanceof CheckoutFlowBase ? $form_object->getOrder() : $form_object->getEntity();
// We're explicitly updating the order items, so skip refreshing. Otherwise
// we get a double save and the entity in the form is out of date.
$order->setRefreshState(OrderInterface::REFRESH_SKIP);
$order_item->save();
// Ensure the item is added to the order and the total recalculated.
$order->addItem($order_item)
->save();
}
/**
* {@inheritdoc}
*/
public static function submitConfirmRemove($form, FormStateInterface $form_state) {
parent::submitConfirmRemove($form, $form_state);
$element = inline_entity_form_get_element($form, $form_state);
$remove_button = $form_state->getTriggeringElement();
$delta = $remove_button['#ief_row_delta'];
/** @var \Drupal\commerce_order\Entity\OrderItemInterface $order_item */
$order_item = $element['entities'][$delta]['form']['#entity'];
// Get the right order entity.
$form_object = $form_state->getFormObject();
// The form object may be a CheckoutFlowBase object which does not extend
// FormBase so needs another method for accessing the order.
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $form_object instanceof CheckoutFlowBase ? $form_object->getOrder() : $form_object->getEntity();
// Remove the order item from the order, save the order and delete the order
// item. We do this immediately so you don't have to separate save the page.
$order->removeItem($order_item);
$order->save();
$order_item->delete();
// Show status message to user.
\Drupal::messenger()->addStatus(new TranslatableMarkup('This additional charge has been removed.'));
}
/**
* Ajax callback to show or hide outer form actions.
*
* @param array $form
* The form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* An AJAX response containing both the default IEF update and the commands
* for showing or hiding the outer form actions.
*/
public static function ajaxCallback(array $form, FormStateInterface $form_state) {
// Wrap the inline entity form ajax callback so we can add commands.
// To do that, build a response from the callback.
// @see FormAjaxResponseBuilder::buildResponse
$form = inline_entity_form_get_element($form, $form_state);
$request = \Drupal::request();
$route_match = \Drupal::routeMatch();
$response = \Drupal::service('main_content_renderer.ajax')->renderResponse($form, $request, $route_match);
// Replace the outer form actions.
$complete_form = $form_state->getCompleteForm();
$actions = [];
foreach (Element::children($complete_form['actions']) as $key) {
$actions[$key] = $complete_form['actions'][$key];
}
$response->addCommand(new HtmlCommand('#edit-actions', $actions));
return $response;
}
}
