arch-8.x-1.x-dev/modules/cart/src/Plugin/Block/MiniCartBlock.php
modules/cart/src/Plugin/Block/MiniCartBlock.php
<?php namespace Drupal\arch_cart\Plugin\Block; use Drupal\arch_cart\Cart\CartHandlerInterface; use Drupal\arch_product\Entity\ProductTypeInterface; use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResultInterface; use Drupal\Core\Block\BlockBase; use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Theme\ThemeManagerInterface; use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a 'Commerce Mini Cart' block. * * @Block( * id = "arch_cart_mini_cart", * admin_label = @Translation("Mini cart", context = "arch_cart") * ) */ class MiniCartBlock extends BlockBase implements ContainerFactoryPluginInterface { /** * Entity field manager. * * @var \Drupal\Core\Entity\EntityFieldManagerInterface */ protected $entityFieldManager; /** * Entity type manager. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; /** * Image style storage. * * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface */ protected $imageStyleStorage; /** * Product type storage. * * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface */ protected $productTypeStorage; /** * Cart. * * @var \Drupal\arch_cart\Cart\CartInterface */ protected $cart; /** * Current route match. * * @var \Drupal\Core\Routing\RouteMatchInterface */ protected $routeMatch; /** * Module handler. * * @var \Drupal\Core\Extension\ModuleHandlerInterface */ protected $moduleHandler; /** * Theme manager. * * @var \Drupal\Core\Theme\ThemeManagerInterface */ protected $themeManager; /** * Image style options. * * @var string[][] */ protected $imageStyleOptions; /** * Bundle settings. * * @var array */ protected $bundleInfo = []; /** * {@inheritdoc} */ public function __construct( array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, CartHandlerInterface $cart_handler, RouteMatchInterface $route_match, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager, ) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->entityFieldManager = $entity_field_manager; $this->entityTypeManager = $entity_type_manager; $this->imageStyleStorage = $entity_type_manager->getStorage('image_style'); $this->productTypeStorage = $entity_type_manager->getStorage('product_type'); $this->cart = $cart_handler->getCart(); $this->routeMatch = $route_match; $this->moduleHandler = $module_handler; $this->themeManager = $theme_manager; } /** * {@inheritdoc} */ public static function create( ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, ) { return new static( $configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager'), $container->get('entity_field.manager'), $container->get('arch_cart_handler'), $container->get('current_route_match'), $container->get('module_handler'), $container->get('theme.manager') ); } /** * {@inheritdoc} * * @todo Make cacheable in https://www.drupal.org/node/2483181 */ public function getCacheMaxAge() { return 0; } /** * {@inheritdoc} */ public function defaultConfiguration() { return [ 'show_cart_item_count' => TRUE, 'click_event' => 'open', 'allow_modify_quantity' => TRUE, 'allow_remove' => TRUE, ]; } /** * {@inheritdoc} */ public function blockForm($form, FormStateInterface $form_state) { $config = $this->configuration; $form['show_cart_item_count'] = [ '#type' => 'checkbox', '#title' => $this->t('Show cart item count', [], ['context' => 'arch_cart_minicart_config']), '#default_value' => $config['show_cart_item_count'], ]; $form['click_event'] = [ '#type' => 'select', '#title' => $this->t('When user clicks on cart icon', [], ['context' => 'arch_cart_minicart_config']), '#default_value' => $config['click_event'], '#options' => [ 'open' => $this->t('Open mini cart block', [], ['context' => 'arch_cart_minicart_config']), 'link' => $this->t('Go to cart page', [], ['context' => 'arch_cart_minicart_config']), ], ]; $form['allow_modify_quantity'] = [ '#type' => 'checkbox', '#title' => $this->t('Allow modify quantities', [], ['context' => 'arch_cart_minicart_config']), '#default_value' => $config['allow_modify_quantity'], '#states' => [ 'visible' => [ ':input[name="settings[click_event]"]' => ['value' => 'open'], ], ], ]; $form['allow_remove'] = [ '#type' => 'checkbox', '#title' => $this->t('Allow removing items', [], ['context' => 'arch_cart_minicart_config']), '#default_value' => $config['allow_remove'], '#states' => [ 'visible' => [ ':input[name="settings[click_event]"]' => ['value' => 'open'], ], ], ]; $form['bundle_settings'] = [ '#type' => 'vertical_tabs', '#title' => $this->t('Bundle settings'), '#access' => FALSE, '#states' => [ 'visible' => [ ':input[name="settings[click_event]"]' => ['value' => 'open'], ], ], ]; foreach ($this->getProductTypes() as $product_type) { $form['product_type_' . $product_type->id()] = [ '#type' => 'details', '#access' => FALSE, '#title' => $product_type->label(), '#group' => 'bundle_settings', '#states' => [ 'visible' => [ ':input[name="settings[click_event]"]' => ['value' => 'open'], ], ], ]; } foreach ($this->getProductTypesWithImageField() as $bundle) { $image_sources = $this->getImageSourcesForProductType($bundle); if (empty($image_sources)) { continue; } $form['bundle_settings']['#access'] = TRUE; $form['product_type_' . $bundle->id()]['#access'] = TRUE; $source_options = []; foreach ($image_sources as $image_source) { $source_options[$image_source['field']] = $image_source['label']; } $form['product_type_' . $bundle->id()]['image_source'] = [ '#type' => 'select', '#title' => $this->t('Image source', [], ['context' => 'arch_cart_minicart_config']), '#default_value' => !empty($config['bundle_settings'][$bundle->id()]['image_source']) ? $config['bundle_settings'][$bundle->id()]['image_source'] : '', '#options' => $source_options, ]; $form['product_type_' . $bundle->id()]['image_style'] = [ '#type' => 'select', '#title' => $this->t('Image style', [], ['context' => 'arch_cart_minicart_config']), '#default_value' => !empty($config['bundle_settings'][$bundle->id()]['image_style']) ? $config['bundle_settings'][$bundle->id()]['image_style'] : '', '#options' => $this->getImageStyleOptions(), ]; } return $form; } /** * {@inheritdoc} */ public function blockSubmit($form, FormStateInterface $form_state) { $this->configuration['show_cart_item_count'] = (bool) $form_state->getValue('show_cart_item_count'); $this->configuration['click_event'] = $form_state->getValue('click_event'); if ($this->configuration['click_event'] == 'open') { $this->configuration['allow_modify_quantity'] = (bool) $form_state->getValue('allow_modify_quantity'); $this->configuration['allow_remove'] = (bool) $form_state->getValue('allow_remove'); foreach ($this->getProductTypes() as $bundle) { $this->configuration['bundle_settings'][$bundle->id()] = [ 'image_source' => $form_state->getValue([ 'product_type_' . $bundle->id(), 'image_source', ]), 'image_style' => $form_state->getValue([ 'product_type_' . $bundle->id(), 'image_style', ]), ]; } } else { $this->configuration['allow_modify_quantity'] = FALSE; $this->configuration['allow_remove'] = FALSE; $this->configuration['bundle_settings'] = []; } } /** * {@inheritdoc} */ public function build() { $url = new Url('arch_cart.content'); $link_attributes = [ 'class' => [ 'cart--icon', 'api-cart--link', ], 'data-count' => $this->cart->getCount(), ]; $settings = [ 'theme' => $this->themeManager->getActiveTheme()->getName(), 'show_cart_item_count' => $this->configuration['show_cart_item_count'], 'click_event' => $this->configuration['click_event'], 'allow_modify_quantity' => $this->configuration['allow_modify_quantity'], 'allow_remove' => $this->configuration['allow_remove'], 'bundle_settings' => $this->configuration['bundle_settings'] ?? [], ]; if (!$this->allowMiniCartDisplay()) { $settings['allow_modify_quantity'] = FALSE; $settings['allow_remove'] = FALSE; $link_attributes['class'][] = 'api-cart--display-disabled'; $link_attributes['data-api-cart-disabled'] = 'disabled'; } $build = [ '#cache' => [ 'context' => [ 'user', 'session', 'route.name', ], ], '#theme' => 'mini_cart', '#settings' => $settings, '#url' => $url, '#text' => $this->t('Cart', [], ['context' => 'arch_cart']), '#attributes' => [ 'class' => ['mini-cart'], ], '#count' => $this->cart->getCount(), '#link_attributes' => $link_attributes, '#templates' => [ '#theme' => 'api_cart_template', ], ]; return $build; } /** * Get list of product types. * * @return \Drupal\arch_product\Entity\ProductTypeInterface[] * Product type list. */ protected function getProductTypes() { /** @var \Drupal\arch_product\Entity\ProductTypeInterface[] $product_types */ $product_types = $this->productTypeStorage->loadMultiple(); return $product_types; } /** * Get product types with image fields. * * @return \Drupal\arch_product\Entity\ProductTypeInterface[] * Product type entity list. */ protected function getProductTypesWithImageField() { return array_filter($this->getProductTypes(), [$this, 'typeHasImageFields']); } /** * Get image style options. * * @return string[][] * Image style options. */ protected function getImageStyleOptions() { if (!isset($this->imageStyleOptions)) { $options = [ '' => $this->t('Default'), ]; foreach ($this->imageStyleStorage->loadMultiple() as $image_style) { /** @var \Drupal\image\ImageStyleInterface $image_style */ $options[$image_style->id()] = $image_style->label(); } $this->imageStyleOptions = $options; } return $this->imageStyleOptions; } /** * Check if given product type has image fields. * * @param \Drupal\arch_product\Entity\ProductTypeInterface $product_type * Product type. * * @return bool * Return TRUE if given bundle has image field. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function typeHasImageFields(ProductTypeInterface $product_type) { $image_fields = $this->getImageSourcesForProductType($product_type); return !empty($image_fields); } /** * Get list of field can use as image source. * * @param \Drupal\arch_product\Entity\ProductTypeInterface $product_type * Product type. * * @return array * Image sources. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function getImageSourcesForProductType(ProductTypeInterface $product_type) { $key = 'product_type:' . $product_type->id() . ':image_fields'; if (!isset($this->bundleInfo[$key])) { $this->bundleInfo[$key] = $this->getImageSourcesForBundle('product', $product_type); } return $this->bundleInfo[$key]; } /** * Get list of fields can use as image source. * * @param string $target_entity_type * Entity type ID. * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $bundle * Entity bundle config. * @param null|string $prefix * Label prefix. * * @return array * Image source info. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ protected function getImageSourcesForBundle($target_entity_type, ConfigEntityInterface $bundle, $prefix = NULL) { $fields = $this->entityFieldManager->getFieldDefinitions($target_entity_type, $bundle->id()); $sources = []; foreach ($fields as $id => $field) { if ( $field->getType() === 'image' || ( $field->getType() === 'entity_reference' && $field->getSetting('target_type') == 'image' ) ) { $sources[$id] = [ 'field' => $id, 'type' => 'image', 'label' => $this->fieldLabel($field->getLabel(), $prefix), ]; continue; } if ( $field->getType() === 'entity_reference' && $field->getSetting('target_type') == 'media' ) { $handler_settings = $field->getSetting('handler_settings'); $media_bundles = !empty($handler_settings['target_bundles']) ? $handler_settings['target_bundles'] : []; foreach ($media_bundles as $media_bundle_id) { $media_bundle = $this->entityTypeManager->getStorage('media_type')->load($media_bundle_id); $media_image_fields = $this->getImageSourcesForBundle('media', $media_bundle, $field->getLabel()); foreach ($media_image_fields as $field_name => $media_image_field) { $sources[$id . ':' . $field_name] = [ 'field' => $id . ':' . $media_image_field['field'], 'type' => $media_image_field['type'], 'label' => $this->fieldLabel($media_image_field['label'], $prefix), ]; } } } } return $sources; } /** * Build field label. * * @param string $label * Field label. * @param string $prefix * Field prefix. * * @return string * Option label. */ protected function fieldLabel($label, $prefix) { if (empty($prefix)) { return $label; } return $prefix . ' - ' . $label; } /** * Check if minicart display is allowed. * * @return bool * Return result. */ protected function allowMiniCartDisplay() { if ($this->routeMatch->getRouteName() === 'arch_checkout.checkout') { $return = AccessResult::forbidden(); } else { $return = AccessResult::neutral(); } $results = $this->moduleHandler->invokeAll('arch_minicart_display_allowed'); foreach ($results as $result) { if ($result instanceof AccessResultInterface) { $return->andIf($result); } } $this->moduleHandler->alter('arch_minicart_display_allowed', $return); if ($return instanceof AccessResultInterface) { return !$return->isForbidden(); } return (bool) $return; } }