123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\CatalogUrlRewrite\Observer;
- use Magento\Catalog\Model\Category;
- use Magento\Catalog\Model\Product\Visibility;
- use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
- use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
- use Magento\CatalogImportExport\Model\Import\Product as ImportProduct;
- use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
- use Magento\Framework\App\ObjectManager;
- use Magento\Framework\Event\Observer;
- use Magento\Framework\Event\ObserverInterface;
- use Magento\ImportExport\Model\Import as ImportExport;
- use Magento\Store\Model\Store;
- use Magento\UrlRewrite\Model\MergeDataProviderFactory;
- use Magento\UrlRewrite\Model\OptionProvider;
- use Magento\UrlRewrite\Model\UrlFinderInterface;
- use Magento\UrlRewrite\Model\UrlPersistInterface;
- use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
- use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory;
- /**
- * Class AfterImportDataObserver
- *
- * @SuppressWarnings(PHPMD.TooManyFields)
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- */
- class AfterImportDataObserver implements ObserverInterface
- {
- /**
- * Url Key Attribute
- */
- const URL_KEY_ATTRIBUTE_CODE = 'url_key';
- /**
- * @var \Magento\CatalogUrlRewrite\Service\V1\StoreViewService
- */
- protected $storeViewService;
- /**
- * @var \Magento\Catalog\Model\Product
- */
- protected $product;
- /**
- * @var array
- */
- protected $productsWithStores;
- /**
- * @var array
- */
- protected $products = [];
- /**
- * @var \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory
- */
- protected $objectRegistryFactory;
- /**
- * @var \Magento\CatalogUrlRewrite\Model\ObjectRegistry
- */
- protected $productCategories;
- /**
- * @var \Magento\UrlRewrite\Model\UrlFinderInterface
- */
- protected $urlFinder;
- /**
- * @var \Magento\Store\Model\StoreManagerInterface
- */
- protected $storeManager;
- /**
- * @var \Magento\UrlRewrite\Model\UrlPersistInterface
- */
- protected $urlPersist;
- /**
- * @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory
- */
- protected $urlRewriteFactory;
- /**
- * @var \Magento\CatalogImportExport\Model\Import\Product
- */
- protected $import;
- /**
- * @var \Magento\Catalog\Model\ProductFactory
- */
- protected $catalogProductFactory;
- /**
- * @var array
- */
- protected $acceptableCategories;
- /**
- * @var \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator
- */
- protected $productUrlPathGenerator;
- /**
- * @var array
- */
- protected $websitesToStoreIds;
- /**
- * @var array
- */
- protected $storesCache = [];
- /**
- * @var array
- */
- protected $categoryCache = [];
- /**
- * @var array
- */
- protected $websiteCache = [];
- /**
- * @var array
- */
- protected $vitalForGenerationFields = [
- 'sku',
- 'url_key',
- 'url_path',
- 'name',
- 'visibility',
- 'save_rewrites_history'
- ];
- /**
- * @var \Magento\UrlRewrite\Model\MergeDataProvider
- */
- private $mergeDataProviderPrototype;
- /**
- * Factory for creating category collection.
- *
- * @var CategoryCollectionFactory
- */
- private $categoryCollectionFactory;
- /**
- * Array of invoked categories during url rewrites generation.
- *
- * @var array
- */
- private $categoriesCache = [];
- /**
- * @param \Magento\Catalog\Model\ProductFactory $catalogProductFactory
- * @param \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory $objectRegistryFactory
- * @param \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator $productUrlPathGenerator
- * @param \Magento\CatalogUrlRewrite\Service\V1\StoreViewService $storeViewService
- * @param \Magento\Store\Model\StoreManagerInterface $storeManager
- * @param UrlPersistInterface $urlPersist
- * @param UrlRewriteFactory $urlRewriteFactory
- * @param UrlFinderInterface $urlFinder
- * @param \Magento\UrlRewrite\Model\MergeDataProviderFactory|null $mergeDataProviderFactory
- * @param CategoryCollectionFactory|null $categoryCollectionFactory
- * @SuppressWarnings(PHPMD.ExcessiveParameterList)
- */
- public function __construct(
- \Magento\Catalog\Model\ProductFactory $catalogProductFactory,
- \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory $objectRegistryFactory,
- \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator $productUrlPathGenerator,
- \Magento\CatalogUrlRewrite\Service\V1\StoreViewService $storeViewService,
- \Magento\Store\Model\StoreManagerInterface $storeManager,
- UrlPersistInterface $urlPersist,
- UrlRewriteFactory $urlRewriteFactory,
- UrlFinderInterface $urlFinder,
- MergeDataProviderFactory $mergeDataProviderFactory = null,
- CategoryCollectionFactory $categoryCollectionFactory = null
- ) {
- $this->urlPersist = $urlPersist;
- $this->catalogProductFactory = $catalogProductFactory;
- $this->objectRegistryFactory = $objectRegistryFactory;
- $this->productUrlPathGenerator = $productUrlPathGenerator;
- $this->storeViewService = $storeViewService;
- $this->storeManager = $storeManager;
- $this->urlRewriteFactory = $urlRewriteFactory;
- $this->urlFinder = $urlFinder;
- if (!isset($mergeDataProviderFactory)) {
- $mergeDataProviderFactory = ObjectManager::getInstance()->get(MergeDataProviderFactory::class);
- }
- $this->mergeDataProviderPrototype = $mergeDataProviderFactory->create();
- $this->categoryCollectionFactory = $categoryCollectionFactory ?:
- ObjectManager::getInstance()->get(CategoryCollectionFactory::class);
- }
- /**
- * Action after data import.
- *
- * Save new url rewrites and remove old if exist.
- *
- * @param Observer $observer
- *
- * @return void
- */
- public function execute(Observer $observer)
- {
- $this->import = $observer->getEvent()->getAdapter();
- if ($products = $observer->getEvent()->getBunch()) {
- foreach ($products as $product) {
- $this->_populateForUrlGeneration($product);
- }
- $productUrls = $this->generateUrls();
- if ($productUrls) {
- $this->urlPersist->replace($productUrls);
- }
- }
- }
- /**
- * Create product model from imported data for URL rewrite purposes.
- *
- * @param array $rowData
- *
- * @return ImportExport
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- */
- protected function _populateForUrlGeneration($rowData)
- {
- $newSku = $this->import->getNewSku($rowData[ImportProduct::COL_SKU]);
- if (empty($newSku) || !isset($newSku['entity_id'])) {
- return null;
- }
- if ($this->import->getRowScope($rowData) == ImportProduct::SCOPE_STORE
- && empty($rowData[self::URL_KEY_ATTRIBUTE_CODE])) {
- return null;
- }
- $rowData['entity_id'] = $newSku['entity_id'];
- $product = $this->catalogProductFactory->create();
- $product->setId($rowData['entity_id']);
- foreach ($this->vitalForGenerationFields as $field) {
- if (isset($rowData[$field])) {
- $product->setData($field, $rowData[$field]);
- }
- }
- $this->categoryCache[$rowData['entity_id']] = $this->import->getProductCategories($rowData['sku']);
- $this->websiteCache[$rowData['entity_id']] = $this->import->getProductWebsites($rowData['sku']);
- foreach ($this->websiteCache[$rowData['entity_id']] as $websiteId) {
- if (!isset($this->websitesToStoreIds[$websiteId])) {
- $this->websitesToStoreIds[$websiteId] = $this->storeManager->getWebsite($websiteId)->getStoreIds();
- }
- }
- $this->setStoreToProduct($product, $rowData);
- if ($this->isGlobalScope($product->getStoreId())) {
- $this->populateGlobalProduct($product);
- } else {
- $this->addProductToImport($product, $product->getStoreId());
- }
- return $this;
- }
- /**
- * Add store id to product data.
- *
- * @param \Magento\Catalog\Model\Product $product
- * @param array $rowData
- * @return void
- */
- protected function setStoreToProduct(\Magento\Catalog\Model\Product $product, array $rowData)
- {
- if (!empty($rowData[ImportProduct::COL_STORE])
- && ($storeId = $this->import->getStoreIdByCode($rowData[ImportProduct::COL_STORE]))
- ) {
- $product->setStoreId($storeId);
- } elseif (!$product->hasData(\Magento\Catalog\Model\Product::STORE_ID)) {
- $product->setStoreId(Store::DEFAULT_STORE_ID);
- }
- }
- /**
- * Add product to import
- *
- * @param \Magento\Catalog\Model\Product $product
- * @param string $storeId
- * @return $this
- */
- protected function addProductToImport($product, $storeId)
- {
- if ($product->getVisibility() == (string)Visibility::getOptionArray()[Visibility::VISIBILITY_NOT_VISIBLE]) {
- return $this;
- }
- if (!isset($this->products[$product->getId()])) {
- $this->products[$product->getId()] = [];
- }
- $this->products[$product->getId()][$storeId] = $product;
- return $this;
- }
- /**
- * Populate global product
- *
- * @param \Magento\Catalog\Model\Product $product
- * @return $this
- */
- protected function populateGlobalProduct($product)
- {
- foreach ($this->import->getProductWebsites($product->getSku()) as $websiteId) {
- foreach ($this->websitesToStoreIds[$websiteId] as $storeId) {
- $this->storesCache[$storeId] = true;
- if (!$this->isGlobalScope($storeId)) {
- $this->addProductToImport($product, $storeId);
- }
- }
- }
- return $this;
- }
- /**
- * Generate product url rewrites
- *
- * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[]
- */
- protected function generateUrls()
- {
- $mergeDataProvider = clone $this->mergeDataProviderPrototype;
- $mergeDataProvider->merge($this->canonicalUrlRewriteGenerate());
- $mergeDataProvider->merge($this->categoriesUrlRewriteGenerate());
- $mergeDataProvider->merge($this->currentUrlRewritesRegenerate());
- $this->productCategories = null;
- unset($this->products);
- $this->products = [];
- return $mergeDataProvider->getData();
- }
- /**
- * Check is global scope
- *
- * @param int|null $storeId
- * @return bool
- */
- protected function isGlobalScope($storeId)
- {
- return null === $storeId || $storeId == Store::DEFAULT_STORE_ID;
- }
- /**
- * Generate list based on store view
- *
- * @return UrlRewrite[]
- */
- protected function canonicalUrlRewriteGenerate()
- {
- $urls = [];
- foreach ($this->products as $productId => $productsByStores) {
- foreach ($productsByStores as $storeId => $product) {
- if ($this->productUrlPathGenerator->getUrlPath($product)) {
- $urls[] = $this->urlRewriteFactory->create()
- ->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE)
- ->setEntityId($productId)
- ->setRequestPath($this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId))
- ->setTargetPath($this->productUrlPathGenerator->getCanonicalUrlPath($product))
- ->setStoreId($storeId);
- }
- }
- }
- return $urls;
- }
- /**
- * Generate list based on categories.
- *
- * @return UrlRewrite[]
- */
- protected function categoriesUrlRewriteGenerate()
- {
- $urls = [];
- foreach ($this->products as $productId => $productsByStores) {
- foreach ($productsByStores as $storeId => $product) {
- foreach ($this->categoryCache[$productId] as $categoryId) {
- $category = $this->getCategoryById($categoryId, $storeId);
- if ($category->getParentId() == Category::TREE_ROOT_ID) {
- continue;
- }
- $requestPath = $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId, $category);
- $urls[] = $this->urlRewriteFactory->create()
- ->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE)
- ->setEntityId($productId)
- ->setRequestPath($requestPath)
- ->setTargetPath($this->productUrlPathGenerator->getCanonicalUrlPath($product, $category))
- ->setStoreId($storeId)
- ->setMetadata(['category_id' => $category->getId()]);
- }
- }
- }
- return $urls;
- }
- /**
- * Generate list based on current rewrites
- *
- * @return UrlRewrite[]
- */
- protected function currentUrlRewritesRegenerate()
- {
- $currentUrlRewrites = $this->urlFinder->findAllByData(
- [
- UrlRewrite::STORE_ID => array_keys($this->storesCache),
- UrlRewrite::ENTITY_ID => array_keys($this->products),
- UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE,
- ]
- );
- $urlRewrites = [];
- foreach ($currentUrlRewrites as $currentUrlRewrite) {
- $category = $this->retrieveCategoryFromMetadata($currentUrlRewrite);
- if ($category === false) {
- continue;
- }
- $url = $currentUrlRewrite->getIsAutogenerated()
- ? $this->generateForAutogenerated($currentUrlRewrite, $category)
- : $this->generateForCustom($currentUrlRewrite, $category);
- $urlRewrites = array_merge($urlRewrites, $url);
- }
- $this->product = null;
- $this->productCategories = null;
- return $urlRewrites;
- }
- /**
- * Generate url-rewrite for outogenerated url-rewirte.
- *
- * @param UrlRewrite $url
- * @param Category $category
- * @return array
- */
- protected function generateForAutogenerated($url, $category)
- {
- $storeId = $url->getStoreId();
- $productId = $url->getEntityId();
- if (isset($this->products[$productId][$storeId])) {
- $product = $this->products[$productId][$storeId];
- if (!$product->getData('save_rewrites_history')) {
- return [];
- }
- $targetPath = $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId, $category);
- if ($url->getRequestPath() === $targetPath) {
- return [];
- }
- return [
- $this->urlRewriteFactory->create()
- ->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE)
- ->setEntityId($productId)
- ->setRequestPath($url->getRequestPath())
- ->setTargetPath($targetPath)
- ->setRedirectType(OptionProvider::PERMANENT)
- ->setStoreId($storeId)
- ->setDescription($url->getDescription())
- ->setIsAutogenerated(0)
- ->setMetadata($url->getMetadata())
- ];
- }
- return [];
- }
- /**
- * Generate url-rewrite for custom url-rewirte.
- *
- * @param UrlRewrite $url
- * @param Category $category
- * @return array
- */
- protected function generateForCustom($url, $category)
- {
- $storeId = $url->getStoreId();
- $productId = $url->getEntityId();
- if (isset($this->products[$productId][$storeId])) {
- $product = $this->products[$productId][$storeId];
- $targetPath = $url->getRedirectType()
- ? $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId, $category)
- : $url->getTargetPath();
- if ($url->getRequestPath() === $targetPath) {
- return [];
- }
- return [
- $this->urlRewriteFactory->create()
- ->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE)
- ->setEntityId($productId)
- ->setRequestPath($url->getRequestPath())
- ->setTargetPath($targetPath)
- ->setRedirectType($url->getRedirectType())
- ->setStoreId($storeId)
- ->setDescription($url->getDescription())
- ->setIsAutogenerated(0)
- ->setMetadata($url->getMetadata())
- ];
- }
- return [];
- }
- /**
- * Retrieve category from url metadata.
- *
- * @param UrlRewrite $url
- * @return Category|null|bool
- */
- protected function retrieveCategoryFromMetadata($url)
- {
- $metadata = $url->getMetadata();
- if (isset($metadata['category_id'])) {
- $category = $this->import->getCategoryProcessor()->getCategoryById($metadata['category_id']);
- return $category === null ? false : $category;
- }
- return null;
- }
- /**
- * Check, category suited for url-rewrite generation.
- *
- * @param \Magento\Catalog\Model\Category $category
- * @param int $storeId
- * @return bool
- */
- protected function isCategoryProperForGenerating($category, $storeId)
- {
- if (isset($this->acceptableCategories[$storeId]) &&
- isset($this->acceptableCategories[$storeId][$category->getId()])) {
- return $this->acceptableCategories[$storeId][$category->getId()];
- }
- $acceptable = false;
- if ($category->getParentId() != \Magento\Catalog\Model\Category::TREE_ROOT_ID) {
- list(, $rootCategoryId) = $category->getParentIds();
- $acceptable = ($rootCategoryId == $this->storeManager->getStore($storeId)->getRootCategoryId());
- }
- if (!isset($this->acceptableCategories[$storeId])) {
- $this->acceptableCategories[$storeId] = [];
- }
- $this->acceptableCategories[$storeId][$category->getId()] = $acceptable;
- return $acceptable;
- }
- /**
- * Get category by id considering store scope.
- *
- * @param int $categoryId
- * @param int $storeId
- * @return Category|\Magento\Framework\DataObject
- */
- private function getCategoryById($categoryId, $storeId)
- {
- if (!isset($this->categoriesCache[$categoryId][$storeId])) {
- /** @var CategoryCollection $categoryCollection */
- $categoryCollection = $this->categoryCollectionFactory->create();
- $categoryCollection->addIdFilter([$categoryId])
- ->setStoreId($storeId)
- ->addAttributeToSelect('name')
- ->addAttributeToSelect('url_key')
- ->addAttributeToSelect('url_path');
- $this->categoriesCache[$categoryId][$storeId] = $categoryCollection->getFirstItem();
- }
- return $this->categoriesCache[$categoryId][$storeId];
- }
- }
|