acquia_commercemanager-8.x-1.122/modules/acm_sku/src/Commands/AcmSkuDrushCommands.php

modules/acm_sku/src/Commands/AcmSkuDrushCommands.php
<?php

namespace Drupal\acm_sku\Commands;

use Drupal\acm\Connector\APIWrapperInterface;
use Drupal\acm\Connector\IngestAPIWrapper;
use Drupal\acm\I18nHelper;
use Drupal\acm_sku\CategoryManager;
use Drupal\acm_sku\Entity\SKU;
use Drupal\acm_sku\Plugin\rest\resource\ProductSyncResource;
use Drupal\acm_sku\ProductOptionsManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\taxonomy\TermInterface;
use Drush\Commands\DrushCommands;
use Drush\Exceptions\UserAbortException;

/**
 * Class AcmSkuDrushCommands.
 *
 * @package Drupal\acm_sku\Commands
 */
class AcmSkuDrushCommands extends DrushCommands {

  const DELETE_BATCH_COUNT = 200;

  /**
   * Api Wrapper.
   *
   * @var \Drupal\acm\Connector\APIWrapperInterface
   */
  private $apiWrapper;

  /**
   * I18n Helper.
   *
   * @var \Drupal\acm\I18nHelper
   */
  private $i18nhelper;

  /**
   * Ingest Api Wrapper.
   *
   * @var \Drupal\acm\Connector\IngestAPIWrapper
   */
  private $ingestApiWrapper;

  /**
   * ACM Category manager service.
   *
   * @var \Drupal\acm_sku\CategoryManager
   */
  private $acmCategoryManager;

  /**
   * Product options manager.
   *
   * @var \Drupal\acm_sku\ProductOptionsManager
   */
  private $productOptionsManager;

  /**
   * Database Connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  private $connection;

  /**
   * Entity Type Manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  private $entityTypeManager;

  /**
   * Query Factory.
   *
   * @var \Drupal\Core\Entity\Query\QueryFactory
   */
  private $queryFactory;

  /**
   * Entity Manager.
   *
   * @var \Drupal\Core\Entity\EntityManagerInterface
   */
  private $entityManager;

  /**
   * Language Manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  private $languageManager;

  /**
   * Module Handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  private $moduleHandler;

  /**
   * Linked SKU cache service.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  private $linkedSkuCache;

  /**
   * Cache Tags Invalidator.
   *
   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface
   */
  private $cacheTagsInvalidator;

  /**
   * Product Node Type - default: acm_product.
   *
   * @var string
   */
  private $productNodeType;

  /**
   * SKU Field Name - default: field_skus.
   *
   * @var string
   */
  private $skuFieldName;

  /**
   * Category Vocabulary name - default: acm_product_category.
   *
   * @var string
   */
  private $categoryVid;

  /**
   * AcmSkuDrushCommands constructor.
   *
   * @param \Drupal\acm\Connector\APIWrapperInterface $api_wrapper
   *   API Wrapper.
   * @param \Drupal\acm\I18nHelper $i18n_helper
   *   I18n Helper.
   * @param \Drupal\acm\Connector\IngestAPIWrapper $ingest_api_wrapper
   *   Ingest API Wrapper.
   * @param \Drupal\acm_sku\CategoryManager $acm_category_manager
   *   ACM Category Manager.
   * @param \Drupal\acm_sku\ProductOptionsManager $product_options_manager
   *   Product Options Manager.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   Logger Factory.
   * @param \Drupal\Core\Database\Connection $connection
   *   Database Connection.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity Type Manager.
   * @param \Drupal\Core\Entity\Query\QueryFactory $query_factory
   *   Query Factory.
   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
   *   Entity Manager.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   Language Manager.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   Module Handler.
   * @param \Drupal\Core\Cache\CacheBackendInterface $linked_sku_cache
   *   Linked SKU cache service.
   * @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $cache_tags_invalidator
   *   Cache Tags Invalidator.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config Factory.
   */
  public function __construct(APIWrapperInterface $api_wrapper,
                              I18nHelper $i18n_helper,
                              IngestAPIWrapper $ingest_api_wrapper,
                              CategoryManager $acm_category_manager,
                              ProductOptionsManager $product_options_manager,
                              LoggerChannelFactoryInterface $logger_factory,
                              Connection $connection,
                              EntityTypeManagerInterface $entity_type_manager,
                              QueryFactory $query_factory,
                              EntityManagerInterface $entity_manager,
                              LanguageManagerInterface $language_manager,
                              ModuleHandlerInterface $module_handler,
                              CacheBackendInterface $linked_sku_cache,
                              CacheTagsInvalidatorInterface $cache_tags_invalidator,
                              ConfigFactoryInterface $config_factory) {
    parent::__construct();

    $this->apiWrapper = $api_wrapper;
    $this->i18nhelper = $i18n_helper;
    $this->ingestApiWrapper = $ingest_api_wrapper;
    $this->acmCategoryManager = $acm_category_manager;
    $this->productOptionsManager = $product_options_manager;
    $this->logger = $logger_factory->get('AcmSkuDrushCommands');
    $this->connection = $connection;
    $this->entityTypeManager = $entity_type_manager;
    $this->queryFactory = $query_factory;
    $this->entityManager = $entity_manager;
    $this->languageManager = $language_manager;
    $this->moduleHandler = $module_handler;
    $this->linkedSkuCache = $linked_sku_cache;
    $this->cacheTagsInvalidator = $cache_tags_invalidator;

    $config = $config_factory->get('acm.connector');
    $this->productNodeType = $config->get('product_node_type');
    $this->skuFieldName = $config->get('sku_field_name');
    $this->categoryVid = $config->get('category_vid');
  }

  /**
   * Run a full synchronization of all commerce product records.
   *
   * @param string $langcode
   *   Sync products available in this langcode.
   * @param string $page_size
   *   Number of items to be synced in one batch.
   * @param array $options
   *   Command options.
   *
   * @command acm_sku:sync-products
   *
   * @option skus SKUs to import (like query).
   * @option category_id Magento category id to sync the products for.
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases acsp,sync-commerce-products
   *
   * @usage drush acsp en 50
   *   Run a full product synchronization of all available products in store
   *   linked to en and page size 50.
   * @usage drush acsp en 50 --skus=\'M-H3495 130 2  FW\',\'M-H3496 130 004FW\',\'M-H3496 130 005FW\''
   *   Synchronize sku data for the skus M-H3495 130 2  FW, M-H3496 130 004FW
   *   & M-H3496 130 005FW only in store linked to en and page size 50.
   * @usage drush acsp en 50 --category_id=1234
   *   Synchronize sku data for the skus in category with id 1234 only in store
   *   linked to en and page size 50.
   *
   * @throws \Drush\Exceptions\UserAbortException
   */
  public function syncProducts($langcode, $page_size, array $options = ['skus' => NULL, 'category_id' => NULL]) {
    $langcode = strtolower($langcode);

    $store_id = $this->i18nhelper->getStoreIdFromLangcode($langcode);

    if (empty($store_id)) {
      $this->output->writeln(dt("Store id not found for provided language code."));
      return;
    }

    $page_size = (int) $page_size;

    if ($page_size <= 0) {
      $this->output->writeln(dt("Page size must be a positive integer."));
      return;
    }

    $skus = $options['skus'];

    $category_id = $options['category_id'];

    // Apply only one filer at a time.
    if ($category_id) {
      $skus = '';
    }

    // Ask for confirmation from user if attempt is to run full sync.
    if (empty($skus) && empty($category_id)) {
      $confirmation_text = dt('I CONFIRM');
      $input = $this->io()->ask(dt('Are you sure you want to import all products for @language language? If yes, type: "@confirmation"', [
        '@language' => $langcode,
        '@confirmation' => $confirmation_text,
      ]));

      if ($input != $confirmation_text) {
        throw new UserAbortException(dt('Please be more attentive in using this command and prove you are not sleep working...'));
      }
    }

    $this->output->writeln(dt('Requesting all commerce products for selected language code...'));
    $this->ingestApiWrapper->productFullSync($store_id, $langcode, $skus, $category_id, $page_size);
    $this->output->writeln(dt('Done.'));
  }

  /**
   * Run a full synchronization of all commerce product category records.
   *
   * @command acm_sku:sync-categories
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases acsc,sync-commerce-cats
   *
   * @usage drush acsc
   *   Run a full category synchronization of all available categories.
   */
  public function syncCategories() {
    $this->output->writeln(dt('Synchronizing all commerce categories, please wait...'));
    $response = $this->acmCategoryManager->synchronizeTree($this->categoryVid);

    // We trigger delete only if there is any term update/create.
    // So if API does not return anything, we don't delete all the categories.
    if (!empty($response['created']) || !empty($response['updated'])) {
      // Get all category terms with commerce id.
      $orphan_categories = $this->acmCategoryManager->getOrphanCategories($response);

      // If there are categories to delete.
      if (!empty($orphan_categories)) {
        // Show `tid + cat name + commerce id` for review.
        $this->io()->table([
          dt('Category Id'),
          dt('Category Name'),
          dt('Category Commerce Id'),
        ], $orphan_categories);

        // Confirmation to delete old categories.
        if ($this->io()->confirm(dt('Are you sure you want to clean these old categories'), FALSE)) {

          // Allow other modules to skipping the deleting of terms.
          $this->moduleHandler->alter('acm_sku_sync_categories_delete', $orphan_categories);

          foreach ($orphan_categories as $tid => $rs) {
            $term = $this->entityTypeManager->getStorage('taxonomy_term')->load($tid);
            if ($term instanceof TermInterface) {
              // Delete the term.
              $term->delete();
            }
          }
        }
      }
    }
    else {
      $this->logger->notice(dt('Not cleaning(deleting) old terms as there is no term update/create.'));
    }

    $this->output->writeln(dt('Done.'));
  }

  /**
   * Run a full synchronization of all commerce product options.
   *
   * @command acm_sku:sync-product-options
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases acspo,sync-commerce-product-options
   */
  public function syncProductOptions() {
    $this->logger->notice(dt('Synchronizing all commerce product options, please wait...'));
    $this->productOptionsManager->synchronizeProductOptions();
    $this->logger->notice(dt('Product attribute sync completed.'));
  }

  /**
   * Run a partial synchronization of commerce product records synchronously.
   *
   * This is used for testing / dev environments.
   *
   * @param int $count
   *   Number of product records to sync.
   *
   * @command acm_sku:sync-products-test
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases acdsp,sync-commerce-products-test
   *
   * @usage drush acdsp
   *   Run a partial synchronization of commerce product records synchronously
   *   for testing / dev.
   */
  public function syncProductsTest($count) {
    $this->output->writeln(dt('Synchronizing @count commerce products for testing / dev...', ['@count' => $count]));

    $container = \Drupal::getContainer();
    foreach ($this->i18nhelper->getStoreLanguageMapping() as $langcode => $store_id) {
      $this->apiWrapper->updateStoreContext($store_id);

      $products = $this->apiWrapper->getProducts($count);
      $product_sync_resource = ProductSyncResource::create($container, [], NULL, NULL);
      $product_sync_resource->post($products);
    }
  }

  /**
   * Remove all duplicate categories available in system.
   *
   * @command acm_sku:remove-category-duplicates
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases acccrd,commerce-cats-remove-duplicates
   *
   * @usage drush acccrd
   *   Remove all duplicate categories available in system.
   */
  public function removeCategoryDuplicates() {
    $this->output->writeln(dt('Cleaning all commerce categories, please wait...'));

    $db = $this->connection;

    /** @var \Drupal\taxonomy\TermStorageInterface $termStorage */
    $termStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');

    /** @var \Drupal\node\NodeStorageInterface $nodeStorage */
    $nodeStorage = \Drupal::entityTypeManager()->getStorage('node');

    $query = $db->select('taxonomy_term__field_commerce_id', 'ttfci');
    $query->addField('ttfci', 'field_commerce_id_value', 'commerce_id');
    $query->groupBy('commerce_id');
    $query->having('count(*) > 1');
    $result = $query->execute()->fetchAllKeyed(0, 0);

    if (empty($result)) {
      $this->output->writeln(dt('No duplicate categories found.'));
      return;
    }

    foreach ($result as $commerce_id) {
      $this->output->writeln(dt('Duplicate categories found for commerce id: @commerce_id.', [
        '@commerce_id' => $commerce_id,
      ]));

      $query = $db->select('taxonomy_term__field_commerce_id', 'ttfci');
      $query->addField('ttfci', 'entity_id', 'tid');
      $query->condition('ttfci.field_commerce_id_value', $commerce_id);
      $query->orderBy('tid', 'DESC');
      $tids = $query->execute()->fetchAllKeyed(0, 0);

      foreach ($tids as $tid) {
        $query = $nodeStorage->getQuery();
        $query->condition('field_category', $tid);
        $nodes = $query->execute();

        if (empty($nodes)) {
          $this->output->writeln(dt('No nodes found for tid: @tid for commerce id: @commerce_id. Deleting', [
            '@commerce_id' => $commerce_id,
            '@tid' => $tid,
          ]));

          $term = $termStorage->load($tid);
          $term->delete();

          unset($tids[$tid]);

          // Break the loop if only one left now, we might not have any products
          // added yet and categories are synced which means there will be no
          // nodes for any term.
          if (count($tids) == 1) {
            break;
          }
        }
        else {
          $this->output->writeln(dt('@count nodes found for tid: @tid for commerce id: @commerce_id. Not Deleting', [
            '@commerce_id' => $commerce_id,
            '@tid' => $tid,
            '@count' => count($nodes),
          ]));
        }
      }
    }

    $this->output->writeln(dt('Done.'));
  }

  /**
   * Remove all duplicate products available in system.
   *
   * @command acm_sku:remove-product-duplicates
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases accprd,commerce-products-remove-duplicates
   *
   * @usage drush accprd
   *   Remove all duplicate products available in system.
   */
  public function removeProductDuplicates() {
    $this->output->writeln(dt('Removing duplicates in commerce products, please wait...'));

    $skus_to_sync = [];

    /** @var \Drupal\node\NodeStorageInterface $nodeStorage */
    $nodeStorage = $this->entityTypeManager->getStorage('node');

    $query = $this->connection->select('acm_sku_field_data', 't1');
    $query->addField('t1', 'id', 'id');
    $query->addField('t1', 'sku', 'sku');
    $query->leftJoin('acm_sku_field_data', 't2', 't1.sku = t2.sku');
    $query->where('t1.id != t2.id');
    $result = $query->execute()->fetchAllKeyed(0, 1);

    if (empty($result)) {
      $this->output->writeln(dt('No duplicate skus found.'));
    }
    else {
      $skus = [];

      foreach ($result as $id => $sku) {
        $skus[$sku][$id] = $id;
        $skus_to_sync[$sku] = $sku;
      }

      foreach ($skus as $sku => $ids) {
        $this->output->writeln(dt('Duplicate skus found for sku: @sku with ids: @ids.', [
          '@sku' => $sku,
          '@ids' => implode(', ', $ids),
        ]));

        // Always delete the one with higher id, first one will have more
        // translations.
        sort($ids);

        // Remove the first id which we don't want to delete.
        array_shift($ids);

        foreach ($ids as $id) {
          $this->output->writeln(dt('Deleting sku with id @id for sku @sku.', [
            '@sku' => $sku,
            '@id' => $id,
          ]));

          $sku_entity = SKU::load($id);
          $sku_entity->delete();
        }
      }
    }

    $query = $this->connection->select('node__' . $this->skuFieldName, 't1');
    $query->addField('t1', 'entity_id', 'id');
    $query->addField('t1', $this->skuFieldName . '_value', 'sku');
    $query->leftJoin('node__' . $this->skuFieldName, 't2', 't1.' . $this->skuFieldName . '_value = t2.' . $this->skuFieldName . '_value');
    $query->where('t1.entity_id != t2.entity_id');
    $result = $query->execute()->fetchAllKeyed(0, 1);

    if (empty($result)) {
      $this->output->writeln(dt('No duplicate product nodes found.'));
    }
    else {
      $nids_to_delete = [];
      $skus = [];

      foreach ($result as $id => $sku) {
        $skus[$sku][$id] = $id;
        $skus_to_sync[$sku] = $sku;
      }

      foreach ($skus as $sku => $ids) {
        $this->output->writeln(dt('Duplicate nodes found for sku: @sku with ids: @ids.', [
          '@sku' => $sku,
          '@ids' => implode(', ', $ids),
        ]));

        // Always delete the one with higher nid, first one will have proper
        // url alias.
        sort($ids);

        // Remove the first id which we don't want to delete.
        array_shift($ids);

        foreach ($ids as $id) {
          $this->output->writeln(dt('Deleting node with id @id for sku @sku.', [
            '@sku' => $sku,
            '@id' => $id,
          ]));

          $nids_to_delete[$id] = $id;
        }
      }

      if ($nids_to_delete) {
        $nodeStorage->delete($nodeStorage->loadMultiple($nids_to_delete));
      }
    }

    if ($skus_to_sync) {
      $sku_texts = implode(',', $skus_to_sync);

      $this->output->writeln(dt('Requesting resync for skus @skus.', [
        '@skus' => $sku_texts,
      ]));

      foreach ($this->i18nhelper->getStoreLanguageMapping() as $langcode => $store_id) {
        // Using very small page size to avoid any issues for skus which already
        // had corrupt data.
        $this->ingestApiWrapper->productFullSync($store_id, $langcode, $sku_texts, NULL, 5);
      }
    }

    $this->output->writeln(dt('Done.'));
  }

  /**
   * Flush all commerce data from the site.
   *
   * Handles Products, SKUs, Product Categories and Product Options and allows
   * more data to be added for cleanup via alter hook.
   *
   * @command acm_sku:flush-synced-data
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases accd,clean-synced-data
   *
   * @usage drush accd
   *   Flush all commerce data from the site (Products, SKUs, Product Categories
   *   and Product Options).
   *
   * @throws \Drush\Exceptions\UserAbortException
   */
  public function flushSyncedData() {
    if (!$this->io()->confirm(dt("Are you sure you want to clean commerce data?"))) {
      throw new UserAbortException();
    }
    $this->output->writeln(dt('Cleaning synced commerce data, please wait...'));

    // Set batch operation.
    $batch = [
      'title' => t('Clean synced data'),
      'init_message' => t('Cleaning synced commerce data starting...'),
      'operations' => [
        ['\Drupal\acm_sku\Commands\AcmSkuDrushCommands::skuCleanProcess', []],
      ],
      'progress_message' => t('Processed @current out of @total.'),
      'error_message' => t('Synced data could not be cleaned because an error occurred.'),
      'finished' => '_acm_sku_clean_finished',
    ];

    batch_set($batch);
    drush_backend_batch_process();
    $this->output->writeln(dt('Synced commerce data cleaned.'));
  }

  /**
   * Function to process entity delete operation.
   *
   * @param mixed|array $context
   *   The batch current context.
   */
  public static function skuCleanProcess(&$context) {
    // Use the $context['sandbox'] at your convenience to store the
    // information needed to track progression between successive calls.
    if (empty($context['sandbox'])) {
      $config = \Drupal::config('acm.connector');

      // Get all the entities that need to be deleted.
      $context['sandbox']['results'] = [];

      // Get all product entities.
      $query = \Drupal::entityQuery('node');
      $query->condition('type', $config->get('product_node_type'));
      $product_entities = $query->execute();
      foreach ($product_entities as $entity_id) {
        $context['sandbox']['results'][] = [
          'type' => 'node',
          'entity_id' => $entity_id,
        ];
      }

      // Get all acm_sku entities.
      $query = \Drupal::entityQuery('acm_sku');
      $sku_entities = $query->execute();
      foreach ($sku_entities as $entity_id) {
        $context['sandbox']['results'][] = [
          'type' => 'acm_sku',
          'entity_id' => $entity_id,
        ];
      }

      // Get all taxonomy_term entities.
      $categories = [$config->get('category_vid'), 'sku_product_option'];
      $query = \Drupal::entityQuery('taxonomy_term');
      $query->condition('vid', $categories, 'IN');
      $cat_entities = $query->execute();
      foreach ($cat_entities as $entity_id) {
        $context['sandbox']['results'][] = [
          'type' => 'taxonomy_term',
          'entity_id' => $entity_id,
        ];
      }

      // Allow other modules to add data to be deleted when cleaning up.
      \Drupal::moduleHandler()->alter('acm_sku_clean_synced_data', $context);

      $context['sandbox']['progress'] = 0;
      $context['sandbox']['current_id'] = 0;
      $context['sandbox']['max'] = count($context['sandbox']['results']);
    }

    $results = [];
    if (isset($context['sandbox']['results']) && !empty($context['sandbox']['results'])) {
      $results = $context['sandbox']['results'];
    }

    $results = array_slice($results, isset($context['sandbox']['current']) ? $context['sandbox']['current'] : 0, self::DELETE_BATCH_COUNT);

    $delete = [];

    foreach ($results as $key => $result) {
      $context['results'][] = $results['type'] . ' : ' . $result['entity_id'];
      $context['sandbox']['progress']++;
      $context['sandbox']['current_id'] = $result['entity_id'];

      $delete[$result['type']][] = $result['entity_id'];

      // Update our progress information.
      $context['sandbox']['current']++;
    }

    foreach ($delete as $type => $entity_ids) {
      try {
        $storage = \Drupal::entityTypeManager()->getStorage($type);
        $entities = $storage->loadMultiple($entity_ids);
        $storage->delete($entities);
      }
      catch (\Exception $e) {
        \Drupal::logger('AcmSkuDrushCommands')->error($e->getMessage());
      }
    }

    $context['message'] = 'Processed ' . $context['sandbox']['progress'] . ' out of ' . $context['sandbox']['max'] . '.';

    if ($context['sandbox']['progress'] !== $context['sandbox']['max']) {
      $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
    }
  }

  /**
   * Clear linked SKUs cache.
   *
   * @param array $options
   *   Command Options.
   *
   * @command acm_sku:clear-linked-skus-cache
   *
   * @option sku SKU to clean linked skus cache of.
   *
   * @validate-module-enabled acm_sku
   *
   * @aliases acclsc,clear-linked-skus-cache
   *
   * @usage drush acclsc
   *   Clear linked SKUs cache for all SKUs.
   * @usage drush acclsc --skus=SKU
   *   Clear linked SKUs cache for particular SKU.
   *
   * @throws \Drush\Exceptions\UserAbortException
   */
  public function flushLinkedSkuCache(array $options = ['sku' => NULL]) {
    // Check if we are asked to clear cache of specific SKU.
    if (!empty($options['sku'])) {
      if ($sku_entity = SKU::loadFromSku($options['sku'])) {
        $this->cacheTagsInvalidator->invalidateTags([
          'acm_sku:linked_skus:' . $sku_entity->id(),
          'acm_sku:' . $sku_entity->id(),
        ]);

        $this->output->writeln(dt('Invalidated linked SKUs cache for @sku.', [
          '@sku' => $options['sku'],
        ]));
      }

      return;
    }

    if (!$this->io()->confirm(dt('Are you sure you want to clear linked SKUs cache for all SKUs?'))) {
      throw new UserAbortException();
    }

    $this->linkedSkuCache->deleteAll();

    $this->output->writeln(dt('Cleared all linked SKUs cache.'));
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc