Importer.php 5.97 KB
Newer Older
Ketan's avatar
Ketan committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
namespace Magento\Store\Model\Config;

use Magento\Framework\App\CacheInterface;
use Magento\Framework\App\DeploymentConfig\ImporterInterface;
use Magento\Framework\Exception\State\InvalidTransitionException;
use Magento\Store\Model\Config\Importer\DataDifferenceCalculator;
use Magento\Store\Model\Config\Importer\Processor\ProcessorFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Store\Model\ResourceModel\Website;

/**
 * Imports stores, websites and groups from transmitted data.
 */
class Importer implements ImporterInterface
{
    /**
     * The data difference calculator.
     *
     * @var DataDifferenceCalculator
     */
    private $dataDifferenceCalculator;

    /**
     * The factory for processors.
     *
     * @var ProcessorFactory
     */
    private $processFactory;

    /**
     * The manager for operations with store.
     *
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * The resource of transaction.
     *
     * @var Website
     */
    private $resource;

    /**
     * The application cache manager.
     *
     * @var CacheInterface
     */
    private $cacheManager;

    /**
     * @param DataDifferenceCalculator $dataDifferenceCalculator The factory for data difference calculators
     * @param ProcessorFactory $processFactory The factory for processes
     * @param StoreManagerInterface $storeManager The manager for operations with store
     * @param CacheInterface $cacheManager The application cache manager
     * @param Website $resource The resource of transaction
     */
    public function __construct(
        DataDifferenceCalculator $dataDifferenceCalculator,
        ProcessorFactory $processFactory,
        StoreManagerInterface $storeManager,
        CacheInterface $cacheManager,
        Website $resource
    ) {
        $this->dataDifferenceCalculator = $dataDifferenceCalculator;
        $this->processFactory = $processFactory;
        $this->storeManager = $storeManager;
        $this->cacheManager = $cacheManager;
        $this->resource = $resource;
    }

    /**
     * Imports the store data into the application.
     * After the import it flushes the store caches.
     *
     * {@inheritdoc}
     */
    public function import(array $data)
    {
        $actions = [
            ProcessorFactory::TYPE_CREATE,
            ProcessorFactory::TYPE_DELETE,
            ProcessorFactory::TYPE_UPDATE
        ];
        $messages = ['Stores were processed'];

        try {
            $newGroups = $this->getGroupsToCreate($data);

            if ($newGroups) {
                $messages[] = sprintf(
                    $this->getStoreGroupAssignMessage(),
                    implode(', ', array_column($newGroups, 'name'))
                );
            }
            // during import websites/stores database already has new entities
            // but cache is outdated which can cause to error in some cases
            $this->reinitStores();

            $this->resource->beginTransaction();

            foreach ($actions as $action) {
                $this->processFactory->create($action)->run($data);
            }
        } catch (\Exception $exception) {
            $this->resource->rollBack();
            $this->reinitStores();

            throw new InvalidTransitionException(__('%1', $exception->getMessage()), $exception);
        }

        $this->resource->commit();
        $this->reinitStores();

        return $messages;
    }

    /**
     * Reinitialize store list.
     *
     * @return void
     */
    private function reinitStores()
    {
        $this->storeManager->reinitStores();
        $this->cacheManager->clean();
    }

    /**
     * Retrieves message reminder about root category assigning.
     *
     * @return string
     */
    private function getStoreGroupAssignMessage()
    {
        return 'The following new store groups must be associated with a root category: %s. '
            . PHP_EOL
            . 'Associate a store group with a root category in the Admin Panel: Stores > Settings > All Stores.';
    }

    /**
     * Checks which new store groups will be created.
     *
     * @param array $data The data set.
     * @return array
     */
    private function getGroupsToCreate(array $data)
    {
        if (!isset($data[ScopeInterface::SCOPE_GROUPS])) {
            return [];
        }

        $groups = $this->dataDifferenceCalculator->getItemsToCreate(
            ScopeInterface::SCOPE_GROUPS,
            $data[ScopeInterface::SCOPE_GROUPS]
        );

        return $groups;
    }

    /**
     * Retrieves all affected entities during the import procedure.
     *
     * {@inheritdoc}
     */
    public function getWarningMessages(array $data)
    {
        $messages = [];

        foreach ($data as $scope => $scopeData) {
            $messageMap = [
                'These %s will be deleted: %s' => $this->dataDifferenceCalculator->getItemsToDelete($scope, $scopeData),
                'These %s will be updated: %s' => $this->dataDifferenceCalculator->getItemsToUpdate($scope, $scopeData),
                'These %s will be created: %s' => $this->dataDifferenceCalculator->getItemsToCreate($scope, $scopeData),
            ];

            foreach ($messageMap as $message => $items) {
                if (!$items) {
                    continue;
                }

                $messages[] = $this->formatMessage($message, $items, $scope);
            }
        }

        return $messages;
    }

    /**
     * Formats message to appropriate format.
     *
     * @param string $message The message to display
     * @param array $items The items to be used
     * @param string $scope The given scope
     * @return string
     */
    private function formatMessage($message, array $items, $scope)
    {
        return sprintf(
            $message,
            ucfirst($scope),
            implode(', ', array_column($items, 'name'))
        );
    }
}