You are here

commerce_cart.module in Commerce Core 8.2

Same filename and directory in other branches
  1. 7 modules/cart/commerce_cart.module

Implements the shopping cart system and add to cart features.

File

modules/cart/commerce_cart.module
View source
<?php

/**
 * @file
 * Implements the shopping cart system and add to cart features.
 */
use Drupal\commerce_cart\CartSessionInterface;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AnonymousUserSession;

/**
 * Implements hook_cron().
 */
function commerce_cart_cron() {
  \Drupal::service('commerce_cart.cron')
    ->run();
}

/**
 * Implements hook_menu_links_discovered_alter().
 */
function commerce_cart_menu_links_discovered_alter(&$links) {
  $description = t('Manage fields, Add to Cart forms, other form and display settings for your order items.');
  $links['entity.commerce_order_item_type.collection']['description'] = $description;
}

/**
 * Implements hook_theme().
 */
function commerce_cart_theme($existing, $type, $theme, $path) {
  return [
    'commerce_cart_block' => [
      'variables' => [
        'icon' => NULL,
        'count' => NULL,
        'count_text' => '',
        'content' => NULL,
        'url' => NULL,
        'links' => [],
      ],
    ],
    'commerce_cart_empty_page' => [
      'render element' => 'element',
    ],
  ];
}

/**
 * Implements hook_preprocess_views_view().
 *
 * Moves the commerce_cart_form footer output above the submit buttons.
 */
function commerce_cart_preprocess_views_view(&$variables) {
  $view = $variables['view'];
  if (strpos($view->storage
    ->get('tag'), 'commerce_cart_form') !== FALSE) {
    $variables['rows']['footer'] = $variables['footer'];
    $variables['footer'] = '';
  }
}

/**
 * Implements hook_entity_base_field_info().
 */
function commerce_cart_entity_base_field_info(EntityTypeInterface $entity_type) {
  if ($entity_type
    ->id() === 'commerce_order') {
    $fields['cart'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Cart'))
      ->setSettings([
      'on_label' => t('Yes'),
      'off_label' => t('No'),
    ])
      ->setDisplayOptions('form', [
      'type' => 'boolean_checkbox',
      'settings' => [
        'display_label' => TRUE,
      ],
      'weight' => 20,
    ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDefaultValue(FALSE);
    return $fields;
  }
}

/**
 * Implements hook_entity_type_build().
 */
function commerce_cart_entity_type_build(array &$entity_types) {
  $entity_types['commerce_order_item']
    ->setFormClass('add_to_cart', '\\Drupal\\commerce_cart\\Form\\AddToCartForm');
}

/**
 * Implements hook_user_login().
 */
function commerce_cart_user_login($account) {

  // Assign the anonymous user's carts to the logged-in account.
  // This will only affect the carts that are in the user's session.
  $anonymous = new AnonymousUserSession();
  $carts = \Drupal::service('commerce_cart.cart_provider')
    ->getCarts($anonymous);
  \Drupal::service('commerce_order.order_assignment')
    ->assignMultiple($carts, $account);
}

/**
 * Implements hook_ENTITY_TYPE_delete().
 *
 * Removes deleted carts from the anonymous user's session.
 */
function commerce_cart_commerce_order_delete(OrderInterface $order) {
  if (!empty($order->cart->value)) {
    \Drupal::service('commerce_cart.cart_session')
      ->deleteCartId($order
      ->id());
  }
}

/**
 * Implements hook_ENTITY_TYPE_access().
 */
function commerce_cart_commerce_order_access(OrderInterface $order, $operation, AccountInterface $account) {
  return _commerce_cart_order_access($order, $operation, $account);
}

/**
 * Implements hook_ENTITY_TYPE_access().
 */
function commerce_cart_commerce_order_item_access(OrderItemInterface $order_item, $operation, AccountInterface $account) {
  $order = $order_item
    ->getOrder();
  return $order ? _commerce_cart_order_access($order, $operation, $account) : AccessResult::neutral();
}

/**
 * Checks that the account has access to the cart.
 *
 * @param \Drupal\commerce_order\Entity\OrderInterface $order
 *   The cart order.
 * @param string $operation
 *   The operation.
 * @param \Drupal\Core\Session\AccountInterface $account
 *   The account.
 *
 * @return \Drupal\Core\Access\AccessResultInterface
 *   The access result.
 */
function _commerce_cart_order_access(OrderInterface $order, $operation, AccountInterface $account) {
  if ($operation !== 'view' && $operation !== 'update') {
    return AccessResult::neutral();
  }
  $customer_id = (int) $order
    ->getCustomerId();
  $order_id = $order
    ->id();
  if ($account
    ->isAuthenticated()) {
    $customer_check = (int) $account
      ->id() === $customer_id;
  }
  else {
    $cart_session = \Drupal::service('commerce_cart.cart_session');
    assert($cart_session instanceof CartSessionInterface);
    $active_cart = $cart_session
      ->hasCartId($order_id, CartSessionInterface::ACTIVE);
    $completed_cart = $cart_session
      ->hasCartId($order_id, CartSessionInterface::COMPLETED);
    $customer_check = $active_cart || $completed_cart;
  }
  $access_result = AccessResult::allowedIf($customer_check);
  if ($operation === 'update') {
    $access_result = $access_result
      ->andIf(AccessResult::allowedIf($order
      ->getState()
      ->getId() === 'draft'));
  }
  return $access_result
    ->addCacheableDependency($order)
    ->cachePerUser();
}

/**
 * Implements hook_form_FORM_ID_alter() for 'entity_form_display_edit_form'.
 *
 * Hides irrelevant purchased_entity widgets on the add_to_cart order item
 * form display.
 */
function commerce_cart_form_entity_form_display_edit_form_alter(array &$form, FormStateInterface $form_state) {

  /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity */
  $entity = $form_state
    ->getFormObject()
    ->getEntity();
  if ($form['#entity_type'] == 'commerce_order_item' && $entity
    ->getMode() == 'add_to_cart') {
    $options =& $form['fields']['purchased_entity']['plugin']['type']['#options'];
    unset($options['commerce_entity_select']);
    unset($options['entity_reference_autocomplete_tags']);
    unset($options['entity_reference_autocomplete']);
    unset($options['inline_entity_form_complex']);
    unset($options['options_buttons']);
    unset($options['options_select']);
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter() for 'commerce_order_form'.
 */
function commerce_cart_form_commerce_order_form_alter(array &$form, FormStateInterface $form_state) {
  if (isset($form['cart'])) {

    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $form_state
      ->getFormObject()
      ->getEntity();
    if ($order
      ->getState()
      ->getId() == 'draft') {

      // Move the cart element to the bottom of the meta sidebar container.
      $form['cart']['#group'] = 'meta';
      $form['cart']['#weight'] = 100;
    }
    else {

      // Only draft orders can be carts.
      $form['cart']['#type'] = 'hidden';
      $form['#default_value'] = FALSE;
    }
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter() for 'commerce_order_type_form'.
 */
function commerce_cart_form_commerce_order_type_form_alter(array &$form, FormStateInterface $form_state) {

  /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
  $order_type = $form_state
    ->getFormObject()
    ->getEntity();
  $cart_form_view = $order_type
    ->getThirdPartySetting('commerce_cart', 'cart_form_view', 'commerce_cart_form');
  $cart_block_view = $order_type
    ->getThirdPartySetting('commerce_cart', 'cart_block_view', 'commerce_cart_block');
  $cart_expiration = $order_type
    ->getThirdPartySetting('commerce_cart', 'cart_expiration');
  $view_storage = \Drupal::entityTypeManager()
    ->getStorage('view');
  $available_form_views = [];
  $available_block_views = [];
  foreach ($view_storage
    ->loadMultiple() as $view) {
    if (strpos($view
      ->get('tag'), 'commerce_cart_form') !== FALSE) {
      $available_form_views[$view
        ->id()] = $view
        ->label();
    }
    if (strpos($view
      ->get('tag'), 'commerce_cart_block') !== FALSE) {
      $available_block_views[$view
        ->id()] = $view
        ->label();
    }
  }
  $form['commerce_cart'] = [
    '#type' => 'details',
    '#title' => t('Shopping cart settings'),
    '#weight' => 5,
    '#open' => TRUE,
    '#attached' => [
      'library' => [
        'commerce_cart/admin',
      ],
    ],
  ];
  $form['commerce_cart']['cart_form_view'] = [
    '#type' => 'select',
    '#title' => t('Shopping cart form view'),
    '#options' => $available_form_views,
    '#default_value' => $cart_form_view,
  ];
  $form['commerce_cart']['cart_block_view'] = [
    '#type' => 'select',
    '#title' => t('Shopping cart block view'),
    '#options' => $available_block_views,
    '#default_value' => $cart_block_view,
  ];
  $form['commerce_cart']['cart_expiration_enable'] = [
    '#type' => 'checkbox',
    '#title' => t('Delete abandoned carts'),
    '#default_value' => !empty($cart_expiration['number']),
  ];
  $form['commerce_cart']['cart_expiration'] = [
    '#type' => 'container',
    '#attributes' => [
      'class' => [
        'interval',
      ],
    ],
    '#states' => [
      'visible' => [
        ':input[name="commerce_cart[cart_expiration_enable]"]' => [
          'checked' => TRUE,
        ],
      ],
    ],
    '#open' => TRUE,
  ];
  $form['commerce_cart']['cart_expiration']['number'] = [
    '#type' => 'number',
    '#title' => t('Interval'),
    '#default_value' => !empty($cart_expiration['number']) ? $cart_expiration['number'] : 30,
    '#required' => TRUE,
    '#min' => 1,
  ];
  $form['commerce_cart']['cart_expiration']['unit'] = [
    '#type' => 'select',
    '#title' => t('Unit'),
    '#title_display' => 'invisible',
    '#default_value' => !empty($cart_expiration['unit']) ? $cart_expiration['unit'] : 'day',
    '#options' => [
      'minute' => t('Minute'),
      'hour' => t('Hour'),
      'day' => t('Day'),
      'month' => t('Month'),
    ],
    '#required' => TRUE,
  ];
  $form['actions']['submit']['#submit'][] = 'commerce_cart_order_type_form_submit';
}

/**
 * Form submission handler for 'commerce_order_type_form'.
 */
function commerce_cart_order_type_form_submit($form, FormStateInterface $form_state) {

  /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
  $order_type = $form_state
    ->getFormObject()
    ->getEntity();
  $settings =& $form_state
    ->getValue('commerce_cart');
  $order_type
    ->setThirdPartySetting('commerce_cart', 'cart_form_view', $settings['cart_form_view']);
  $order_type
    ->setThirdPartySetting('commerce_cart', 'cart_block_view', $settings['cart_block_view']);
  $cart_expiration = [];
  if (!empty($settings['cart_expiration_enable'])) {
    $cart_expiration = [
      'unit' => $settings['cart_expiration']['unit'],
      'number' => $settings['cart_expiration']['number'],
    ];
  }
  $order_type
    ->setThirdPartySetting('commerce_cart', 'cart_expiration', $cart_expiration);
  $order_type
    ->save();
}

/**
 * Implements hook_form_BASE_FORM_ID_alter() for 'commerce_order_item_type_form'.
 */
function commerce_cart_form_commerce_order_item_type_form_alter(array &$form, FormStateInterface $form_state) {
  $form['actions']['submit']['#submit'][] = 'commerce_cart_order_item_type_form_submit';
}

/**
 * Form submission handler for 'commerce_order_item_type_form'.
 */
function commerce_cart_order_item_type_form_submit($form, FormStateInterface $form_state) {
  $form_object = $form_state
    ->getFormObject();
  assert($form_object instanceof EntityForm);
  if ($form_object
    ->getOperation() == 'add') {

    // Help merchants navigate the admin UI by ensuring the order item type
    // has a matching 'add_to_cart' form display.
    $storage = \Drupal::entityTypeManager()
      ->getStorage('entity_form_display');

    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
    $form_display = $storage
      ->create([
      'targetEntityType' => 'commerce_order_item',
      'bundle' => $form_object
        ->getEntity()
        ->id(),
      'mode' => 'add_to_cart',
      'status' => TRUE,
    ]);
    $form_display
      ->removeComponent('unit_price');
    $form_display
      ->save();
  }
}

/**
 * Implements hook_entity_form_display_alter().
 */
function commerce_cart_entity_form_display_alter(EntityFormDisplayInterface $form_display, array $context) {
  if ($context['entity_type'] != 'commerce_order_item') {
    return;
  }

  // The "add_to_cart" form mode doesn't have a form display yet.
  // Default to hiding the unit_price field.
  if ($context['form_mode'] == 'add_to_cart' && $context['form_mode'] != $form_display
    ->getMode()) {
    $form_display
      ->removeComponent('unit_price');
  }
}

/**
 * Implements hook_views_data_alter().
 */
function commerce_cart_views_data_alter(array &$data) {
  $data['commerce_order_item']['edit_quantity']['field'] = [
    'title' => t('Quantity text field'),
    'help' => t('Adds a text field for editing the quantity.'),
    'id' => 'commerce_order_item_edit_quantity',
  ];
  $data['commerce_order_item']['remove_button']['field'] = [
    'title' => t('Remove button'),
    'help' => t('Adds a button for removing the order item.'),
    'id' => 'commerce_order_item_remove_button',
  ];
  $data['commerce_order']['empty_cart_button'] = [
    'title' => t('Empty cart button'),
    'help' => t('Adds a button for emptying the cart.'),
    'area' => [
      'id' => 'commerce_order_empty_cart_button',
    ],
  ];
}

/**
 * Prepares variables for the cart block element template.
 */
function template_preprocess_commerce_cart_block(&$variables) {
  $variables['attributes']['class'][] = 'cart--cart-block';
}

Functions

Namesort descending Description
commerce_cart_commerce_order_access Implements hook_ENTITY_TYPE_access().
commerce_cart_commerce_order_delete Implements hook_ENTITY_TYPE_delete().
commerce_cart_commerce_order_item_access Implements hook_ENTITY_TYPE_access().
commerce_cart_cron Implements hook_cron().
commerce_cart_entity_base_field_info Implements hook_entity_base_field_info().
commerce_cart_entity_form_display_alter Implements hook_entity_form_display_alter().
commerce_cart_entity_type_build Implements hook_entity_type_build().
commerce_cart_form_commerce_order_form_alter Implements hook_form_BASE_FORM_ID_alter() for 'commerce_order_form'.
commerce_cart_form_commerce_order_item_type_form_alter Implements hook_form_BASE_FORM_ID_alter() for 'commerce_order_item_type_form'.
commerce_cart_form_commerce_order_type_form_alter Implements hook_form_BASE_FORM_ID_alter() for 'commerce_order_type_form'.
commerce_cart_form_entity_form_display_edit_form_alter Implements hook_form_FORM_ID_alter() for 'entity_form_display_edit_form'.
commerce_cart_menu_links_discovered_alter Implements hook_menu_links_discovered_alter().
commerce_cart_order_item_type_form_submit Form submission handler for 'commerce_order_item_type_form'.
commerce_cart_order_type_form_submit Form submission handler for 'commerce_order_type_form'.
commerce_cart_preprocess_views_view Implements hook_preprocess_views_view().
commerce_cart_theme Implements hook_theme().
commerce_cart_user_login Implements hook_user_login().
commerce_cart_views_data_alter Implements hook_views_data_alter().
template_preprocess_commerce_cart_block Prepares variables for the cart block element template.
_commerce_cart_order_access Checks that the account has access to the cart.