You are here

commerce_cart_blocks.module in Commerce Cart Blocks 8

File

commerce_cart_blocks.module
View source
<?php

/**
 * @file
 * Contains commerce_cart_blocks.module.
 */
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\StatusMessages;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Ajax\AfterCommand;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Render\Markup;

/**
 * Implements hook_theme().
 */
function commerce_cart_blocks_theme($existing, $type, $theme, $path) {
  return [
    'commerce_cart_blocks_cart' => [
      'variables' => [
        'count' => NULL,
        'heading' => NULL,
        'content' => NULL,
        'in_cart' => NULL,
        'links' => [],
      ],
    ],
    'commerce_cart_blocks_cart_button' => [
      'variables' => [
        'count' => NULL,
        'icon' => NULL,
        'button_label' => '',
        'in_cart' => NULL,
        'url' => NULL,
        'content' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function commerce_cart_blocks_form_commerce_order_type_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
  $order_type = $form_state
    ->getFormObject()
    ->getEntity();
  $form['commerce_cart_blocks'] = [
    '#type' => 'details',
    '#title' => t('Commerce Cart Blocks'),
    '#description' => t('Additional options when using Commerce Cart Blocks.'),
    '#weight' => 5,
    '#open' => TRUE,
    '#collapsible' => TRUE,
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_ajax'] = [
    '#type' => 'checkbox',
    '#title' => t('Enable AJAX for Commerce Cart Blocks'),
    '#weight' => 0,
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax', FALSE),
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_ajax_modal'] = [
    '#type' => 'checkbox',
    '#title' => t('Show a modal window after adding an item to the cart with AJAX'),
    '#weight' => 1,
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax_modal', TRUE),
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_refresh_page_after_modal'] = [
    '#type' => 'checkbox',
    '#title' => t('Refresh the page when closing the modal window'),
    '#weight' => 2,
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_refresh_page_after_modal', FALSE),
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax_modal]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_links'] = [
    '#type' => 'details',
    '#title' => t('Add links to modal'),
    '#description' => t('You can optionally add buttons to the modal window that link to the cart, checkout, or back to the current page.'),
    '#weight' => 3,
    '#open' => TRUE,
    '#collapsible' => TRUE,
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_links']['commerce_cart_blocks_add_cart'] = [
    '#type' => 'checkbox',
    '#title' => t('Show View Cart link in modal'),
    '#weight' => 0,
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_cart', TRUE),
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_links']['commerce_cart_blocks_add_checkout'] = [
    '#type' => 'checkbox',
    '#title' => t('Show Checkout link in modal'),
    '#weight' => 1,
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_checkout', TRUE),
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_links']['commerce_cart_blocks_add_keep_browsing'] = [
    '#type' => 'checkbox',
    '#title' => t('Show Keep Browsing link in modal'),
    '#weight' => 2,
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_keep_browsing', TRUE),
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_ajax_modal_title'] = [
    '#type' => 'textfield',
    '#title' => 'Title of the cart modal',
    '#weight' => 4,
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax_modal_title', 'Cart'),
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['commerce_cart_blocks']['commerce_cart_blocks_append_block_id'] = [
    '#type' => 'textfield',
    '#title' => t('Block id to append cart to block'),
    '#weight' => 5,
    '#description' => t('Enter the block id the cart block immediately follows to append when entering the first cart item.'),
    '#default_value' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_append_block_id', ''),
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart_blocks[commerce_cart_blocks_ajax]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
  ];
  $form['actions']['submit']['#submit'][] = 'commerce_cart_blocks_order_type_form_submit';
}

/**
 * Form submission handler for 'commerce_order_type_form'.
 *
 * @param array $form
 *   The form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state object.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function commerce_cart_blocks_order_type_form_submit(array $form, FormStateInterface $form_state) {

  /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
  $order_type = $form_state
    ->getFormObject()
    ->getEntity();
  $settings = $form_state
    ->getValue([
    'commerce_cart_blocks',
  ]);
  $order_type
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax', $settings['commerce_cart_blocks_ajax'])
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax_modal', $settings['commerce_cart_blocks_ajax_modal'])
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_refresh_page_after_modal', $settings['commerce_cart_blocks_refresh_page_after_modal'])
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_cart', $settings['commerce_cart_blocks_links']['commerce_cart_blocks_add_cart'])
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_checkout', $settings['commerce_cart_blocks_links']['commerce_cart_blocks_add_checkout'])
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_keep_browsing', $settings['commerce_cart_blocks_links']['commerce_cart_blocks_add_keep_browsing'])
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax_modal_title', $settings['commerce_cart_blocks_ajax_modal_title'])
    ->setThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_append_block_id', $settings['commerce_cart_blocks_append_block_id'])
    ->save();
}

/**
 * Get AJAX settings.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity.
 *
 * @return array
 *   An array of commerce_cart_blocks_ajax settings
 *
 * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
 */
function commerce_cart_blocks_ajax_settings(EntityInterface $entity) {

  /** @var \Drupal\commerce_order\Entity\OrderItemTypeInterface $item_type */
  $item_type = \Drupal::entityTypeManager()
    ->getStorage('commerce_order_item_type')
    ->load($entity
    ->bundle());

  /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
  $order_type = \Drupal::entityTypeManager()
    ->getStorage('commerce_order_type')
    ->load($item_type
    ->getOrderTypeId());
  return [
    'enabled' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax', FALSE),
    'modal_enabled' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax_modal', TRUE),
    'refresh_page_after_modal' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_refresh_page_after_modal', FALSE),
    'cart' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_cart', FALSE),
    'checkout' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_checkout', FALSE),
    'keep_browsing' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_add_keep_browsing', FALSE),
    'modal_title' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_ajax_modal_title', 'Cart'),
    'append_block_id' => $order_type
      ->getThirdPartySetting('commerce_cart_blocks', 'commerce_cart_blocks_append_block_id', ''),
  ];
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
 */
function commerce_cart_blocks_form_commerce_order_item_add_to_cart_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $formObject = $form_state
    ->getFormObject();
  if (is_object($formObject) && method_exists($formObject, 'getEntity')) {
    $entity = $formObject
      ->getEntity();
    if ($entity instanceof EntityInterface) {
      $settings = commerce_cart_blocks_ajax_settings($entity);
      if ($settings['enabled']) {
        $form['commerce_cart_blocks_ajax'] = [
          '#type' => 'value',
          '#value' => $settings,
        ];
        $form['actions']['submit']['#ajax'] = [
          'callback' => 'commerce_cart_blocks_ajax_callback',
          'wrapper' => 'commerce-product-add-to-cart-form',
          'event' => 'click',
          'effect' => 'fade',
          'progress' => [
            'type' => 'throbber',
            'message' => t('Adding to Cart'),
          ],
        ];
        $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
        $form['#attached']['library'][] = 'commerce_cart_blocks/commerce_cart_blocks_add_to_cart_form';
        $form['#attached']['drupalSettings']['commerce_cart_blocks']['ajax'] = $settings;
      }
    }
  }
}

/**
 * Ajax callback for variation product form.
 *
 * @param array $form
 *   The form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 *
 * @return \Drupal\Core\Ajax\AjaxResponse
 *   The AJAX response.
 */
function commerce_cart_blocks_ajax_callback(array $form, FormStateInterface $form_state) {
  $response = new AjaxResponse();
  $ajax_settings = $form_state
    ->getValue('commerce_cart_blocks_ajax', [
    'modal_enabled' => TRUE,
    'modal_title' => 'Cart',
    'append_block_id' => '',
  ]);

  // Update cart blocks after adding an item to cart.
  if (!$form_state
    ->getErrors()) {
    $types = [
      'commerce_cart_blocks_cart',
      'commerce_cart_blocks_button',
    ];
    foreach ($types as $block_type) {
      $query = \Drupal::entityQuery('block')
        ->condition('plugin', $block_type);
      foreach ((array) $query
        ->execute() as $block_id) {
        $response
          ->addCommand(commerce_cart_blocks_render_cart_block_command($block_id));
      }
    }

    // @todo Figure out why a page reload is sometimes required.
    $cartCount = commerce_cart_blocks_cart_count();
    $appendId = $ajax_settings['append_block_id'];
    if ($cartCount === 1 && !empty($appendId)) {
      $command = commerce_cart_blocks_render_cart_block_command_append('cart');
      $appendCommand = new AfterCommand($appendId, $command);
      $response
        ->addCommand($appendCommand);
    }
    if ($ajax_settings['modal_enabled']) {
      $response
        ->addCommand(commerce_cart_blocks_show_cart_dialog_command($ajax_settings, $cartCount));
    }
    else {
      $status_messages = [
        '#type' => 'status_messages',
      ];
      $messages = \Drupal::service('renderer')
        ->renderRoot($status_messages);
      if (!empty($messages)) {
        $response
          ->addCommand(new PrependCommand('.commerce-order-item-add-to-cart-form', $messages));
      }
    }
  }
  return $response;
}

/**
 * Render HTML of cart block.
 *
 * @return \Drupal\Core\Ajax\HtmlCommand
 *   An AJAX command to replace the provided cart block.
 */
function commerce_cart_blocks_render_cart_block_command($block_id) {
  try {
    $block = \Drupal::entityTypeManager()
      ->getStorage('block')
      ->load($block_id);
    if (!empty($block)) {
      $build = \Drupal::entityTypeManager()
        ->getViewBuilder('block')
        ->view($block);
      $render = \Drupal::service('renderer')
        ->render($build);
      $block_id = str_replace('_', '-', $block_id);
      return new HtmlCommand('#block-' . $block_id, $render);
    }
    return NULL;
  } catch (InvalidPluginDefinitionException $e) {
    return NULL;
  }
}

/**
 * Append the cart block.
 *
 * @param string $block_id
 *   Block id.
 *
 * @return mixed
 *   Block content.
 */
function commerce_cart_blocks_render_cart_block_command_append($block_id) {
  try {
    $append = '';
    $block = \Drupal::entityTypeManager()
      ->getStorage('block')
      ->load($block_id);
    if (!empty($block)) {
      $build = \Drupal::entityTypeManager()
        ->getViewBuilder('block')
        ->view($block);
      $append = \Drupal::service('renderer')
        ->render($build);
    }
    return $append;
  } catch (InvalidPluginDefinitionException $e) {
    return '';
  }
}

/**
 * Dialog command callback.
 */
function commerce_cart_blocks_show_cart_dialog_command(array $ajax_settings, $cartCount) {
  $messages = StatusMessages::renderMessages(NULL);

  /** @var \Drupal\Core\Render\Markup $message */
  $message = $messages['#message_list']['status'][0];
  $linksTemplate = '<div class="commerce-cart-block--contents__links">%s</div>';
  $links = [];
  if ($ajax_settings['keep_browsing']) {
    $links[] = '<a href="#" class="button keepBrowsing dialog-cancel">' . t('Keep Browsing') . '</a>';
  }
  if ($ajax_settings['cart']) {
    $links[] = '<a href="/cart" class="button viewCart">' . t('View Cart') . '</a>';
  }
  if ($ajax_settings['checkout']) {
    $carts = commerce_cart_blocks_get_carts();
    if (!empty($carts)) {

      /** @var \Drupal\commerce_order\Entity\OrderInterface $cart */
      $cart = reset($carts);
      $links[] = '<a href="/checkout/' . $cart
        ->id() . '" class="button checkout">' . t('Checkout') . '</a>';
    }
  }
  $message .= sprintf($linksTemplate, implode("\n", $links));
  $messages['#message_list']['status'][0] = Markup::create($message);
  $dialogOptions = [
    'width' => 'auto',
    'maxWidth' => 500,
    'height' => 'auto',
    'modal' => TRUE,
    'fluid' => TRUE,
    'resizable' => FALSE,
    'dialogClass' => 'added-to-cart-dialog',
  ];
  if ($cartCount === 1) {
    $dialogOptions['dialogClass'] .= ' js-firstItem';
  }
  return new OpenModalDialogCommand($ajax_settings['modal_title'], $messages, $dialogOptions);
}

/**
 * Return the cart count.
 *
 * @return int
 *   Cart count.
 */
function commerce_cart_blocks_cart_count() {
  $carts = commerce_cart_blocks_get_carts();
  $count = 0;
  if (!empty($carts)) {
    foreach ($carts as $cart_id => $cart) {
      foreach ($cart
        ->getItems() as $order_item) {
        $count += (int) $order_item
          ->getQuantity();
      }
    }
  }
  return $count;
}

/**
 * Get carts.
 */
function commerce_cart_blocks_get_carts() {

  /** @var \Drupal\commerce_order\Entity\OrderInterface[] $carts */
  $carts = \Drupal::service('commerce_cart.cart_provider')
    ->getCarts();
  $carts = array_filter($carts, function ($cart) {

    /** @var \Drupal\commerce_order\Entity\OrderInterface $cart */
    return $cart
      ->hasItems() && $cart->cart->value;
  });
  return $carts;
}