From e27fb807fff2d5245d7173e242fd47be6d1c0020 Mon Sep 17 00:00:00 2001 From: DavidLambauer Date: Tue, 3 Sep 2019 12:24:36 +0200 Subject: [PATCH 01/74] Added dependencies --- Iazel/RegenProductUrl/etc/module.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/etc/module.xml b/Iazel/RegenProductUrl/etc/module.xml index 7c691f1..8a9c8ff 100644 --- a/Iazel/RegenProductUrl/etc/module.xml +++ b/Iazel/RegenProductUrl/etc/module.xml @@ -1,4 +1,13 @@ - + + + + + + + + + + From c90636df2dcad6b197d2caf039ea90d3eafb472a Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Fri, 6 Sep 2019 14:22:28 +0200 Subject: [PATCH 02/74] Extract regenerate product url code into service class --- .../Command/RegenerateProductUrlCommand.php | 106 ++++------------ .../Service/RegenerateProductUrl.php | 116 ++++++++++++++++++ 2 files changed, 138 insertions(+), 84 deletions(-) create mode 100644 Iazel/RegenProductUrl/Service/RegenerateProductUrl.php diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php index a340c3a..f9246e2 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php @@ -1,68 +1,47 @@ state = $state; - $this->collection = $collection; - $this->productUrlRewriteGenerator = $productUrlRewriteGenerator; - $this->urlPersist = $urlPersist; - parent::__construct(); $this->storeManager = $storeManager; + $this->regenerateProductUrl = $regenerateProductUrl; + parent::__construct(); } protected function configure() @@ -78,7 +57,8 @@ protected function configure() ->addArgument( 'pids', InputArgument::IS_ARRAY, - 'Product IDs to regenerate' + 'Product IDs to regenerate', + [] ); } @@ -97,53 +77,11 @@ public function execute(InputInterface $input, OutputInterface $output) $storeId = $this->getStoreIdByCode($storeId, $stores); } - if (!is_numeric($storeId)) { - throw new \Exception('Store could not be found. Please enter a store ID or a store code.'); - } else { - $this->storeManager->getStore($storeId); - } - - foreach ($stores as $store) { - // If store has been given through option, skip other stores - if ($storeId != Store::DEFAULT_STORE_ID AND $store->getId() != $storeId) { - continue; - } - - $this->collection - ->addStoreFilter($store->getId()) - ->setStoreId($store->getId()) - ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); - - $pids = $input->getArgument('pids'); - if (!empty($pids)) { - $this->collection->addIdFilter($pids); - } - - $this->collection->addAttributeToSelect(['url_path', 'url_key']); - $list = $this->collection->load(); - $regenerated = 0; - - /** @var \Magento\Catalog\Model\Product $product */ - foreach ($list as $product) { - echo 'Regenerating urls for ' . $product->getSku() . ' (' . $product->getId() . ') in store ' . $store->getName() . PHP_EOL; - $product->setStoreId($store->getId()); - - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $store->getId() - ]); - - $newUrls = $this->productUrlRewriteGenerator->generate($product); - try { - $this->urlPersist->replace($newUrls); - $regenerated += count($newUrls); - } catch (\Exception $e) { - $output->writeln(sprintf('Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, $store->getId(), $product->getId(), $product->getSku(), $e->getMessage(), implode(PHP_EOL, array_keys($newUrls)))); - } - } - $output->writeln('Done regenerating. Regenerated ' . $regenerated . ' urls for store ' . $store->getName()); + try { + $this->regenerateProductUrl->setOutput($output); + $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId); + } catch (NoSuchEntityException $e) { + $output->writeln(sprintf('%s', $e->getMessage())); } } diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php new file mode 100644 index 0000000..0d3b9da --- /dev/null +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -0,0 +1,116 @@ +collection = $collection; + $this->urlRewriteGenerator = $urlRewriteGenerator; + $this->urlPersist = $urlPersist; + $this->storeManager = $storeManager; + } + + /** + * @param int[] $productIds + * @param int $storeId + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function execute(array $productIds, int $storeId) + { + $this->storeManager->getStore($storeId); + + $stores = $this->storeManager->getStores(false); + foreach ($stores as $store) { + // If store has been given through option, skip other stores + if ($storeId !== Store::DEFAULT_STORE_ID AND (int) $store->getId() !== $storeId) { + continue; + } + + $this->collection + ->addStoreFilter($store->getId()) + ->setStoreId($store->getId()) + ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); + + if (!empty($productIds)) { + $this->collection->addIdFilter($productIds); + } + + $this->collection->addAttributeToSelect(['url_path', 'url_key']); + $list = $this->collection->load(); + $regenerated = 0; + + /** @var \Magento\Catalog\Model\Product $product */ + foreach ($list as $product) { + $this->log('Regenerating urls for ' . $product->getSku() . ' (' . $product->getId() . ') in store ' . $store->getName()); + $product->setStoreId($store->getId()); + + $this->urlPersist->deleteByData([ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::REDIRECT_TYPE => 0, + UrlRewrite::STORE_ID => $store->getId() + ]); + + $newUrls = $this->urlRewriteGenerator->generate($product); + try { + $this->urlPersist->replace($newUrls); + $regenerated += count($newUrls); + } catch (\Exception $e) { + $this->log(sprintf('Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, $store->getId(), $product->getId(), $product->getSku(), $e->getMessage(), implode(PHP_EOL, array_keys($newUrls)))); + } + } + $this->log('Done regenerating. Regenerated ' . $regenerated . ' urls for store ' . $store->getName()); + } + } + + public function setOutput(OutputInterface $output) + { + $this->output = $output; + } + + private function log(string $message) + { + if ($this->output !== null) { + $this->output->writeln($message); + } + } +} From c8b554efa6a5678b840e3d3bc0cd5e734772b84c Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Fri, 6 Sep 2019 15:19:04 +0200 Subject: [PATCH 03/74] Add adminhtml mass action to regenerate product urls --- .../Controller/Adminhtml/Action/Product.php | 87 +++++++++++++++++++ .../Service/RegenerateProductUrl.php | 17 +++- .../RegenProductUrl/etc/adminhtml/routes.xml | 9 ++ .../ui_component/product_listing.xml | 16 ++++ 4 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php create mode 100644 Iazel/RegenProductUrl/etc/adminhtml/routes.xml create mode 100644 Iazel/RegenProductUrl/view/adminhtml/ui_component/product_listing.xml diff --git a/Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php b/Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php new file mode 100644 index 0000000..2b02837 --- /dev/null +++ b/Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php @@ -0,0 +1,87 @@ +collectionFactory = $collectionFactory; + $this->filter = $filter; + $this->regenerateProductUrl = $regenerateProductUrl; + parent::__construct($context); + } + + /** + * Execute action based on request and return result + * + * Note: Request will be added as operation argument in future + * + * @return ResultInterface|ResponseInterface + * @throws LocalizedException + */ + public function execute() + { + $productIds = $this->getSelectedProductIds(); + $storeId = (int) $this->getRequest()->getParam('store', 0); + $filters = $this->getRequest()->getParam('filters', []); + + if (isset($filters['store_id'])) { + $storeId = (int) $filters['store_id']; + } + + try { + $this->regenerateProductUrl->execute($productIds, $storeId); + $this->messageManager->addSuccessMessage(__( + 'Successfully regenerated %1 urls for store id %2.', + $this->regenerateProductUrl->getRegeneratedCount(), + $storeId + )); + } catch (Exception $e) { + $this->messageManager->addExceptionMessage($e, __('Something went wrong while regenerating the product(s) url.')); + } + + $resultRedirect = $this->resultRedirectFactory->create(); + return $resultRedirect->setPath('catalog/product/index'); + } + + /** + * @return array + * @throws LocalizedException + */ + private function getSelectedProductIds(): array + { + return $this->filter->getCollection( + $this->collectionFactory->create() + )->getAllIds(); + } +} diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 0d3b9da..7cf7d78 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -35,6 +35,11 @@ class RegenerateProductUrl * @var StoreManagerInterface\Proxy */ private $storeManager; + /** + * Counter for amount of urls regenerated. + * @var int + */ + private $regeneratedCount = 0; public function __construct( Collection\Proxy $collection, @@ -58,6 +63,8 @@ public function execute(array $productIds, int $storeId) { $this->storeManager->getStore($storeId); + $this->regeneratedCount = 0; + $stores = $this->storeManager->getStores(false); foreach ($stores as $store) { // If store has been given through option, skip other stores @@ -76,7 +83,6 @@ public function execute(array $productIds, int $storeId) $this->collection->addAttributeToSelect(['url_path', 'url_key']); $list = $this->collection->load(); - $regenerated = 0; /** @var \Magento\Catalog\Model\Product $product */ foreach ($list as $product) { @@ -93,12 +99,12 @@ public function execute(array $productIds, int $storeId) $newUrls = $this->urlRewriteGenerator->generate($product); try { $this->urlPersist->replace($newUrls); - $regenerated += count($newUrls); + $this->regeneratedCount += count($newUrls); } catch (\Exception $e) { $this->log(sprintf('Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, $store->getId(), $product->getId(), $product->getSku(), $e->getMessage(), implode(PHP_EOL, array_keys($newUrls)))); } } - $this->log('Done regenerating. Regenerated ' . $regenerated . ' urls for store ' . $store->getName()); + $this->log('Done regenerating. Regenerated ' . $this->regeneratedCount . ' urls for store ' . $store->getName()); } } @@ -107,6 +113,11 @@ public function setOutput(OutputInterface $output) $this->output = $output; } + public function getRegeneratedCount(): int + { + return $this->regeneratedCount; + } + private function log(string $message) { if ($this->output !== null) { diff --git a/Iazel/RegenProductUrl/etc/adminhtml/routes.xml b/Iazel/RegenProductUrl/etc/adminhtml/routes.xml new file mode 100644 index 0000000..5411e22 --- /dev/null +++ b/Iazel/RegenProductUrl/etc/adminhtml/routes.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/Iazel/RegenProductUrl/view/adminhtml/ui_component/product_listing.xml b/Iazel/RegenProductUrl/view/adminhtml/ui_component/product_listing.xml new file mode 100644 index 0000000..4af0099 --- /dev/null +++ b/Iazel/RegenProductUrl/view/adminhtml/ui_component/product_listing.xml @@ -0,0 +1,16 @@ + ++ + + + + regenerate_url + + + + + + + From b181ca3b68c1e3b7ccc181cd312c2708a4f8c257 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Fri, 6 Sep 2019 15:19:13 +0200 Subject: [PATCH 04/74] Add translations dictionary --- Iazel/RegenProductUrl/i18n/en_US.csv | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Iazel/RegenProductUrl/i18n/en_US.csv diff --git a/Iazel/RegenProductUrl/i18n/en_US.csv b/Iazel/RegenProductUrl/i18n/en_US.csv new file mode 100644 index 0000000..554295d --- /dev/null +++ b/Iazel/RegenProductUrl/i18n/en_US.csv @@ -0,0 +1,3 @@ +"Successfully regenerated %1 urls for store id %2.","Successfully regenerated %1 urls for store id %2." +"Something went wrong while regenerating the product(s) url.","Something went wrong while regenerating the product(s) url." +"Regenerate Url","Regenerate Url" From d03ccde2343e769de2c3e732fb6e7aa4f2bb63a8 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Fri, 6 Sep 2019 15:19:26 +0200 Subject: [PATCH 05/74] Remove Magento copyright headers --- Iazel/RegenProductUrl/etc/di.xml | 6 ------ Iazel/RegenProductUrl/etc/events.xml | 6 ------ 2 files changed, 12 deletions(-) diff --git a/Iazel/RegenProductUrl/etc/di.xml b/Iazel/RegenProductUrl/etc/di.xml index 81c45e9..722bc5e 100644 --- a/Iazel/RegenProductUrl/etc/di.xml +++ b/Iazel/RegenProductUrl/etc/di.xml @@ -1,10 +1,4 @@ - diff --git a/Iazel/RegenProductUrl/etc/events.xml b/Iazel/RegenProductUrl/etc/events.xml index 284933e..36f54de 100644 --- a/Iazel/RegenProductUrl/etc/events.xml +++ b/Iazel/RegenProductUrl/etc/events.xml @@ -1,10 +1,4 @@ - From 8214faf6853ac22a246d76a674e89889ef273f28 Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Fri, 6 Sep 2019 15:23:45 +0200 Subject: [PATCH 06/74] Change counting of regenerated urls --- Iazel/RegenProductUrl/Service/RegenerateProductUrl.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 7cf7d78..95fd44f 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -62,11 +62,11 @@ public function __construct( public function execute(array $productIds, int $storeId) { $this->storeManager->getStore($storeId); - $this->regeneratedCount = 0; $stores = $this->storeManager->getStores(false); foreach ($stores as $store) { + $regeneratedForStore = 0; // If store has been given through option, skip other stores if ($storeId !== Store::DEFAULT_STORE_ID AND (int) $store->getId() !== $storeId) { continue; @@ -99,12 +99,13 @@ public function execute(array $productIds, int $storeId) $newUrls = $this->urlRewriteGenerator->generate($product); try { $this->urlPersist->replace($newUrls); - $this->regeneratedCount += count($newUrls); + $regeneratedForStore += count($newUrls); } catch (\Exception $e) { $this->log(sprintf('Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, $store->getId(), $product->getId(), $product->getSku(), $e->getMessage(), implode(PHP_EOL, array_keys($newUrls)))); } } - $this->log('Done regenerating. Regenerated ' . $this->regeneratedCount . ' urls for store ' . $store->getName()); + $this->log('Done regenerating. Regenerated ' . $regeneratedForStore . ' urls for store ' . $store->getName()); + $this->regeneratedCount += $regeneratedForStore; } } From 9b63163439971ace5541b24492582f4cbcbaa54d Mon Sep 17 00:00:00 2001 From: Timon de Groot Date: Fri, 6 Sep 2019 15:30:36 +0200 Subject: [PATCH 07/74] Remove PHP versions 5.6 and 7.0 from composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4985f1c..a9c2494 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "elgentos/regenerate-catalog-urls", "description": "N/A", "require": { - "php": "~5.6.0|~7.0|~7.1|~7.2", + "php": "~7.1|~7.2", "magento/framework": ">=100.1", "magento/module-catalog-url-rewrite": ">=100.1", "magento/magento-composer-installer": "*" From 5a91970cfcfdf8bd4fb4b44652b9ff3151d5c8e9 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sat, 12 Oct 2019 18:08:42 +0000 Subject: [PATCH 08/74] Apply fixes from StyleCI --- .../Command/RegenerateCategoryPathCommand.php | 13 ++++++------- .../Command/RegenerateCategoryUrlCommand.php | 14 ++++++-------- .../Command/RegenerateCmsPageUrlCommand.php | 3 +-- .../Model/CategoryUrlPathGenerator.php | 2 +- .../Service/RegenerateProductUrl.php | 2 +- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php index c4c5d4d..c7de176 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php @@ -107,9 +107,9 @@ protected function configure() public function execute(InputInterface $inp, OutputInterface $out) { - try{ + try { $this->state->getAreaCode(); - }catch ( \Magento\Framework\Exception\LocalizedException $e){ + } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } @@ -120,17 +120,16 @@ public function execute(InputInterface $inp, OutputInterface $out) ->addAttributeToSelect(['name', 'url_path', 'url_key']); $cids = $inp->getArgument('cids'); - if( !empty($cids) ) { + if (!empty($cids)) { $categories->addAttributeToFilter('entity_id', ['in' => $cids]); } $regenerated = 0; - foreach($categories as $category) - { + foreach ($categories as $category) { $out->writeln('Regenerating urls for ' . $category->getName() . ' (' . $category->getId() . ')'); - $category->setOrigData('url_key', mt_rand(1,1000)); // set url_key in orig data to random value to force regeneration of path - $category->setOrigData('url_path', mt_rand(1,1000)); // set url_path in orig data to random value to force regeneration of path for children + $category->setOrigData('url_key', mt_rand(1, 1000)); // set url_key in orig data to random value to force regeneration of path + $category->setOrigData('url_path', mt_rand(1, 1000)); // set url_path in orig data to random value to force regeneration of path for children // Make use of Magento's event for this $this->emulation->startEnvironmentEmulation($store_id, Area::AREA_FRONTEND, true); diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php index 72dd260..fd2f52c 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php @@ -96,9 +96,9 @@ protected function configure() public function execute(InputInterface $inp, OutputInterface $out) { - try{ + try { $this->state->getAreaCode(); - }catch ( \Magento\Framework\Exception\LocalizedException $e){ + } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } @@ -110,13 +110,12 @@ public function execute(InputInterface $inp, OutputInterface $out) ->addAttributeToSelect(['name', 'url_path', 'url_key']); $cids = $inp->getArgument('cids'); - if( !empty($cids) ) { + if (!empty($cids)) { $categories->addAttributeToFilter('entity_id', ['in' => $cids]); } $regenerated = 0; - foreach($categories as $category) - { + foreach ($categories as $category) { $out->writeln('Regenerating urls for ' . $category->getName() . ' (' . $category->getId() . ')'); $this->urlPersist->deleteByData([ @@ -131,8 +130,7 @@ public function execute(InputInterface $inp, OutputInterface $out) $newUrls = $this->filterEmptyRequestPaths($newUrls); $this->urlPersist->replace($newUrls); $regenerated += count($newUrls); - } - catch(\Exception $e) { + } catch (\Exception $e) { $out->writeln(sprintf('Duplicated url for store ID %d, category %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, $store_id, $category->getId(), $category->getName(), $e->getMessage(), implode(PHP_EOL, array_keys($newUrls)))); } } @@ -156,5 +154,5 @@ private function filterEmptyRequestPaths($newUrls) } } return $result; - } + } } diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php index 67806e0..f3dfae8 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php @@ -3,7 +3,6 @@ namespace Iazel\RegenProductUrl\Console\Command; - use Magento\Cms\Model\Page; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; use Magento\CmsUrlRewrite\Model\CmsPageUrlRewriteGenerator; @@ -144,4 +143,4 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln(sprintf('Finished regenerating. Regenerated %d urls.', $regenerated)); return Cli::RETURN_SUCCESS; } -} \ No newline at end of file +} diff --git a/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php b/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php index 93a0899..a5b7563 100644 --- a/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php +++ b/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php @@ -18,4 +18,4 @@ protected function isNeedToGenerateUrlPathForParent($category) return $category->isObjectNew() || $category->getLevel() >= self::MINIMAL_CATEGORY_LEVEL_FOR_PROCESSING; } -} \ No newline at end of file +} diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 95fd44f..445298d 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -68,7 +68,7 @@ public function execute(array $productIds, int $storeId) foreach ($stores as $store) { $regeneratedForStore = 0; // If store has been given through option, skip other stores - if ($storeId !== Store::DEFAULT_STORE_ID AND (int) $store->getId() !== $storeId) { + if ($storeId !== Store::DEFAULT_STORE_ID and (int) $store->getId() !== $storeId) { continue; } From 6a31dc88c8eba00bca1bdea7f0310283d02d792c Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Mon, 21 Oct 2019 14:31:17 +0200 Subject: [PATCH 09/74] Add filter to skip root categories in path generation Fixes issue #20 --- .../Console/Command/RegenerateCategoryPathCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php index c7de176..1b91f27 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php @@ -117,7 +117,8 @@ public function execute(InputInterface $inp, OutputInterface $out) $categories = $this->categoryCollectionFactory->create() ->setStore($store_id) - ->addAttributeToSelect(['name', 'url_path', 'url_key']); + ->addAttributeToSelect(['name', 'url_path', 'url_key']) + ->addAttributeToFilter('level', ['gt' => 1]); $cids = $inp->getArgument('cids'); if (!empty($cids)) { From fe2e4a9f77f66c9cc0397338deeacca5aac9991c Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Wed, 11 Dec 2019 10:28:34 +0100 Subject: [PATCH 10/74] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a9c2494..4ad5da5 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "elgentos/regenerate-catalog-urls", "description": "N/A", "require": { - "php": "~7.1|~7.2", + "php": "~7.1|~7.2|~7.3", "magento/framework": ">=100.1", "magento/module-catalog-url-rewrite": ">=100.1", "magento/magento-composer-installer": "*" From ee520499ced6b73b51924c48304e49f71c32158d Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Tue, 17 Dec 2019 14:49:03 +0100 Subject: [PATCH 11/74] Added level filter on regenerate:category:url as well (next to path) --- .../Console/Command/RegenerateCategoryUrlCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php index fd2f52c..6852c0e 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php @@ -107,7 +107,8 @@ public function execute(InputInterface $inp, OutputInterface $out) $categories = $this->categoryCollectionFactory->create() ->setStore($store_id) - ->addAttributeToSelect(['name', 'url_path', 'url_key']); + ->addAttributeToSelect(['name', 'url_path', 'url_key']) + ->addAttributeToFilter('level', ['gt' => 1]); $cids = $inp->getArgument('cids'); if (!empty($cids)) { From 4f74474cff55b9b632831ef392b5202da40782c9 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Thu, 2 Apr 2020 16:38:32 +0200 Subject: [PATCH 12/74] Remove module:enable command from readme module:enable is not needed when running setup:upgrade --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c0769f2..984fb2f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Using Composer; ```sh composer require elgentos/regenerate-catalog-urls -php bin/magento module:enable Iazel_RegenProductUrl php bin/magento setup:upgrade ``` From 647333b1fdf06050630e0c695af05878d92b1196 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Wed, 8 Apr 2020 09:45:09 +0200 Subject: [PATCH 13/74] Fix #27 --- .../Service/RegenerateProductUrl.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 445298d..feb0bef 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -5,7 +5,7 @@ namespace Iazel\RegenProductUrl\Service; use Magento\Catalog\Model\Product\Visibility; -use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; @@ -20,9 +20,9 @@ class RegenerateProductUrl */ private $output; /** - * @var Collection\Proxy + * @var CollectionFactory */ - private $collection; + private $collectionFactory; /** * @var ProductUrlRewriteGenerator\Proxy */ @@ -42,12 +42,12 @@ class RegenerateProductUrl private $regeneratedCount = 0; public function __construct( - Collection\Proxy $collection, + CollectionFactory $collectionFactory, ProductUrlRewriteGenerator\Proxy $urlRewriteGenerator, UrlPersistInterface\Proxy $urlPersist, StoreManagerInterface\Proxy $storeManager ) { - $this->collection = $collection; + $this->collectionFactory = $collectionFactory; $this->urlRewriteGenerator = $urlRewriteGenerator; $this->urlPersist = $urlPersist; $this->storeManager = $storeManager; @@ -61,7 +61,6 @@ public function __construct( */ public function execute(array $productIds, int $storeId) { - $this->storeManager->getStore($storeId); $this->regeneratedCount = 0; $stores = $this->storeManager->getStores(false); @@ -72,9 +71,12 @@ public function execute(array $productIds, int $storeId) continue; } + $this->collection = $this->collectionFactory->create(); $this->collection - ->addStoreFilter($store->getId()) ->setStoreId($store->getId()) + ->addStoreFilter($store->getId()) + ->addAttributeToSelect('name') + ->addFieldToFilter('status', ['eq' => \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED]) ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); if (!empty($productIds)) { From df309463cf258f88d332caf265f101697b46da2b Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Wed, 8 Apr 2020 10:09:05 +0200 Subject: [PATCH 14/74] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 984fb2f..3dab74f 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,11 @@ php bin/magento regenerate:product:url # Regenerate url for products with id (1, 2, 3, 4) for store 1 php bin/magento regenerate:product:url -s1 1 2 3 4 ``` + +# FAQ + +## What's the difference between url_key and url_path? +`url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path')`. + +## Why am I getting a 'Duplicated url' warning when running the command? +If you see this error, you have duplicate `url_key` values (within a store) in `catalog_product_entity_varchar`. You can use this extension to check those (and you need to fix them manually): [baldwin/magento2-module-url-data-integrity-checker](https://github.com/baldwin-agency/magento2-module-url-data-integrity-checker). From 43c607c2da42016504b6f7c56817c8747e31c787 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Wed, 22 Apr 2020 05:52:10 +0200 Subject: [PATCH 15/74] Added url_path section in faq --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3dab74f..7842d0b 100644 --- a/README.md +++ b/README.md @@ -45,5 +45,8 @@ php bin/magento regenerate:product:url -s1 1 2 3 4 ## What's the difference between url_key and url_path? `url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path')`. +## Why are my category rewrites for a non-default storeview in the default storeview's language? +This is probably due to `url_path` values in the database. Since these aren't updated anymore, but they are still present, the data may be out of sync. See [these](https://github.com/magento/magento2/issues/16202) [issues](https://github.com/magento/magento2/issues/12718). The easiest fix is to delete the `url_path` values from the database (only if you are running >2.1). Here's a quick query; `DELETE FROM catalog_category_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = 3);`. + ## Why am I getting a 'Duplicated url' warning when running the command? If you see this error, you have duplicate `url_key` values (within a store) in `catalog_product_entity_varchar`. You can use this extension to check those (and you need to fix them manually): [baldwin/magento2-module-url-data-integrity-checker](https://github.com/baldwin-agency/magento2-module-url-data-integrity-checker). From 14bb8cefda1dd5054432e4da1f266f23d5565847 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Wed, 22 Apr 2020 05:53:54 +0200 Subject: [PATCH 16/74] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7842d0b..bd71d1d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ php bin/magento regenerate:product:url -s1 1 2 3 4 # FAQ ## What's the difference between url_key and url_path? -`url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path')`. +`url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. It also adds the category slugs of the parent categories so the `url_path` might become `bags/joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = 4)`. ## Why are my category rewrites for a non-default storeview in the default storeview's language? This is probably due to `url_path` values in the database. Since these aren't updated anymore, but they are still present, the data may be out of sync. See [these](https://github.com/magento/magento2/issues/16202) [issues](https://github.com/magento/magento2/issues/12718). The easiest fix is to delete the `url_path` values from the database (only if you are running >2.1). Here's a quick query; `DELETE FROM catalog_category_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = 3);`. From 252906b0b0a3723de3b0fdb49a56dd72ba0dba79 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Tue, 29 Sep 2020 08:23:00 +0200 Subject: [PATCH 17/74] Updated CMS command to also use proxies See https://github.com/elgentos/regenerate-catalog-urls/issues/18 --- .../Command/RegenerateCmsPageUrlCommand.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php index f3dfae8..ca73b67 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php @@ -32,17 +32,17 @@ class RegenerateCmsPageUrlCommand extends Command private $emulation; /** - * @var PageCollectionFactory + * @var PageCollectionFactory\Proxy */ private $pageCollectionFactory; /** - * @var UrlPersistInterface + * @var UrlPersistInterface\Proxy */ private $urlPersist; /** - * @var CmsPageUrlRewriteGenerator + * @var CmsPageUrlRewriteGenerator\Proxy */ private $cmsPageUrlRewriteGenerator; @@ -50,17 +50,17 @@ class RegenerateCmsPageUrlCommand extends Command * RegenerateCmsPageUrlCommand constructor. * * @param State $state - * @param Emulation $emulation - * @param PageCollectionFactory $pageCollectionFactory - * @param UrlPersistInterface $urlPersist - * @param CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator + * @param Emulation\Proxy $emulation + * @param PageCollectionFactory\Proxy $pageCollectionFactory + * @param UrlPersistInterface\Proxy $urlPersist + * @param CmsPageUrlRewriteGenerator\Proxy $cmsPageUrlRewriteGenerator */ public function __construct( State $state, - Emulation $emulation, - PageCollectionFactory $pageCollectionFactory, - UrlPersistInterface $urlPersist, - CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator + Emulation\Proxy $emulation, + PageCollectionFactory\Proxy $pageCollectionFactory, + UrlPersistInterface\Proxy $urlPersist, + CmsPageUrlRewriteGenerator\Proxy $cmsPageUrlRewriteGenerator ) { parent::__construct(); $this->state = $state; From 54b832d661f5bb9e266a9cc873ba68cefaec3c4f Mon Sep 17 00:00:00 2001 From: Arjen Miedema Date: Mon, 28 Jun 2021 09:17:18 +0200 Subject: [PATCH 18/74] Implement testing suite To ensure and increase the coding standards of the Magento extension, coding standards have been applied. --- .eslintignore | 1 + .eslintrc.json | 57 +++++++ .../Command/RegenerateCategoryPathCommand.php | 122 ++++++++------- .../Command/RegenerateCategoryUrlCommand.php | 142 +++++++++++------- .../Command/RegenerateCmsPageUrlCommand.php | 82 ++++++---- .../Command/RegenerateProductUrlCommand.php | 59 +++++--- .../Controller/Adminhtml/Action/Product.php | 42 ++++-- .../Model/CategoryUrlPathGenerator.php | 11 +- .../Service/RegenerateProductUrl.php | 120 +++++++++++---- Iazel/RegenProductUrl/etc/di.xml | 3 +- Iazel/RegenProductUrl/etc/events.xml | 3 +- Iazel/RegenProductUrl/registration.php | 7 +- composer.json | 72 ++++++--- grumphp.yml | 2 + package.json | 5 + pdepend.xml | 11 ++ phpcs.xml | 10 ++ phpmd.xml | 10 ++ phpstan.neon | 9 ++ phpunit.xml | 15 ++ 20 files changed, 551 insertions(+), 232 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 grumphp.yml create mode 100644 package.json create mode 100644 pdepend.xml create mode 100644 phpcs.xml create mode 100644 phpmd.xml create mode 100644 phpstan.neon create mode 100644 phpunit.xml diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..0baf809 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +*.min.js \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..174dd28 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,57 @@ +{ + "env": { + "amd": true, + "browser": true, + "jasmine": true + }, + "rules": { + "consistent-return": 2, + "eqeqeq": [2, "smart"], + "guard-for-in": 2, + "lines-around-comment": [ + 2, + { + "beforeBlockComment": true + } + ], + "max-len": [2, 120, 4], + "max-nested-callbacks": [2, 3], + "no-alert": 2, + "no-array-constructor": 2, + "no-caller": 2, + "no-catch-shadow": 2, + "no-else-return": 2, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-lone-blocks": 2, + "no-lonely-if": 2, + "no-loop-func": 2, + "no-multi-str": 2, + "no-new-object": 2, + "no-proto": 2, + "no-return-assign": 2, + "no-self-compare": 2, + "no-shadow": 2, + "no-undef-init": 2, + "no-unused-vars": [ + 2, + { + "args": "after-used", + "vars": "all", + "varsIgnorePattern": "^config$" + } + ], + "no-with": 2, + "operator-assignment": [2, "always"], + "radix": 2, + "semi": [2, "always"], + "semi-spacing": 2, + "space-before-blocks": "error", + "space-before-function-paren":"error", + "func-style": [2, "expression"], + "eol-last":"error" + } +} \ No newline at end of file diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php index 1b91f27..3e0db03 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php @@ -1,18 +1,20 @@ state = $state; - $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; - $this->urlPersist = $urlPersist; - parent::__construct(); + $this->state = $state; + $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; + $this->urlPersist = $urlPersist; $this->categoryCollectionFactory = $categoryCollectionFactory; - $this->eventManager = $eventManager; - $this->categoryResource = $categoryResource; - $this->emulation = $emulation; + $this->eventManager = $eventManager; + $this->emulation = $emulation; + + parent::__construct(); } - protected function configure() + /** + * @return void + */ + protected function configure(): void { $this->setName('regenerate:category:path') ->setDescription('Regenerate path for given categories') @@ -96,50 +97,67 @@ protected function configure() 'Categories to regenerate' ) ->addOption( - 'store', 's', + 'store', + 's', InputOption::VALUE_REQUIRED, 'Use the specific Store View', Store::DEFAULT_STORE_ID - ) - ; - return parent::configure(); + ); + + parent::configure(); } - public function execute(InputInterface $inp, OutputInterface $out) + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void|int + * @throws LocalizedException + */ + public function execute(InputInterface $input, OutputInterface $output) { try { $this->state->getAreaCode(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } - $store_id = $inp->getOption('store'); + $store_id = $input->getOption('store'); $categories = $this->categoryCollectionFactory->create() ->setStore($store_id) ->addAttributeToSelect(['name', 'url_path', 'url_key']) ->addAttributeToFilter('level', ['gt' => 1]); - $cids = $inp->getArgument('cids'); - if (!empty($cids)) { - $categories->addAttributeToFilter('entity_id', ['in' => $cids]); + $categoryIds = $input->getArgument('categoryIds'); + + if (!empty($categoryIds)) { + $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); } - $regenerated = 0; + $counter = 0; + foreach ($categories as $category) { - $out->writeln('Regenerating urls for ' . $category->getName() . ' (' . $category->getId() . ')'); + $output->writeln( + sprintf('Regenerating urls for %s (%s)', $category->getName(), $category->getId()) + ); + + // set url_key in orig data to random value to force regeneration of path + $category->setOrigData('url_key', mt_rand(1, 1000)); - $category->setOrigData('url_key', mt_rand(1, 1000)); // set url_key in orig data to random value to force regeneration of path - $category->setOrigData('url_path', mt_rand(1, 1000)); // set url_path in orig data to random value to force regeneration of path for children + // set url_path in orig data to random value to force regeneration of path for children + $category->setOrigData('url_path', mt_rand(1, 1000)); // Make use of Magento's event for this $this->emulation->startEnvironmentEmulation($store_id, Area::AREA_FRONTEND, true); $this->eventManager->dispatch('regenerate_category_url_path', ['category' => $category]); $this->emulation->stopEnvironmentEmulation(); - $regenerated++; + $counter++; } - $out->writeln('Done regenerating. Regenerated url paths for ' . $regenerated . ' categories'); + $output->writeln( + sprintf('Done regenerating. Regenerated url paths for %d categories', $counter) + ); } } diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php index 6852c0e..20c68bd 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php @@ -1,15 +1,19 @@ state = $state; - $this->collection = $collection; + $this->state = $state; $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; - $this->urlPersist = $urlPersist; - $this->categoryCollectionFactory = $categoryCollectionFactory; + $this->urlPersist = $urlPersist; + $this->categoryCollectionFactory = $categoryCollectionFactory; + $this->emulation = $emulation; parent::__construct(); - $this->emulation = $emulation; } + /** + * @return void + */ protected function configure() { $this->setName('regenerate:category:url') @@ -85,24 +85,32 @@ protected function configure() 'Categories to regenerate' ) ->addOption( - 'store', 's', + 'store', + 's', InputOption::VALUE_REQUIRED, 'Use the specific Store View', Store::DEFAULT_STORE_ID - ) - ; - return parent::configure(); + ); + + parent::configure(); } - public function execute(InputInterface $inp, OutputInterface $out) + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void|int + * @throws LocalizedException + */ + public function execute(InputInterface $input, OutputInterface $output) { try { $this->state->getAreaCode(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } - $store_id = $inp->getOption('store'); + $store_id = $input->getOption('store'); $this->emulation->startEnvironmentEmulation($store_id, Area::AREA_FRONTEND, true); $categories = $this->categoryCollectionFactory->create() @@ -110,50 +118,74 @@ public function execute(InputInterface $inp, OutputInterface $out) ->addAttributeToSelect(['name', 'url_path', 'url_key']) ->addAttributeToFilter('level', ['gt' => 1]); - $cids = $inp->getArgument('cids'); - if (!empty($cids)) { - $categories->addAttributeToFilter('entity_id', ['in' => $cids]); + $categoryIds = $input->getArgument('categoryIds'); + + if (!empty($categoryIds)) { + $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); } - $regenerated = 0; + $counter = 0; + foreach ($categories as $category) { - $out->writeln('Regenerating urls for ' . $category->getName() . ' (' . $category->getId() . ')'); + $output->writeln( + sprintf('Regenerating urls for %s (%s)', $category->getName(), $category->getId()) + ); - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $category->getId(), - UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $store_id - ]); + $this->urlPersist->deleteByData( + [ + UrlRewrite::ENTITY_ID => $category->getId(), + UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::REDIRECT_TYPE => 0, + UrlRewrite::STORE_ID => $store_id + ] + ); $newUrls = $this->categoryUrlRewriteGenerator->generate($category); + try { $newUrls = $this->filterEmptyRequestPaths($newUrls); $this->urlPersist->replace($newUrls); - $regenerated += count($newUrls); - } catch (\Exception $e) { - $out->writeln(sprintf('Duplicated url for store ID %d, category %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, $store_id, $category->getId(), $category->getName(), $e->getMessage(), implode(PHP_EOL, array_keys($newUrls)))); + $counter += count($newUrls); + } catch (Exception $e) { + $output->writeln( + sprintf( + 'Duplicated url for store ID %d, category %d (%s) - %s Generated URLs:' . + PHP_EOL . '%s' . PHP_EOL, + $store_id, + $category->getId(), + $category->getName(), + $e->getMessage(), + implode(PHP_EOL, array_keys($newUrls)) + ) + ); } } + $this->emulation->stopEnvironmentEmulation(); - $out->writeln('Done regenerating. Regenerated ' . $regenerated . ' urls'); + $output->writeln( + sprintf('Done regenerating. Regenerated %d urls', $counter) + ); } - + /** * Remove entries with request_path='' to prevent error 404 for "http://site.com/" address. * - * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $newUrls - * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] + * @param UrlRewrite[] $newUrls + * + * @return UrlRewrite[] */ - private function filterEmptyRequestPaths($newUrls) + private function filterEmptyRequestPaths(array $newUrls): array { $result = []; + foreach ($newUrls as $key => $url) { $requestPath = $url->getRequestPath(); + if (!empty($requestPath)) { $result[$key] = $url; } } + return $result; } } diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php index ca73b67..9a3d70d 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php @@ -1,14 +1,15 @@ state = $state; - $this->emulation = $emulation; - $this->pageCollectionFactory = $pageCollectionFactory; - $this->urlPersist = $urlPersist; + + $this->state = $state; + $this->emulation = $emulation; + $this->pageCollectionFactory = $pageCollectionFactory; + $this->urlPersist = $urlPersist; $this->cmsPageUrlRewriteGenerator = $cmsPageUrlRewriteGenerator; } + /** + * @return void + */ protected function configure() { $this->setName('regenerate:cms-page:url') @@ -95,18 +100,19 @@ protected function configure() } /** - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * - * @return int|void|null - * @throws \Magento\Framework\Exception\LocalizedException + * @return int + * @throws LocalizedException */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Start regenerating urls for CMS pages.'); + try { $this->state->getAreaCode(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->state->setAreaCode(Area::AREA_ADMINHTML); } @@ -124,23 +130,39 @@ protected function execute(InputInterface $input, OutputInterface $output) $pages->addFieldToFilter('page_id', ['in' => $pageIds]); } - $regenerated = 0; + $counter = 0; + /** @var Page $page */ foreach ($pages as $page) { $newUrls = $this->cmsPageUrlRewriteGenerator->generate($page); + try { $this->urlPersist->replace($newUrls); - $regenerated += count($newUrls); + $counter += count($newUrls); } catch (UrlAlreadyExistsException $e) { - $output->writeln(sprintf('Url for page %s (%d) already exists.' . PHP_EOL . '%s', - $page->getTitle(), $page->getId(), $e->getMessage())); - } catch (\Exception $e) { - $output->writeln(sprintf('Couldn\'t replace url for %s (%d)' . PHP_EOL . '%s')); + $output->writeln( + sprintf( + 'Url for page %s (%d) already exists.' . PHP_EOL . '%s', + $page->getTitle(), + $page->getId(), + $e->getMessage() + ) + ); + } catch (Exception $e) { + $output->writeln( + 'Couldn\'t replace url for %s (%d)' . PHP_EOL . '%s' + ); } } $this->emulation->stopEnvironmentEmulation(); - $output->writeln(sprintf('Finished regenerating. Regenerated %d urls.', $regenerated)); + $output->writeln( + sprintf( + 'Finished regenerating. Regenerated %d urls.', + $counter + ) + ); + return Cli::RETURN_SUCCESS; } } diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php index f9246e2..cbf68d2 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php @@ -1,9 +1,11 @@ state = $state; - $this->storeManager = $storeManager; + $this->state = $state; + $this->storeManager = $storeManager; $this->regenerateProductUrl = $regenerateProductUrl; parent::__construct(); } - protected function configure() + /** + * @return void + */ + protected function configure(): void { $this->setName('regenerate:product:url') ->setDescription('Regenerate url for given products') ->addOption( - 'store', 's', + 'store', + 's', InputOption::VALUE_REQUIRED, 'Regenerate for one specific store view', Store::DEFAULT_STORE_ID @@ -62,33 +70,42 @@ protected function configure() ); } + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return void|int + * @throws LocalizedException + */ public function execute(InputInterface $input, OutputInterface $output) { try { $this->state->getAreaCode(); - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } $storeId = $input->getOption('store'); - $stores = $this->storeManager->getStores(false); + $stores = $this->storeManager->getStores(false); if (!is_numeric($storeId)) { $storeId = $this->getStoreIdByCode($storeId, $stores); } - try { - $this->regenerateProductUrl->setOutput($output); - $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId); - } catch (NoSuchEntityException $e) { - $output->writeln(sprintf('%s', $e->getMessage())); - } + $this->regenerateProductUrl->setOutput($output); + $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId); } - private function getStoreIdByCode($store_id, $stores) + /** + * @param string $storeId + * @param StoreInterface[] $stores + * + * @return bool|int + */ + private function getStoreIdByCode(string $storeId, array $stores) { foreach ($stores as $store) { - if ($store->getCode() == $store_id) { + if ($store->getCode() == $storeId) { return $store->getId(); } } diff --git a/Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php b/Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php index 2b02837..757f3fc 100644 --- a/Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php +++ b/Iazel/RegenProductUrl/Controller/Adminhtml/Action/Product.php @@ -10,6 +10,7 @@ use Magento\Backend\App\Action\Context; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Ui\Component\MassAction\Filter; @@ -20,24 +21,35 @@ class Product extends Action * @var RegenerateProductUrl */ private $regenerateProductUrl; + /** * @var Filter */ private $filter; + /** * @var CollectionFactory */ private $collectionFactory; + /** + * Constructor. + * + * @param CollectionFactory $collectionFactory + * @param Filter $filter + * @param RegenerateProductUrl $regenerateProductUrl + * @param Context $context + */ public function __construct( CollectionFactory $collectionFactory, Filter $filter, RegenerateProductUrl $regenerateProductUrl, Context $context ) { - $this->collectionFactory = $collectionFactory; - $this->filter = $filter; + $this->collectionFactory = $collectionFactory; + $this->filter = $filter; $this->regenerateProductUrl = $regenerateProductUrl; + parent::__construct($context); } @@ -46,14 +58,14 @@ public function __construct( * * Note: Request will be added as operation argument in future * - * @return ResultInterface|ResponseInterface + * @return Redirect * @throws LocalizedException */ - public function execute() + public function execute(): Redirect { $productIds = $this->getSelectedProductIds(); - $storeId = (int) $this->getRequest()->getParam('store', 0); - $filters = $this->getRequest()->getParam('filters', []); + $storeId = (int) $this->getRequest()->getParam('store', 0); + $filters = $this->getRequest()->getParam('filters', []); if (isset($filters['store_id'])) { $storeId = (int) $filters['store_id']; @@ -61,16 +73,22 @@ public function execute() try { $this->regenerateProductUrl->execute($productIds, $storeId); - $this->messageManager->addSuccessMessage(__( - 'Successfully regenerated %1 urls for store id %2.', - $this->regenerateProductUrl->getRegeneratedCount(), - $storeId - )); + $this->messageManager->addSuccessMessage( + __( + 'Successfully regenerated %1 urls for store id %2.', + $this->regenerateProductUrl->getRegeneratedCount(), + $storeId + ) + ); } catch (Exception $e) { - $this->messageManager->addExceptionMessage($e, __('Something went wrong while regenerating the product(s) url.')); + $this->messageManager->addExceptionMessage( + $e, + __('Something went wrong while regenerating the product(s) url.') + ); } $resultRedirect = $this->resultRedirectFactory->create(); + return $resultRedirect->setPath('catalog/product/index'); } diff --git a/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php b/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php index a5b7563..e920f94 100644 --- a/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php +++ b/Iazel/RegenProductUrl/Model/CategoryUrlPathGenerator.php @@ -2,20 +2,23 @@ namespace Iazel\RegenProductUrl\Model; +use Magento\Catalog\Model\Category; + class CategoryUrlPathGenerator extends \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator { - /** - * @param \Magento\Catalog\Model\Category $category + * @param Category $category + * * @return bool */ - protected function isNeedToGenerateUrlPathForParent($category) + protected function isNeedToGenerateUrlPathForParent($category): bool { /* Force true when command is run from the CLI */ if (PHP_SAPI == 'cli') { return true; } - return $category->isObjectNew() || $category->getLevel() >= self::MINIMAL_CATEGORY_LEVEL_FOR_PROCESSING; + return $category->isObjectNew() || + $category->getLevel() >= self::MINIMAL_CATEGORY_LEVEL_FOR_PROCESSING; } } diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index feb0bef..2fc6f35 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -4,6 +4,9 @@ namespace Iazel\RegenProductUrl\Service; +use Exception; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Visibility; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; @@ -19,109 +22,162 @@ class RegenerateProductUrl * @var OutputInterface|null */ private $output; + /** * @var CollectionFactory */ private $collectionFactory; + /** - * @var ProductUrlRewriteGenerator\Proxy + * @var ProductUrlRewriteGenerator */ private $urlRewriteGenerator; + /** - * @var UrlPersistInterface\Proxy + * @var UrlPersistInterface */ private $urlPersist; + /** - * @var StoreManagerInterface\Proxy + * @var StoreManagerInterface */ private $storeManager; + /** * Counter for amount of urls regenerated. * @var int */ private $regeneratedCount = 0; + /** + * Constructor. + * + * @param CollectionFactory $collectionFactory + * @param ProductUrlRewriteGenerator $urlRewriteGenerator + * @param UrlPersistInterface $urlPersist + * @param StoreManagerInterface $storeManager + */ public function __construct( CollectionFactory $collectionFactory, - ProductUrlRewriteGenerator\Proxy $urlRewriteGenerator, - UrlPersistInterface\Proxy $urlPersist, - StoreManagerInterface\Proxy $storeManager + ProductUrlRewriteGenerator $urlRewriteGenerator, + UrlPersistInterface $urlPersist, + StoreManagerInterface $storeManager ) { - $this->collectionFactory = $collectionFactory; + $this->collectionFactory = $collectionFactory; $this->urlRewriteGenerator = $urlRewriteGenerator; - $this->urlPersist = $urlPersist; - $this->storeManager = $storeManager; + $this->urlPersist = $urlPersist; + $this->storeManager = $storeManager; } /** * @param int[] $productIds - * @param int $storeId + * @param int $storeId + * * @return void - * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function execute(array $productIds, int $storeId) + public function execute(array $productIds, int $storeId): void { $this->regeneratedCount = 0; + $stores = $this->storeManager->getStores(false); - $stores = $this->storeManager->getStores(false); foreach ($stores as $store) { $regeneratedForStore = 0; + // If store has been given through option, skip other stores if ($storeId !== Store::DEFAULT_STORE_ID and (int) $store->getId() !== $storeId) { continue; } - $this->collection = $this->collectionFactory->create(); - $this->collection + $collection = $this->collectionFactory->create(); + $collection ->setStoreId($store->getId()) ->addStoreFilter($store->getId()) ->addAttributeToSelect('name') - ->addFieldToFilter('status', ['eq' => \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED]) + ->addFieldToFilter('status', ['eq' => Status::STATUS_ENABLED]) ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); if (!empty($productIds)) { - $this->collection->addIdFilter($productIds); + $collection->addIdFilter($productIds); } - $this->collection->addAttributeToSelect(['url_path', 'url_key']); - $list = $this->collection->load(); + $collection->addAttributeToSelect(['url_path', 'url_key']); + $list = $collection->load(); - /** @var \Magento\Catalog\Model\Product $product */ + /** @var Product $product */ foreach ($list as $product) { - $this->log('Regenerating urls for ' . $product->getSku() . ' (' . $product->getId() . ') in store ' . $store->getName()); + $this->log( + sprintf( + 'Regenerating urls for %s (%s) in store (%s)', + $product->getSku(), + $product->getId(), + $store->getName() + ) + ); $product->setStoreId($store->getId()); - $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $store->getId() - ]); + $this->urlPersist->deleteByData( + [ + UrlRewrite::ENTITY_ID => $product->getId(), + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::REDIRECT_TYPE => 0, + UrlRewrite::STORE_ID => $store->getId() + ] + ); $newUrls = $this->urlRewriteGenerator->generate($product); try { $this->urlPersist->replace($newUrls); $regeneratedForStore += count($newUrls); - } catch (\Exception $e) { - $this->log(sprintf('Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, $store->getId(), $product->getId(), $product->getSku(), $e->getMessage(), implode(PHP_EOL, array_keys($newUrls)))); + } catch (Exception $e) { + $this->log( + sprintf( + 'Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . + PHP_EOL . '%s' . PHP_EOL, + $store->getId(), + $product->getId(), + $product->getSku(), + $e->getMessage(), + implode(PHP_EOL, array_keys($newUrls)) + ) + ); } } - $this->log('Done regenerating. Regenerated ' . $regeneratedForStore . ' urls for store ' . $store->getName()); + + $this->log( + sprintf( + 'Done regenerating. Regenerated %d urls for store %s', + $regeneratedForStore, + $store->getName() + ) + ); $this->regeneratedCount += $regeneratedForStore; } } - public function setOutput(OutputInterface $output) + /** + * @param OutputInterface $output + * + * @return void + */ + public function setOutput(OutputInterface $output): void { $this->output = $output; } + /** + * @return int + */ public function getRegeneratedCount(): int { return $this->regeneratedCount; } - private function log(string $message) + /** + * @param string $message + * + * @return void + */ + private function log(string $message): void { if ($this->output !== null) { $this->output->writeln($message); diff --git a/Iazel/RegenProductUrl/etc/di.xml b/Iazel/RegenProductUrl/etc/di.xml index 722bc5e..1f9942d 100644 --- a/Iazel/RegenProductUrl/etc/di.xml +++ b/Iazel/RegenProductUrl/etc/di.xml @@ -1,5 +1,6 @@ - + diff --git a/Iazel/RegenProductUrl/etc/events.xml b/Iazel/RegenProductUrl/etc/events.xml index 36f54de..d185c11 100644 --- a/Iazel/RegenProductUrl/etc/events.xml +++ b/Iazel/RegenProductUrl/etc/events.xml @@ -1,6 +1,7 @@ - + diff --git a/Iazel/RegenProductUrl/registration.php b/Iazel/RegenProductUrl/registration.php index d11a223..a2786b4 100644 --- a/Iazel/RegenProductUrl/registration.php +++ b/Iazel/RegenProductUrl/registration.php @@ -1,6 +1,9 @@ =100.1", - "magento/module-catalog-url-rewrite": ">=100.1", - "magento/magento-composer-installer": "*" - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "Iazel/RegenProductUrl/registration.php" + "name": "elgentos/regenerate-catalog-urls", + "description": "N/A", + "require": { + "php": "~7.1|~7.2|~7.3", + "magento/framework": "^100.1|^101.0|^102.0|^103.0", + "magento/module-catalog-url-rewrite": "^100.1", + "magento/module-url-rewrite": "^102.0" + }, + "repositories": { + "magento": { + "type": "composer", + "url": "https://repo.magento.com/" + } + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" ], - "psr-4": { - "Iazel\\RegenProductUrl\\": "Iazel/RegenProductUrl" + "autoload": { + "files": [ + "Iazel/RegenProductUrl/registration.php" + ], + "psr-4": { + "Iazel\\RegenProductUrl\\": "Iazel/RegenProductUrl" + } + }, + "replace": { + "iazel/module-regen-product-url": "*" + }, + "require-dev": { + "mediact/testing-suite": "^2.9", + "mediact/coding-standard-magento2": "@stable" + }, + "archive": { + "exclude": [ + "/.gitignore", + "/grumphp.yml", + "/pdepend.xml", + "/phpstan.neon", + "/phpunit.xml", + "/phpcs.xml", + "/phpmd.xml", + "/package.json", + "/.eslintrc.json", + "/.eslintignore", + "/tests" + ] + }, + "config": { + "sort-packages": true } - }, - "replace": { - "iazel/module-regen-product-url": "*" - } } diff --git a/grumphp.yml b/grumphp.yml new file mode 100644 index 0000000..3779b23 --- /dev/null +++ b/grumphp.yml @@ -0,0 +1,2 @@ +imports: + - resource: 'vendor/mediact/testing-suite/config/default/grumphp.yml' \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..b76468f --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "eslint": "^4.19.1" + } +} \ No newline at end of file diff --git a/pdepend.xml b/pdepend.xml new file mode 100644 index 0000000..98099fd --- /dev/null +++ b/pdepend.xml @@ -0,0 +1,11 @@ + + + + + memory + + + diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..12a9a3c --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,10 @@ + + + PHPCS + + + diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 0000000..4852635 --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,10 @@ + + + PHPMD + + + diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..5d19f6e --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +parameters: + level: 6 + paths: + - src + - tests + ignoreErrors: + - '#(class|type) Magento\\TestFramework#i' + - '#(class|type) Magento\\\S*Factory#i' + - '#(method) Magento\\Framework\\Api\\ExtensionAttributesInterface#i' \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..8d7cdbb --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,15 @@ + + + + tests + + + + + src + + + From 8d95d40ca465fed3d309a97b2e7516d13d7920f3 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Tue, 29 Jun 2021 17:01:10 +0200 Subject: [PATCH 19/74] Changed categoryIds argument name to cids --- .../Console/Command/RegenerateCategoryPathCommand.php | 2 +- .../Console/Command/RegenerateCategoryUrlCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php index 3e0db03..a1aa501 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryPathCommand.php @@ -129,7 +129,7 @@ public function execute(InputInterface $input, OutputInterface $output) ->addAttributeToSelect(['name', 'url_path', 'url_key']) ->addAttributeToFilter('level', ['gt' => 1]); - $categoryIds = $input->getArgument('categoryIds'); + $categoryIds = $input->getArgument('cids'); if (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php index 20c68bd..6fd1965 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCategoryUrlCommand.php @@ -118,7 +118,7 @@ public function execute(InputInterface $input, OutputInterface $output) ->addAttributeToSelect(['name', 'url_path', 'url_key']) ->addAttributeToFilter('level', ['gt' => 1]); - $categoryIds = $input->getArgument('categoryIds'); + $categoryIds = $input->getArgument('cids'); if (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); From f583a7ef075210fd862128e0db9f5e4a6325c640 Mon Sep 17 00:00:00 2001 From: Simon Sprankel Date: Tue, 6 Jul 2021 08:41:34 +0200 Subject: [PATCH 20/74] Allow PHP 7.4 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a5b638e..dbedf0d 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "elgentos/regenerate-catalog-urls", "description": "N/A", "require": { - "php": "~7.1|~7.2|~7.3", + "php": "~7.1|~7.2|~7.3|~7.4", "magento/framework": "^100.1|^101.0|^102.0|^103.0", "magento/module-catalog-url-rewrite": "^100.1", "magento/module-url-rewrite": "^102.0" From fc40eadcc09a513f5e905e92766bcef66a432d4d Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Tue, 6 Jul 2021 09:13:44 +0200 Subject: [PATCH 21/74] Allow PHP 8.0 and 8.1 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index dbedf0d..07940c4 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "elgentos/regenerate-catalog-urls", "description": "N/A", "require": { - "php": "~7.1|~7.2|~7.3|~7.4", + "php": "~7.1|~7.2|~7.3|~7.4|~8.0|~8.1", "magento/framework": "^100.1|^101.0|^102.0|^103.0", "magento/module-catalog-url-rewrite": "^100.1", "magento/module-url-rewrite": "^102.0" From f91b916390daffab6d3a97d4eb3c6ba303d4a65d Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Tue, 27 Jul 2021 12:39:23 +0200 Subject: [PATCH 22/74] Loosened restraints for modules --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 07940c4..cd12faa 100644 --- a/composer.json +++ b/composer.json @@ -4,8 +4,8 @@ "require": { "php": "~7.1|~7.2|~7.3|~7.4|~8.0|~8.1", "magento/framework": "^100.1|^101.0|^102.0|^103.0", - "magento/module-catalog-url-rewrite": "^100.1", - "magento/module-url-rewrite": "^102.0" + "magento/module-catalog-url-rewrite": "^100.1|^101.0|^102.0|^103.0", + "magento/module-url-rewrite": "^100.1|^101.0|^102.0|^103.0" }, "repositories": { "magento": { From 913c2da9dc95ca9b22c281d1c4cfda3b44109b09 Mon Sep 17 00:00:00 2001 From: Rico Neitzel Date: Mon, 25 Oct 2021 13:31:41 +0200 Subject: [PATCH 23/74] get rid of module version this just forces us to run setup:upgrade - but that's not required as we don't have any setup scripts here --- Iazel/RegenProductUrl/etc/module.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/etc/module.xml b/Iazel/RegenProductUrl/etc/module.xml index 8a9c8ff..7c920fe 100644 --- a/Iazel/RegenProductUrl/etc/module.xml +++ b/Iazel/RegenProductUrl/etc/module.xml @@ -1,6 +1,6 @@ - + From b4a1014d4e7b688401404ecec5f66db1219c1022 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Tue, 26 Oct 2021 08:50:00 +0200 Subject: [PATCH 24/74] Changed version constraints to drop support for Magento 2.2 --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index cd12faa..665dac4 100644 --- a/composer.json +++ b/composer.json @@ -2,10 +2,10 @@ "name": "elgentos/regenerate-catalog-urls", "description": "N/A", "require": { - "php": "~7.1|~7.2|~7.3|~7.4|~8.0|~8.1", - "magento/framework": "^100.1|^101.0|^102.0|^103.0", - "magento/module-catalog-url-rewrite": "^100.1|^101.0|^102.0|^103.0", - "magento/module-url-rewrite": "^100.1|^101.0|^102.0|^103.0" + "php": "~7.1.3|~7.2|~7.3|~7.4|~8.0|~8.1", + "magento/framework": "^102.0|^103.0", + "magento/module-catalog-url-rewrite": "^100.3|^100.4", + "magento/module-url-rewrite": "^101.1|^102.0" }, "repositories": { "magento": { From ed2ed3729027238192a80f0680f0828c507747fc Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Fri, 24 Dec 2021 10:45:31 +0100 Subject: [PATCH 25/74] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bd71d1d..0e97305 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ This extension adds console commands to be able to regenerate; - a product rewrite URL based on its url path; - a category rewrite URL based on its url path; +- a CMS page rewrite URL based on its url path; - a category URL path based on its URL key and its parent categories. # Install @@ -21,9 +22,10 @@ Usage: regenerate:product:url [-s|--store="..."] [pids1] ... [pidsN] regenerate:category:url [-s]--store="..."] [cids1] ... [cidsN] regenerate:category:path [-s]--store="..."] [cids1] ... [cidsN] + regenerate:cms-page:url [-s]--store="..."] [pids1] ... [pidsN] Arguments: - pids Products to regenerate + pids Products (or Pages) to regenerate cids Categories to regenerate Options: @@ -45,8 +47,7 @@ php bin/magento regenerate:product:url -s1 1 2 3 4 ## What's the difference between url_key and url_path? `url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. It also adds the category slugs of the parent categories so the `url_path` might become `bags/joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = 4)`. -## Why are my category rewrites for a non-default storeview in the default storeview's language? -This is probably due to `url_path` values in the database. Since these aren't updated anymore, but they are still present, the data may be out of sync. See [these](https://github.com/magento/magento2/issues/16202) [issues](https://github.com/magento/magento2/issues/12718). The easiest fix is to delete the `url_path` values from the database (only if you are running >2.1). Here's a quick query; `DELETE FROM catalog_category_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = 3);`. +URL paths are still used in categories though, so don't remove those. ## Why am I getting a 'Duplicated url' warning when running the command? If you see this error, you have duplicate `url_key` values (within a store) in `catalog_product_entity_varchar`. You can use this extension to check those (and you need to fix them manually): [baldwin/magento2-module-url-data-integrity-checker](https://github.com/baldwin-agency/magento2-module-url-data-integrity-checker). From d8348a7e21de9a62d2ad06b162a894bde4d8384b Mon Sep 17 00:00:00 2001 From: Pieter Zandbergen Date: Fri, 28 Jan 2022 13:48:21 +0100 Subject: [PATCH 26/74] fix: Use CommandListInterface Configuring commands in the Magento Console Command List should be done on its interface. Otherwise the commands won't show up in generated production code. --- Iazel/RegenProductUrl/etc/di.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/etc/di.xml b/Iazel/RegenProductUrl/etc/di.xml index 1f9942d..99f4242 100644 --- a/Iazel/RegenProductUrl/etc/di.xml +++ b/Iazel/RegenProductUrl/etc/di.xml @@ -1,7 +1,7 @@ - + Iazel\RegenProductUrl\Console\Command\RegenerateProductUrlCommand From d52978ecb829d915b2d6d994630a351f799c4063 Mon Sep 17 00:00:00 2001 From: Sascha Date: Mon, 7 Feb 2022 10:42:51 +0000 Subject: [PATCH 27/74] Add Batch Update for Product Urls --- .../Command/RegenerateProductUrlCommand.php | 9 +- .../Service/RegenerateProductUrl.php | 159 ++++++++++++------ 2 files changed, 116 insertions(+), 52 deletions(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php index cbf68d2..28be6df 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php @@ -59,14 +59,12 @@ protected function configure(): void 'store', 's', InputOption::VALUE_REQUIRED, - 'Regenerate for one specific store view', - Store::DEFAULT_STORE_ID + 'Regenerate for one specific store view' ) ->addArgument( 'pids', InputArgument::IS_ARRAY, - 'Product IDs to regenerate', - [] + 'Product IDs to regenerate' ); } @@ -86,6 +84,7 @@ public function execute(InputInterface $input, OutputInterface $output) } $storeId = $input->getOption('store'); + $stores = $this->storeManager->getStores(false); if (!is_numeric($storeId)) { @@ -93,7 +92,7 @@ public function execute(InputInterface $input, OutputInterface $output) } $this->regenerateProductUrl->setOutput($output); - $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId); + $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId, $output->isVerbose()); } /** diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 2fc6f35..16976d8 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -5,11 +5,13 @@ namespace Iazel\RegenProductUrl\Service; use Exception; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Visibility; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\UrlPersistInterface; @@ -18,6 +20,8 @@ class RegenerateProductUrl { + const BATCH_SIZE = 500; + /** * @var OutputInterface|null */ @@ -52,10 +56,10 @@ class RegenerateProductUrl /** * Constructor. * - * @param CollectionFactory $collectionFactory + * @param CollectionFactory $collectionFactory * @param ProductUrlRewriteGenerator $urlRewriteGenerator - * @param UrlPersistInterface $urlPersist - * @param StoreManagerInterface $storeManager + * @param UrlPersistInterface $urlPersist + * @param StoreManagerInterface $storeManager */ public function __construct( CollectionFactory $collectionFactory, @@ -70,23 +74,25 @@ public function __construct( } /** - * @param int[] $productIds - * @param int $storeId + * @param int[]|nul $productIds + * @param int|nul $storeId + * @param bool $verbose * * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function execute(array $productIds, int $storeId): void + public function execute(?array $productIds = null, ?int $storeId = null, bool $verbose = false): void { $this->regeneratedCount = 0; - $stores = $this->storeManager->getStores(false); + + $stores = isNull($storeId) + ? [$this->storeManager->getStore($storeId)] + : $this->storeManager->getStores(false); foreach ($stores as $store) { $regeneratedForStore = 0; - // If store has been given through option, skip other stores - if ($storeId !== Store::DEFAULT_STORE_ID and (int) $store->getId() !== $storeId) { - continue; - } + $this->log(sprintf('Start regenerating for store %s (%d)', $store->getName(), $store->getId())); $collection = $this->collectionFactory->create(); $collection @@ -96,58 +102,74 @@ public function execute(array $productIds, int $storeId): void ->addFieldToFilter('status', ['eq' => Status::STATUS_ENABLED]) ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); - if (!empty($productIds)) { + if (is_null($productIds)) { $collection->addIdFilter($productIds); } $collection->addAttributeToSelect(['url_path', 'url_key']); - $list = $collection->load(); + + $deleteProducts = []; /** @var Product $product */ - foreach ($list as $product) { + foreach ($collection as $product) { + $deleteProducts[] = $product->getId(); + if (count($deleteProducts) >= self::BATCH_SIZE) { + $this->deleteUrls($deleteProducts, $store); + } + } + + if (count($deleteProducts)) { + $this->deleteUrls($deleteProducts, $store, true); + } + + $newUrls = []; + try { + /** @var Product $product */ + foreach ($collection as $product) { + if ($verbose) { + $this->log( + sprintf( + 'Regenerating urls for %s (%s) in store (%s)', + $product->getSku(), + $product->getId(), + $store->getName() + ) + ); + } + + $product->setStoreId($store->getId()); + + $newUrls = array_merge($newUrls, $this->urlRewriteGenerator->generate($product)); + if (count($newUrls) >= self::BATCH_SIZE) { + $regeneratedForStore += $this->replaceUrls($newUrls); + } + + } + + if (count($newUrls)) { + $regeneratedForStore += $this->replaceUrls($newUrls, true); + } + + } catch (Exception $e) { $this->log( sprintf( - 'Regenerating urls for %s (%s) in store (%s)', - $product->getSku(), + 'Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . + PHP_EOL . '%s' . PHP_EOL, + $store->getId(), $product->getId(), - $store->getName() + $product->getSku(), + $e->getMessage(), + implode(PHP_EOL, array_keys($newUrls)) ) ); - $product->setStoreId($store->getId()); - - $this->urlPersist->deleteByData( - [ - UrlRewrite::ENTITY_ID => $product->getId(), - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $store->getId() - ] - ); - - $newUrls = $this->urlRewriteGenerator->generate($product); - try { - $this->urlPersist->replace($newUrls); - $regeneratedForStore += count($newUrls); - } catch (Exception $e) { - $this->log( - sprintf( - 'Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . - PHP_EOL . '%s' . PHP_EOL, - $store->getId(), - $product->getId(), - $product->getSku(), - $e->getMessage(), - implode(PHP_EOL, array_keys($newUrls)) - ) - ); - } } $this->log( sprintf( - 'Done regenerating. Regenerated %d urls for store %s', + 'Done regenerating. Regenerated %d urls for store %s (%d)', $regeneratedForStore, - $store->getName() + $store->getName(), + $store->getId() ) ); $this->regeneratedCount += $regeneratedForStore; @@ -183,4 +205,47 @@ private function log(string $message): void $this->output->writeln($message); } } + + /** + * Add product ulrs + * + * @param array $urls + * @param bool $last + * + * @return int + * @throws \Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException + */ + private function replaceUrls(array &$urls, bool $last = false): int + { + $this->log(sprintf('replaceUrls%s batch: %d', $last ? ' last' : '', count($urls))); + $this->urlPersist->replace($urls); + $count = count($urls); + $urls = []; + + return $count; + } + + /** + * Remove old product urls + * + * @param array $productIds + * @param StoreInterface $store + * @param bool $last + * + * @return int + */ + private function deleteUrls(array &$productIds, StoreInterface $store, bool $last = false): int + { + $this->log(sprintf('deleteUrls%s batch: %d', $last ? 'last' : '', count($productIds))); + $this->urlPersist->deleteByData([ + UrlRewrite::ENTITY_ID => $productIds, + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::REDIRECT_TYPE => 0, + UrlRewrite::STORE_ID => $store->getId() + ]); + $count = count($productIds); + $productIds = []; + + return $count; + } } From 16bd5aa8cfbf7bafa87390357dd5fd426ad3bbcd Mon Sep 17 00:00:00 2001 From: Sascha Date: Mon, 7 Feb 2022 10:50:14 +0000 Subject: [PATCH 28/74] Code CleanUp --- Iazel/RegenProductUrl/Service/RegenerateProductUrl.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 16976d8..b22b7b4 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -143,13 +143,11 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v if (count($newUrls) >= self::BATCH_SIZE) { $regeneratedForStore += $this->replaceUrls($newUrls); } - } if (count($newUrls)) { $regeneratedForStore += $this->replaceUrls($newUrls, true); } - } catch (Exception $e) { $this->log( sprintf( From 5e4d0fd9d2d74a735ec96546916539735700ed04 Mon Sep 17 00:00:00 2001 From: Sascha Date: Mon, 7 Feb 2022 13:02:02 +0000 Subject: [PATCH 29/74] Wrong NULL check function https://github.com/elgentos/regenerate-catalog-urls/pull/52/files/16bd5aa8cfbf7bafa87390357dd5fd426ad3bbcd#r800540098 --- Iazel/RegenProductUrl/Service/RegenerateProductUrl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index b22b7b4..932a7a1 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -85,7 +85,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v { $this->regeneratedCount = 0; - $stores = isNull($storeId) + $stores = is_null($storeId) ? [$this->storeManager->getStore($storeId)] : $this->storeManager->getStores(false); From cf52667adeaaa19ce90b8ee4195591467d4341fe Mon Sep 17 00:00:00 2001 From: Sascha Date: Wed, 9 Feb 2022 16:46:14 +0100 Subject: [PATCH 30/74] [Bugfix] type error without option -s After my optimizations I forgot one final test :( --- .../Command/RegenerateProductUrlCommand.php | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php index 28be6df..fc9789c 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php @@ -85,30 +85,29 @@ public function execute(InputInterface $input, OutputInterface $output) $storeId = $input->getOption('store'); - $stores = $this->storeManager->getStores(false); - - if (!is_numeric($storeId)) { + if (!is_null($storeId) && !is_numeric($storeId)) { $storeId = $this->getStoreIdByCode($storeId, $stores); } $this->regenerateProductUrl->setOutput($output); - $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId, $output->isVerbose()); + $this->regenerateProductUrl->execute($input->getArgument('pids'), $storeId, $output->isVerbose()); } /** - * @param string $storeId - * @param StoreInterface[] $stores + * @param string $storeId * - * @return bool|int + * @return null|int */ - private function getStoreIdByCode(string $storeId, array $stores) + private function getStoreIdByCode(string $storeId):?int { + $stores = $this->storeManager->getStores(false); + foreach ($stores as $store) { if ($store->getCode() == $storeId) { - return $store->getId(); + return (int)$store->getId(); } } - return false; + return null; } } From 4b83b2cc78236e3a3455d57042d74f2bf81b5286 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sat, 19 Feb 2022 16:57:38 +0100 Subject: [PATCH 31/74] Fixed bug where passing a storeId would cause regenerating for all stores --- Iazel/RegenProductUrl/Service/RegenerateProductUrl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 932a7a1..8634180 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -85,7 +85,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v { $this->regeneratedCount = 0; - $stores = is_null($storeId) + $stores = !is_null($storeId) ? [$this->storeManager->getStore($storeId)] : $this->storeManager->getStores(false); From 9cb009648b1d60f57f3efd4c2447d81880f01db7 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sat, 19 Feb 2022 16:58:07 +0100 Subject: [PATCH 32/74] Add 'all' value for store input argument --- .../Command/RegenerateProductUrlCommand.php | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php index fc9789c..f8cc1fb 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php @@ -83,25 +83,38 @@ public function execute(InputInterface $input, OutputInterface $output) $this->state->setAreaCode('adminhtml'); } - $storeId = $input->getOption('store'); + $stores = $this->storeManager->getStores(false); + + $this->regenerateProductUrl->setOutput($output); - if (!is_null($storeId) && !is_numeric($storeId)) { + $storeId = $input->getOption('store'); + if ($storeId !== 'all' && !is_null($storeId) && !is_numeric($storeId)) { $storeId = $this->getStoreIdByCode($storeId, $stores); } - $this->regenerateProductUrl->setOutput($output); - $this->regenerateProductUrl->execute($input->getArgument('pids'), $storeId, $output->isVerbose()); + if (!is_null($storeId) && is_numeric($storeId)) { + $this->regenerateProductUrl->execute($input->getArgument('pids'), $storeId, $output->isVerbose()); + return 0; + } + + if ($storeId === 'all') { + foreach($stores as $store) { + $this->regenerateProductUrl->execute($input->getArgument('pids'), $store->getId(), $output->isVerbose()); + } + return 0; + } + + throw new \Exception('No URLs are regenerated - did you pass a store and are you sure the store exists?'); } /** * @param string $storeId + * @param array $stores * * @return null|int */ - private function getStoreIdByCode(string $storeId):?int + private function getStoreIdByCode(string $storeId, array $stores):?int { - $stores = $this->storeManager->getStores(false); - foreach ($stores as $store) { if ($store->getCode() == $storeId) { return (int)$store->getId(); From 931c24958d20d7dea33b0cc699fa2f12893855c7 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sat, 19 Feb 2022 17:13:42 +0100 Subject: [PATCH 33/74] Fixed another bug where all products were selected if product ID's were passed --- Iazel/RegenProductUrl/Service/RegenerateProductUrl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 8634180..8ff8e45 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -102,7 +102,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v ->addFieldToFilter('status', ['eq' => Status::STATUS_ENABLED]) ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); - if (is_null($productIds)) { + if (!is_null($productIds)) { $collection->addIdFilter($productIds); } From f38ab1051fee069f68be715bd27abb07615922be Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sat, 19 Feb 2022 17:34:59 +0100 Subject: [PATCH 34/74] Workaround for odd bug where no products would show up when no IdFilter was added --- Iazel/RegenProductUrl/Service/RegenerateProductUrl.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php index 8ff8e45..285cd6a 100644 --- a/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php +++ b/Iazel/RegenProductUrl/Service/RegenerateProductUrl.php @@ -103,8 +103,9 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); if (!is_null($productIds)) { - $collection->addIdFilter($productIds); + $productIds = $collection->getAllIds(); } + $collection->addIdFilter($productIds); $collection->addAttributeToSelect(['url_path', 'url_key']); From 0327ef30732caea59dc8a7212ff140fd18a0dff6 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sat, 19 Feb 2022 17:44:34 +0100 Subject: [PATCH 35/74] Workaround to regenerate all CMS pages when no pids are given --- .../Console/Command/RegenerateCmsPageUrlCommand.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php index 9a3d70d..d836103 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateCmsPageUrlCommand.php @@ -127,8 +127,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (count($input->getArgument('pids')) > 0) { $pageIds = $input->getArgument('pids'); - $pages->addFieldToFilter('page_id', ['in' => $pageIds]); + } else { + $pageIds = $pages->getAllIds(); } + $pageIds = array_unique($pageIds); + $pages->addFieldToFilter('page_id', ['in' => $pageIds]); $counter = 0; From 95259c949fb7793c36e3ee0782d474649826867b Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sat, 19 Feb 2022 16:44:40 +0000 Subject: [PATCH 36/74] Apply fixes from StyleCI --- .../Console/Command/RegenerateProductUrlCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php index f8cc1fb..781551e 100644 --- a/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php +++ b/Iazel/RegenProductUrl/Console/Command/RegenerateProductUrlCommand.php @@ -98,7 +98,7 @@ public function execute(InputInterface $input, OutputInterface $output) } if ($storeId === 'all') { - foreach($stores as $store) { + foreach ($stores as $store) { $this->regenerateProductUrl->execute($input->getArgument('pids'), $store->getId(), $output->isVerbose()); } return 0; From 4e604461ea7119da0f8758a6f68ceaf8545121be Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sun, 20 Feb 2022 11:30:03 +0100 Subject: [PATCH 37/74] Moved extension files to src, spruced up composer.json, better formatting for README --- LICENSE | 2 +- README.md | 16 ++--- composer.json | 58 +++++++++++++------ .../Command/AbstractRegenerateCommand.php | 20 +++++++ .../Command/RegenerateCategoryPathCommand.php | 4 +- .../Command/RegenerateCategoryUrlCommand.php | 2 +- .../Command/RegenerateCmsPageUrlCommand.php | 2 +- .../Command/RegenerateProductUrlCommand.php | 4 +- .../Controller/Adminhtml/Action/Product.php | 4 +- .../Model/CategoryUrlPathGenerator.php | 2 +- .../Service/RegenerateProductUrl.php | 2 +- .../etc/adminhtml/routes.xml | 2 +- {Iazel/RegenProductUrl => src}/etc/di.xml | 10 ++-- {Iazel/RegenProductUrl => src}/etc/events.xml | 0 {Iazel/RegenProductUrl => src}/etc/module.xml | 2 +- {Iazel/RegenProductUrl => src}/i18n/en_US.csv | 0 .../RegenProductUrl => src}/registration.php | 2 +- .../ui_component/product_listing.xml | 0 18 files changed, 87 insertions(+), 45 deletions(-) create mode 100644 src/Console/Command/AbstractRegenerateCommand.php rename {Iazel/RegenProductUrl => src}/Console/Command/RegenerateCategoryPathCommand.php (97%) rename {Iazel/RegenProductUrl => src}/Console/Command/RegenerateCategoryUrlCommand.php (99%) rename {Iazel/RegenProductUrl => src}/Console/Command/RegenerateCmsPageUrlCommand.php (98%) rename {Iazel/RegenProductUrl => src}/Console/Command/RegenerateProductUrlCommand.php (96%) rename {Iazel/RegenProductUrl => src}/Controller/Adminhtml/Action/Product.php (95%) rename {Iazel/RegenProductUrl => src}/Model/CategoryUrlPathGenerator.php (92%) rename {Iazel/RegenProductUrl => src}/Service/RegenerateProductUrl.php (99%) rename {Iazel/RegenProductUrl => src}/etc/adminhtml/routes.xml (83%) rename {Iazel/RegenProductUrl => src}/etc/di.xml (59%) rename {Iazel/RegenProductUrl => src}/etc/events.xml (100%) rename {Iazel/RegenProductUrl => src}/etc/module.xml (90%) rename {Iazel/RegenProductUrl => src}/i18n/en_US.csv (100%) rename {Iazel/RegenProductUrl => src}/registration.php (78%) rename {Iazel/RegenProductUrl => src}/view/adminhtml/ui_component/product_listing.xml (100%) diff --git a/LICENSE b/LICENSE index 99f4da9..3e8ba6c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016 Iazel +Copyright (c) 2016 Elgentos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0e97305..221c85a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# What does it do +# Elgentos Regenerate Catalog URLs + +## What does it do This extension adds console commands to be able to regenerate; - a product rewrite URL based on its url path; @@ -6,7 +8,7 @@ This extension adds console commands to be able to regenerate; - a CMS page rewrite URL based on its url path; - a category URL path based on its URL key and its parent categories. -# Install +## Install Using Composer; ```sh @@ -14,9 +16,9 @@ composer require elgentos/regenerate-catalog-urls php bin/magento setup:upgrade ``` -Or download and copy the `Iazel` directory into `app/code/`, enable the module and run `php bin/magento setup:upgrade`. +Or download and copy the `src` directory into `app/code/Elgentos/RegenerateCatalogUrls` and run `php bin/magento setup:upgrade`. -# How to use +## How to use ``` Usage: regenerate:product:url [-s|--store="..."] [pids1] ... [pidsN] @@ -42,12 +44,12 @@ php bin/magento regenerate:product:url php bin/magento regenerate:product:url -s1 1 2 3 4 ``` -# FAQ +## FAQ -## What's the difference between url_key and url_path? +### What's the difference between url_key and url_path? `url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. It also adds the category slugs of the parent categories so the `url_path` might become `bags/joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = 4)`. URL paths are still used in categories though, so don't remove those. -## Why am I getting a 'Duplicated url' warning when running the command? +### Why am I getting a 'Duplicated url' warning when running the command? If you see this error, you have duplicate `url_key` values (within a store) in `catalog_product_entity_varchar`. You can use this extension to check those (and you need to fix them manually): [baldwin/magento2-module-url-data-integrity-checker](https://github.com/baldwin-agency/magento2-module-url-data-integrity-checker). diff --git a/composer.json b/composer.json index 665dac4..e94fa54 100644 --- a/composer.json +++ b/composer.json @@ -1,38 +1,50 @@ { "name": "elgentos/regenerate-catalog-urls", - "description": "N/A", + "description": "Regenerate Catalog URL Rewrites (products, categories, cms pages)", + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], "require": { "php": "~7.1.3|~7.2|~7.3|~7.4|~8.0|~8.1", "magento/framework": "^102.0|^103.0", "magento/module-catalog-url-rewrite": "^100.3|^100.4", "magento/module-url-rewrite": "^101.1|^102.0" }, - "repositories": { - "magento": { - "type": "composer", - "url": "https://repo.magento.com/" - } - }, - "type": "magento2-module", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "Iazel/RegenProductUrl/registration.php" - ], - "psr-4": { - "Iazel\\RegenProductUrl\\": "Iazel/RegenProductUrl" - } + "minimum-stability": "stable", + "archive": { + "exclude": [ + "/.gitignore", + "/grumphp.yml", + "/pdepend.xml", + "/phpstan.neon", + "/phpunit.xml", + "/phpcs.xml", + "/phpmd.xml", + "/package.json", + "/.eslintrc.json", + "/.eslintignore", + "/tests" + ] }, "replace": { "iazel/module-regen-product-url": "*" }, + "suggest": { + "baldwin/magento2-module-url-data-integrity-checker": "Magento 2 module which can find potential url related problems in your catalog data", + "fisheye/module-url-rewrite-optimiser": "A Magento module that stops URL rewrites with category paths being generated for products when 'Use Categories Path for Product URLs' setting is disabled in config." + }, "require-dev": { "mediact/testing-suite": "^2.9", "mediact/coding-standard-magento2": "@stable" }, + "repositories": { + "magento": { + "type": "composer", + "url": "https://repo.magento.com/" + } + }, "archive": { "exclude": [ "/.gitignore", @@ -50,5 +62,13 @@ }, "config": { "sort-packages": true + }, + "autoload": { + "files": [ + "src/registration.php" + ], + "psr-4": { + "Elgentos\\RegenerateCatalogUrls\\": "src/" + } } } diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php new file mode 100644 index 0000000..f718195 --- /dev/null +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -0,0 +1,20 @@ + - + diff --git a/Iazel/RegenProductUrl/etc/di.xml b/src/etc/di.xml similarity index 59% rename from Iazel/RegenProductUrl/etc/di.xml rename to src/etc/di.xml index 99f4242..d473cc5 100644 --- a/Iazel/RegenProductUrl/etc/di.xml +++ b/src/etc/di.xml @@ -4,12 +4,12 @@ - Iazel\RegenProductUrl\Console\Command\RegenerateProductUrlCommand - Iazel\RegenProductUrl\Console\Command\RegenerateCategoryUrlCommand - Iazel\RegenProductUrl\Console\Command\RegenerateCategoryPathCommand - Iazel\RegenProductUrl\Console\Command\RegenerateCmsPageUrlCommand + Elgentos\RegenerateCatalogUrls\Console\Command\RegenerateProductUrlCommand + Elgentos\RegenerateCatalogUrls\Console\Command\RegenerateCategoryUrlCommand + Elgentos\RegenerateCatalogUrls\Console\Command\RegenerateCategoryPathCommand + Elgentos\RegenerateCatalogUrls\Console\Command\RegenerateCmsPageUrlCommand - + diff --git a/Iazel/RegenProductUrl/etc/events.xml b/src/etc/events.xml similarity index 100% rename from Iazel/RegenProductUrl/etc/events.xml rename to src/etc/events.xml diff --git a/Iazel/RegenProductUrl/etc/module.xml b/src/etc/module.xml similarity index 90% rename from Iazel/RegenProductUrl/etc/module.xml rename to src/etc/module.xml index 7c920fe..0aef6c5 100644 --- a/Iazel/RegenProductUrl/etc/module.xml +++ b/src/etc/module.xml @@ -1,6 +1,6 @@ - + diff --git a/Iazel/RegenProductUrl/i18n/en_US.csv b/src/i18n/en_US.csv similarity index 100% rename from Iazel/RegenProductUrl/i18n/en_US.csv rename to src/i18n/en_US.csv diff --git a/Iazel/RegenProductUrl/registration.php b/src/registration.php similarity index 78% rename from Iazel/RegenProductUrl/registration.php rename to src/registration.php index a2786b4..8448d79 100644 --- a/Iazel/RegenProductUrl/registration.php +++ b/src/registration.php @@ -4,6 +4,6 @@ ComponentRegistrar::register( ComponentRegistrar::MODULE, - 'Iazel_RegenProductUrl', + 'Elgentos_RegenerateCatalogUrls', __DIR__ ); diff --git a/Iazel/RegenProductUrl/view/adminhtml/ui_component/product_listing.xml b/src/view/adminhtml/ui_component/product_listing.xml similarity index 100% rename from Iazel/RegenProductUrl/view/adminhtml/ui_component/product_listing.xml rename to src/view/adminhtml/ui_component/product_listing.xml From 51114bb3a3a382615bcf65868369302e74079f10 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sun, 20 Feb 2022 11:49:50 +0100 Subject: [PATCH 38/74] Cleaned up code --- composer.json | 2 +- .../Command/AbstractRegenerateCommand.php | 29 ++++++++- .../Command/RegenerateCategoryPathCommand.php | 47 ++++---------- .../Command/RegenerateCategoryUrlCommand.php | 35 ++++------- .../Command/RegenerateCmsPageUrlCommand.php | 56 +++++++---------- .../Command/RegenerateProductUrlCommand.php | 63 +++++-------------- src/Controller/Adminhtml/Action/Product.php | 40 +++++------- src/Model/CategoryUrlPathGenerator.php | 4 +- src/Service/RegenerateProductUrl.php | 51 +++++---------- 9 files changed, 127 insertions(+), 200 deletions(-) diff --git a/composer.json b/composer.json index e94fa54..cce5e3b 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "AFL-3.0" ], "require": { - "php": "~7.1.3|~7.2|~7.3|~7.4|~8.0|~8.1", + "php": "~7.4|~8.0|~8.1", "magento/framework": "^102.0|^103.0", "magento/module-catalog-url-rewrite": "^100.3|^100.4", "magento/module-url-rewrite": "^101.1|^102.0" diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index f718195..e2bb640 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -15,6 +15,33 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -abstract class AbstractRegenerateCommand extends Command { +abstract class AbstractRegenerateCommand extends Command +{ + protected function configure() + { + $this->addOption( + 'store', + 's', + InputOption::VALUE_REQUIRED, + 'Regenerate for one specific store view' + ); + } + + /** + * @param string $storeId + * @param array $stores + * + * @return null|int + */ + protected function getStoreIdByCode(string $storeId, array $stores): ?int + { + foreach ($stores as $store) { + if ($store->getCode() === $storeId) { + return (int)$store->getId(); + } + } + + return null; + } } diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index 3e16bee..b2e054c 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -1,10 +1,11 @@ setOrigData('url_key', mt_rand(1, 1000)); + $category->setOrigData('url_key', random_int(1, 1000)); // set url_path in orig data to random value to force regeneration of path for children - $category->setOrigData('url_path', mt_rand(1, 1000)); + $category->setOrigData('url_path', random_int(1, 1000)); // Make use of Magento's event for this $this->emulation->startEnvironmentEmulation($store_id, Area::AREA_FRONTEND, true); diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 9cce455..6beecbb 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -1,5 +1,7 @@ setName('regenerate:category:url') ->setDescription('Regenerate url for given categories') @@ -99,10 +86,10 @@ protected function configure() * @param InputInterface $input * @param OutputInterface $output * - * @return void|int + * @return int * @throws LocalizedException */ - public function execute(InputInterface $input, OutputInterface $output) + public function execute(InputInterface $input, OutputInterface $output): int { try { $this->state->getAreaCode(); @@ -165,6 +152,8 @@ public function execute(InputInterface $input, OutputInterface $output) $output->writeln( sprintf('Done regenerating. Regenerated %d urls', $counter) ); + + return 0; } /** diff --git a/src/Console/Command/RegenerateCmsPageUrlCommand.php b/src/Console/Command/RegenerateCmsPageUrlCommand.php index b0d75f4..ccdbb70 100644 --- a/src/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/src/Console/Command/RegenerateCmsPageUrlCommand.php @@ -1,5 +1,7 @@ state = $state; - $this->emulation = $emulation; - $this->pageCollectionFactory = $pageCollectionFactory; - $this->urlPersist = $urlPersist; + $this->state = $state; + $this->emulation = $emulation; + $this->pageCollectionFactory = $pageCollectionFactory; + $this->urlPersist = $urlPersist; $this->cmsPageUrlRewriteGenerator = $cmsPageUrlRewriteGenerator; } @@ -100,7 +88,7 @@ protected function configure() } /** - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * * @return int diff --git a/src/Console/Command/RegenerateProductUrlCommand.php b/src/Console/Command/RegenerateProductUrlCommand.php index bbf26a6..c7c9b72 100644 --- a/src/Console/Command/RegenerateProductUrlCommand.php +++ b/src/Console/Command/RegenerateProductUrlCommand.php @@ -1,5 +1,7 @@ state = $state; - $this->storeManager = $storeManager; + RegenerateProductUrl $regenerateProductUrl + ) + { + $this->state = $state; + $this->storeManager = $storeManager; $this->regenerateProductUrl = $regenerateProductUrl; parent::__construct(); } @@ -55,12 +49,6 @@ protected function configure(): void { $this->setName('regenerate:product:url') ->setDescription('Regenerate url for given products') - ->addOption( - 'store', - 's', - InputOption::VALUE_REQUIRED, - 'Regenerate for one specific store view' - ) ->addArgument( 'pids', InputArgument::IS_ARRAY, @@ -69,10 +57,10 @@ protected function configure(): void } /** - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * - * @return void|int + * @return int * @throws LocalizedException */ public function execute(InputInterface $input, OutputInterface $output) @@ -104,23 +92,6 @@ public function execute(InputInterface $input, OutputInterface $output) return 0; } - throw new \Exception('No URLs are regenerated - did you pass a store and are you sure the store exists?'); - } - - /** - * @param string $storeId - * @param array $stores - * - * @return null|int - */ - private function getStoreIdByCode(string $storeId, array $stores):?int - { - foreach ($stores as $store) { - if ($store->getCode() == $storeId) { - return (int)$store->getId(); - } - } - - return null; + throw new LocalizedException(__('No URLs are regenerated - did you pass a store and are you sure the store exists?')); } } diff --git a/src/Controller/Adminhtml/Action/Product.php b/src/Controller/Adminhtml/Action/Product.php index fbb3a03..9317652 100644 --- a/src/Controller/Adminhtml/Action/Product.php +++ b/src/Controller/Adminhtml/Action/Product.php @@ -17,37 +17,29 @@ class Product extends Action { - /** - * @var RegenerateProductUrl - */ - private $regenerateProductUrl; + private RegenerateProductUrl $regenerateProductUrl; - /** - * @var Filter - */ - private $filter; + private Filter $filter; - /** - * @var CollectionFactory - */ - private $collectionFactory; + private CollectionFactory $collectionFactory; /** * Constructor. * - * @param CollectionFactory $collectionFactory - * @param Filter $filter + * @param CollectionFactory $collectionFactory + * @param Filter $filter * @param RegenerateProductUrl $regenerateProductUrl - * @param Context $context + * @param Context $context */ public function __construct( - CollectionFactory $collectionFactory, - Filter $filter, + CollectionFactory $collectionFactory, + Filter $filter, RegenerateProductUrl $regenerateProductUrl, - Context $context - ) { - $this->collectionFactory = $collectionFactory; - $this->filter = $filter; + Context $context + ) + { + $this->collectionFactory = $collectionFactory; + $this->filter = $filter; $this->regenerateProductUrl = $regenerateProductUrl; parent::__construct($context); @@ -64,11 +56,11 @@ public function __construct( public function execute(): Redirect { $productIds = $this->getSelectedProductIds(); - $storeId = (int) $this->getRequest()->getParam('store', 0); - $filters = $this->getRequest()->getParam('filters', []); + $storeId = (int)$this->getRequest()->getParam('store', 0); + $filters = $this->getRequest()->getParam('filters', []); if (isset($filters['store_id'])) { - $storeId = (int) $filters['store_id']; + $storeId = (int)$filters['store_id']; } try { diff --git a/src/Model/CategoryUrlPathGenerator.php b/src/Model/CategoryUrlPathGenerator.php index 61f5a6d..c20244c 100644 --- a/src/Model/CategoryUrlPathGenerator.php +++ b/src/Model/CategoryUrlPathGenerator.php @@ -1,5 +1,7 @@ log(sprintf('deleteUrls%s batch: %d', $last ? 'last' : '', count($productIds))); $this->urlPersist->deleteByData([ @@ -242,9 +225,5 @@ private function deleteUrls(array &$productIds, StoreInterface $store, bool $las UrlRewrite::REDIRECT_TYPE => 0, UrlRewrite::STORE_ID => $store->getId() ]); - $count = count($productIds); - $productIds = []; - - return $count; } } From e8dc1f4793d2fac9f622e10546e4884bb363cb1f Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sun, 20 Feb 2022 12:19:40 +0100 Subject: [PATCH 39/74] Refactored method of getting storeId from input --- .../Command/AbstractRegenerateCommand.php | 70 ++++++++++++++++--- .../Command/RegenerateProductUrlCommand.php | 49 +++---------- 2 files changed, 72 insertions(+), 47 deletions(-) diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index e2bb640..56847ce 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -4,44 +4,96 @@ namespace Elgentos\RegenerateCatalogUrls\Console\Command; +use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; use Magento\Framework\App\State; use Magento\Framework\Exception\LocalizedException; use Magento\Store\Api\Data\StoreInterface; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; abstract class AbstractRegenerateCommand extends Command { + protected StoreManagerInterface $storeManager; + + protected State $state; + + protected RegenerateProductUrl $regenerateProductUrl; + + protected QuestionHelper $questionHelper; + + public function __construct( + StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper + ) + { + $this->storeManager = $storeManager; + $this->state = $state; + $this->regenerateProductUrl = $regenerateProductUrl; + $this->questionHelper = $questionHelper; + parent::__construct(); + } + protected function configure() { $this->addOption( 'store', 's', - InputOption::VALUE_REQUIRED, - 'Regenerate for one specific store view' + InputOption::VALUE_OPTIONAL, + 'Regenerate for a specific store view', + false ); } + protected function getStoreId() + { + $storeInput = $this->input->getOption('store'); + + if ($storeInput === 'all') { + return $storeInput; + } + + $storeId = false; + if (is_numeric($storeInput)) { + $storeId = (int) $storeInput; + } else if (is_string($storeInput)) { + $storeId = $this->getStoreIdByCode($storeInput); + } else if (false === $storeInput) { + $choices = array_merge(['all'], array_map(fn ($store) => $store->getCode(), $this->getAllStores())); + $question = new ChoiceQuestion(__('Pick a store')->getText(), $choices, 'all'); + $storeCode = $this->questionHelper->ask($this->input, $this->output, $question); + $storeId = $this->getStoreIdByCode($storeCode); + } + + return $storeId; + } + + protected function getAllStores($withDefault = false): array + { + return $this->storeManager->getStores($withDefault); + } /** - * @param string $storeId - * @param array $stores - * + * @param string $storeCode * @return null|int + * @throws LocalizedException */ - protected function getStoreIdByCode(string $storeId, array $stores): ?int + protected function getStoreIdByCode(string $storeCode): ?int { - foreach ($stores as $store) { - if ($store->getCode() === $storeId) { + foreach ($this->getAllStores() as $store) { + if ($store->getCode() === $storeCode) { return (int)$store->getId(); } } - return null; + throw new LocalizedException(__('The store that was requested (%1) wasn\'t found. Verify the store and try again.', $storeCode)); } } diff --git a/src/Console/Command/RegenerateProductUrlCommand.php b/src/Console/Command/RegenerateProductUrlCommand.php index c7c9b72..47cb9c7 100644 --- a/src/Console/Command/RegenerateProductUrlCommand.php +++ b/src/Console/Command/RegenerateProductUrlCommand.php @@ -7,41 +7,14 @@ use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; use Magento\Framework\App\State; use Magento\Framework\Exception\LocalizedException; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Model\Store; -use Magento\Store\Model\StoreManagerInterface; -use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; class RegenerateProductUrlCommand extends AbstractRegenerateCommand { - private State $state; - - private StoreManagerInterface $storeManager; - - private RegenerateProductUrl $regenerateProductUrl; - - /** - * RegenerateProductUrlCommand constructor. - * @param State $state - * @param StoreManagerInterface $storeManager - * @param RegenerateProductUrl $regenerateProductUrl - */ - public function __construct( - State $state, - StoreManagerInterface $storeManager, - RegenerateProductUrl $regenerateProductUrl - ) - { - $this->state = $state; - $this->storeManager = $storeManager; - $this->regenerateProductUrl = $regenerateProductUrl; - parent::__construct(); - } - /** * @return void */ @@ -54,6 +27,8 @@ protected function configure(): void InputArgument::IS_ARRAY, 'Product IDs to regenerate' ); + + parent::configure(); } /** @@ -65,29 +40,27 @@ protected function configure(): void */ public function execute(InputInterface $input, OutputInterface $output) { + $this->input = $input; + $this->output = $output; + try { $this->state->getAreaCode(); } catch (LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } - $stores = $this->storeManager->getStores(false); + $storeId = $this->getStoreId(); $this->regenerateProductUrl->setOutput($output); - $storeId = $input->getOption('store'); - if ($storeId !== 'all' && !is_null($storeId) && !is_numeric($storeId)) { - $storeId = $this->getStoreIdByCode($storeId, $stores); - } - - if (!is_null($storeId) && is_numeric($storeId)) { + if (is_numeric($storeId)) { $this->regenerateProductUrl->execute($input->getArgument('pids'), $storeId, $output->isVerbose()); return 0; } if ($storeId === 'all') { - foreach ($stores as $store) { - $this->regenerateProductUrl->execute($input->getArgument('pids'), $store->getId(), $output->isVerbose()); + foreach ($this->getAllStores() as $store) { + $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $store->getId(), $output->isVerbose()); } return 0; } From 4059670e8f664fd8908b2bbf130eb9c04f3ae0c1 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sun, 20 Feb 2022 12:26:24 +0100 Subject: [PATCH 40/74] More refactoring --- .../Command/AbstractRegenerateCommand.php | 7 +- .../Command/RegenerateCategoryUrlCommand.php | 67 ++++++++----------- .../Command/RegenerateProductUrlCommand.php | 16 ++--- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index 56847ce..8448b09 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -7,12 +7,9 @@ use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; use Magento\Framework\App\State; use Magento\Framework\Exception\LocalizedException; -use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\QuestionHelper; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -20,6 +17,10 @@ abstract class AbstractRegenerateCommand extends Command { + protected InputInterface $input; + + protected OutputInterface $output; + protected StoreManagerInterface $storeManager; protected State $state; diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 6beecbb..51a3de6 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -4,25 +4,23 @@ namespace Elgentos\RegenerateCatalogUrls\Console\Command; +use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; use Exception; -use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Framework\App\Area; use Magento\Framework\Exception\LocalizedException; use Magento\Store\Model\App\Emulation; +use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\UrlPersistInterface; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; -use Magento\Catalog\Model\ResourceModel\Category\Collection; -use Magento\Store\Model\Store; use Magento\Framework\App\State; -class RegenerateCategoryUrlCommand extends Command +class RegenerateCategoryUrlCommand extends AbstractRegenerateCommand { protected CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator; @@ -34,29 +32,21 @@ class RegenerateCategoryUrlCommand extends Command private Emulation $emulation; - /** - * RegenerateCategoryUrlCommand constructor. - * - * @param State $state - * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator - * @param UrlPersistInterface $urlPersist - * @param CategoryCollectionFactory $categoryCollectionFactory - * @param Emulation $emulation - */ - public function __construct( - State $state, - CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, - UrlPersistInterface $urlPersist, - CategoryCollectionFactory $categoryCollectionFactory, - Emulation $emulation - ) { - $this->state = $state; + public function __construct(StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, + CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, + UrlPersistInterface $urlPersist, + CategoryCollectionFactory $categoryCollectionFactory, + Emulation $emulation + ) + { + parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; - $this->urlPersist = $urlPersist; - $this->categoryCollectionFactory = $categoryCollectionFactory; - $this->emulation = $emulation; - - parent::__construct(); + $this->urlPersist = $urlPersist; + $this->categoryCollectionFactory = $categoryCollectionFactory; + $this->emulation = $emulation; } /** @@ -70,13 +60,6 @@ protected function configure(): void 'cids', InputArgument::IS_ARRAY, 'Categories to regenerate' - ) - ->addOption( - 'store', - 's', - InputOption::VALUE_REQUIRED, - 'Use the specific Store View', - Store::DEFAULT_STORE_ID ); parent::configure(); @@ -91,17 +74,21 @@ protected function configure(): void */ public function execute(InputInterface $input, OutputInterface $output): int { + $this->input = $input; + $this->output = $output; + try { $this->state->getAreaCode(); } catch (LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } - $store_id = $input->getOption('store'); - $this->emulation->startEnvironmentEmulation($store_id, Area::AREA_FRONTEND, true); + $storeId = $this->getStoreId(); + + $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); $categories = $this->categoryCollectionFactory->create() - ->setStore($store_id) + ->setStore($storeId) ->addAttributeToSelect(['name', 'url_path', 'url_key']) ->addAttributeToFilter('level', ['gt' => 1]); @@ -123,7 +110,7 @@ public function execute(InputInterface $input, OutputInterface $output): int UrlRewrite::ENTITY_ID => $category->getId(), UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $store_id + UrlRewrite::STORE_ID => $storeId ] ); @@ -138,7 +125,7 @@ public function execute(InputInterface $input, OutputInterface $output): int sprintf( 'Duplicated url for store ID %d, category %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, - $store_id, + $storeId, $category->getId(), $category->getName(), $e->getMessage(), diff --git a/src/Console/Command/RegenerateProductUrlCommand.php b/src/Console/Command/RegenerateProductUrlCommand.php index 47cb9c7..ce870d3 100644 --- a/src/Console/Command/RegenerateProductUrlCommand.php +++ b/src/Console/Command/RegenerateProductUrlCommand.php @@ -53,18 +53,16 @@ public function execute(InputInterface $input, OutputInterface $output) $this->regenerateProductUrl->setOutput($output); - if (is_numeric($storeId)) { - $this->regenerateProductUrl->execute($input->getArgument('pids'), $storeId, $output->isVerbose()); - return 0; + if ($storeId === 'all') { + $stores = array_map(fn($store) => $store->getId(), $this->getAllStores()); + } else { + $stores = [$storeId]; } - if ($storeId === 'all') { - foreach ($this->getAllStores() as $store) { - $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $store->getId(), $output->isVerbose()); - } - return 0; + foreach ($stores as $storeId) { + $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId, $output->isVerbose()); } - throw new LocalizedException(__('No URLs are regenerated - did you pass a store and are you sure the store exists?')); + return 0; } } From c4c487aacfd1495b70c88477e827cffe028fb5a7 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sun, 20 Feb 2022 12:40:06 +0100 Subject: [PATCH 41/74] Finished refactoring --- .../Command/AbstractRegenerateCommand.php | 21 ++- .../Command/RegenerateCategoryPathCommand.php | 122 +++++++-------- .../Command/RegenerateCategoryUrlCommand.php | 109 +++++++------- .../Command/RegenerateCmsPageUrlCommand.php | 140 ++++++++---------- .../Command/RegenerateProductUrlCommand.php | 17 +-- 5 files changed, 185 insertions(+), 224 deletions(-) diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index 8448b09..ea86cc6 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -54,27 +54,34 @@ protected function configure() ); } - protected function getStoreId() + /** + * @throws LocalizedException + */ + protected function getChosenStores() { $storeInput = $this->input->getOption('store'); - if ($storeInput === 'all') { - return $storeInput; - } - $storeId = false; if (is_numeric($storeInput)) { $storeId = (int) $storeInput; + } else if ($storeInput === 'all') { + $storeId = $storeInput; } else if (is_string($storeInput)) { $storeId = $this->getStoreIdByCode($storeInput); } else if (false === $storeInput) { $choices = array_merge(['all'], array_map(fn ($store) => $store->getCode(), $this->getAllStores())); $question = new ChoiceQuestion(__('Pick a store')->getText(), $choices, 'all'); $storeCode = $this->questionHelper->ask($this->input, $this->output, $question); - $storeId = $this->getStoreIdByCode($storeCode); + $storeId = ($storeCode === 'all' ? 'all' : $this->getStoreIdByCode($storeCode)); + } + + if ($storeId === 'all') { + $stores = array_map(fn($store) => $store->getId(), $this->getAllStores()); + } else { + $stores = [$storeId]; } - return $storeId; + return $stores; } protected function getAllStores($withDefault = false): array diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index b2e054c..5c67ed8 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -4,62 +4,42 @@ namespace Elgentos\RegenerateCatalogUrls\Console\Command; -use Elgentos\RegenerateCatalogUrls\Model\CategoryUrlPathGenerator; +use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; use Exception; use Magento\Framework\App\Area; +use Magento\Framework\Console\Cli; use Magento\Framework\EntityManager\EventManager; use Magento\Framework\Exception\LocalizedException; use Magento\Store\Model\App\Emulation; -use Magento\UrlRewrite\Model\UrlPersistInterface; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputOption; +use Magento\Store\Model\StoreManagerInterface; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; -use Magento\Store\Model\Store; use Magento\Framework\App\State; -class RegenerateCategoryPathCommand extends Command +class RegenerateCategoryPathCommand extends AbstractRegenerateCommand { - protected CategoryUrlPathGenerator $categoryUrlPathGenerator; - - protected UrlPersistInterface $urlPersist; - - protected State $state; - private CategoryCollectionFactory $categoryCollectionFactory; private EventManager $eventManager; private Emulation $emulation; - /** - * RegenerateCategoryPathCommand constructor. - * - * @param State $state - * @param CategoryCollectionFactory $categoryCollectionFactory - * @param CategoryUrlPathGenerator $categoryUrlPathGenerator - * @param UrlPersistInterface $urlPersist - * @param EventManager $eventManager - * @param Emulation $emulation - */ - public function __construct( - State $state, - CategoryCollectionFactory $categoryCollectionFactory, - CategoryUrlPathGenerator $categoryUrlPathGenerator, - UrlPersistInterface $urlPersist, - EventManager $eventManager, - Emulation $emulation - ) { - $this->state = $state; - $this->categoryUrlPathGenerator = $categoryUrlPathGenerator; - $this->urlPersist = $urlPersist; + public function __construct(StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, + CategoryCollectionFactory $categoryCollectionFactory, + EventManager $eventManager, + Emulation $emulation + ) + { + parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryCollectionFactory = $categoryCollectionFactory; - $this->eventManager = $eventManager; - $this->emulation = $emulation; - - parent::__construct(); + $this->eventManager = $eventManager; + $this->emulation = $emulation; } /** @@ -73,13 +53,6 @@ protected function configure(): void 'cids', InputArgument::IS_ARRAY, 'Categories to regenerate' - ) - ->addOption( - 'store', - 's', - InputOption::VALUE_REQUIRED, - 'Use the specific Store View', - Store::DEFAULT_STORE_ID ); parent::configure(); @@ -89,54 +62,61 @@ protected function configure(): void * @param InputInterface $input * @param OutputInterface $output * - * @return void|int + * @return int * @throws LocalizedException * @throws Exception */ public function execute(InputInterface $input, OutputInterface $output) { + $this->input = $input; + $this->output = $output; + try { $this->state->getAreaCode(); } catch (LocalizedException $e) { $this->state->setAreaCode('adminhtml'); } - $store_id = $input->getOption('store'); + $stores = $this->getChosenStores(); - $categories = $this->categoryCollectionFactory->create() - ->setStore($store_id) - ->addAttributeToSelect(['name', 'url_path', 'url_key']) - ->addAttributeToFilter('level', ['gt' => 1]); + foreach ($stores as $storeId) { + $categories = $this->categoryCollectionFactory->create() + ->setStore($storeId) + ->addAttributeToSelect(['name', 'url_path', 'url_key']) + ->addAttributeToFilter('level', ['gt' => 1]); - $categoryIds = $input->getArgument('cids'); + $categoryIds = $input->getArgument('cids'); - if (!empty($categoryIds)) { - $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); - } + if (!empty($categoryIds)) { + $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); + } - $counter = 0; + $counter = 0; - foreach ($categories as $category) { - $output->writeln( - sprintf('Regenerating urls for %s (%s)', $category->getName(), $category->getId()) - ); + foreach ($categories as $category) { + $output->writeln( + sprintf('Regenerating paths for %s (%s)', $category->getName(), $category->getId()) + ); - // set url_key in orig data to random value to force regeneration of path - $category->setOrigData('url_key', random_int(1, 1000)); + // set url_key in orig data to random value to force regeneration of path + $category->setOrigData('url_key', random_int(1, 1000)); - // set url_path in orig data to random value to force regeneration of path for children - $category->setOrigData('url_path', random_int(1, 1000)); + // set url_path in orig data to random value to force regeneration of path for children + $category->setOrigData('url_path', random_int(1, 1000)); - // Make use of Magento's event for this - $this->emulation->startEnvironmentEmulation($store_id, Area::AREA_FRONTEND, true); - $this->eventManager->dispatch('regenerate_category_url_path', ['category' => $category]); - $this->emulation->stopEnvironmentEmulation(); + // Make use of Magento's event for this + $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); + $this->eventManager->dispatch('regenerate_category_url_path', ['category' => $category]); + $this->emulation->stopEnvironmentEmulation(); - $counter++; + $counter++; + } + + $output->writeln( + sprintf('Done regenerating. Regenerated url paths for %d categories', $counter) + ); } - $output->writeln( - sprintf('Done regenerating. Regenerated url paths for %d categories', $counter) - ); + return Cli::RETURN_SUCCESS; } } diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 51a3de6..977a3b7 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -7,6 +7,7 @@ use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; use Exception; use Magento\Framework\App\Area; +use Magento\Framework\Console\Cli; use Magento\Framework\Exception\LocalizedException; use Magento\Store\Model\App\Emulation; use Magento\Store\Model\StoreManagerInterface; @@ -22,24 +23,22 @@ class RegenerateCategoryUrlCommand extends AbstractRegenerateCommand { - protected CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator; + private CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator; - protected UrlPersistInterface $urlPersist; - - protected State $state; + private UrlPersistInterface $urlPersist; private CategoryCollectionFactory $categoryCollectionFactory; private Emulation $emulation; - public function __construct(StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper, + public function __construct(StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, - UrlPersistInterface $urlPersist, - CategoryCollectionFactory $categoryCollectionFactory, - Emulation $emulation + UrlPersistInterface $urlPersist, + CategoryCollectionFactory $categoryCollectionFactory, + Emulation $emulation ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); @@ -66,7 +65,7 @@ protected function configure(): void } /** - * @param InputInterface $input + * @param InputInterface $input * @param OutputInterface $output * * @return int @@ -83,64 +82,66 @@ public function execute(InputInterface $input, OutputInterface $output): int $this->state->setAreaCode('adminhtml'); } - $storeId = $this->getStoreId(); + $counter = 0; - $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); + $stores = $this->getChosenStores(); - $categories = $this->categoryCollectionFactory->create() - ->setStore($storeId) - ->addAttributeToSelect(['name', 'url_path', 'url_key']) - ->addAttributeToFilter('level', ['gt' => 1]); + foreach ($stores as $storeId) { + $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); - $categoryIds = $input->getArgument('cids'); + $categories = $this->categoryCollectionFactory->create() + ->setStore($storeId) + ->addAttributeToSelect(['name', 'url_path', 'url_key']) + ->addAttributeToFilter('level', ['gt' => 1]); - if (!empty($categoryIds)) { - $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); - } + $categoryIds = $input->getArgument('cids'); - $counter = 0; + if (!empty($categoryIds)) { + $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); + } - foreach ($categories as $category) { - $output->writeln( - sprintf('Regenerating urls for %s (%s)', $category->getName(), $category->getId()) - ); + foreach ($categories as $category) { + $output->writeln( + sprintf('Regenerating urls for %s (%s)', $category->getName(), $category->getId()) + ); - $this->urlPersist->deleteByData( - [ - UrlRewrite::ENTITY_ID => $category->getId(), - UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, - UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $storeId - ] - ); + $this->urlPersist->deleteByData( + [ + UrlRewrite::ENTITY_ID => $category->getId(), + UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::REDIRECT_TYPE => 0, + UrlRewrite::STORE_ID => $storeId + ] + ); - $newUrls = $this->categoryUrlRewriteGenerator->generate($category); + $newUrls = $this->categoryUrlRewriteGenerator->generate($category); - try { - $newUrls = $this->filterEmptyRequestPaths($newUrls); - $this->urlPersist->replace($newUrls); - $counter += count($newUrls); - } catch (Exception $e) { - $output->writeln( - sprintf( - 'Duplicated url for store ID %d, category %d (%s) - %s Generated URLs:' . + try { + $newUrls = $this->filterEmptyRequestPaths($newUrls); + $this->urlPersist->replace($newUrls); + $counter += count($newUrls); + } catch (Exception $e) { + $output->writeln( + sprintf( + 'Duplicated url for store ID %d, category %d (%s) - %s Generated URLs:' . PHP_EOL . '%s' . PHP_EOL, - $storeId, - $category->getId(), - $category->getName(), - $e->getMessage(), - implode(PHP_EOL, array_keys($newUrls)) - ) - ); + $storeId, + $category->getId(), + $category->getName(), + $e->getMessage(), + implode(PHP_EOL, array_keys($newUrls)) + ) + ); + } } - } - $this->emulation->stopEnvironmentEmulation(); + $this->emulation->stopEnvironmentEmulation(); + } $output->writeln( sprintf('Done regenerating. Regenerated %d urls', $counter) ); - return 0; + return Cli::RETURN_SUCCESS; } /** diff --git a/src/Console/Command/RegenerateCmsPageUrlCommand.php b/src/Console/Command/RegenerateCmsPageUrlCommand.php index ccdbb70..fc7ccb4 100644 --- a/src/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/src/Console/Command/RegenerateCmsPageUrlCommand.php @@ -4,6 +4,7 @@ namespace Elgentos\RegenerateCatalogUrls\Console\Command; +use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; use Exception; use Magento\Cms\Model\Page; use Magento\Cms\Model\ResourceModel\Page\CollectionFactory as PageCollectionFactory; @@ -13,19 +14,16 @@ use Magento\Framework\Console\Cli; use Magento\Framework\Exception\LocalizedException; use Magento\Store\Model\App\Emulation; -use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\UrlPersistInterface; -use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -class RegenerateCmsPageUrlCommand extends Command +class RegenerateCmsPageUrlCommand extends AbstractRegenerateCommand { - private State $state; - private Emulation $emulation; private PageCollectionFactory $pageCollectionFactory; @@ -34,26 +32,17 @@ class RegenerateCmsPageUrlCommand extends Command private CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator; - /** - * RegenerateCmsPageUrlCommand constructor. - * - * @param State $state - * @param Emulation $emulation - * @param PageCollectionFactory $pageCollectionFactory - * @param UrlPersistInterface $urlPersist - * @param CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator - */ - public function __construct( - State $state, - Emulation $emulation, - PageCollectionFactory $pageCollectionFactory, - UrlPersistInterface $urlPersist, - CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator + public function __construct(StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, + Emulation $emulation, + PageCollectionFactory $pageCollectionFactory, + UrlPersistInterface $urlPersist, + CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator ) { - parent::__construct(); - - $this->state = $state; + parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->emulation = $emulation; $this->pageCollectionFactory = $pageCollectionFactory; $this->urlPersist = $urlPersist; @@ -71,20 +60,9 @@ protected function configure() 'pids', InputArgument::IS_ARRAY, 'CMS Pages to regenerate' - ) - ->addOption( - 'store', - 's', - InputOption::VALUE_OPTIONAL, - 'Regenerate for one specific store view', - Store::DEFAULT_STORE_ID - )->addOption( - 'all-stores', - 'a', - InputOption::VALUE_OPTIONAL, - 'Regenerate for all stores.', - false ); + + parent::configure(); } /** @@ -96,6 +74,9 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output): int { + $this->input = $input; + $this->output = $output; + $output->writeln('Start regenerating urls for CMS pages.'); try { @@ -104,55 +85,56 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->state->setAreaCode(Area::AREA_ADMINHTML); } - $storeId = $input->getOption('store'); - $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); + $counter = 0; - $pages = $this->pageCollectionFactory->create(); + $stores = $this->getChosenStores(); - if (!$input->getOption('all-stores') !== false) { - $pages->addStoreFilter($storeId); - } + foreach ($stores as $storeId) { + $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); - if (count($input->getArgument('pids')) > 0) { - $pageIds = $input->getArgument('pids'); - } else { - $pageIds = $pages->getAllIds(); - } - $pageIds = array_unique($pageIds); - $pages->addFieldToFilter('page_id', ['in' => $pageIds]); + $pages = $this->pageCollectionFactory->create(); - $counter = 0; + $pages->addStoreFilter($storeId); - /** @var Page $page */ - foreach ($pages as $page) { - $newUrls = $this->cmsPageUrlRewriteGenerator->generate($page); - - try { - $this->urlPersist->replace($newUrls); - $counter += count($newUrls); - } catch (UrlAlreadyExistsException $e) { - $output->writeln( - sprintf( - 'Url for page %s (%d) already exists.' . PHP_EOL . '%s', - $page->getTitle(), - $page->getId(), - $e->getMessage() - ) - ); - } catch (Exception $e) { - $output->writeln( - 'Couldn\'t replace url for %s (%d)' . PHP_EOL . '%s' - ); + if (count($input->getArgument('pids')) > 0) { + $pageIds = $input->getArgument('pids'); + } else { + $pageIds = $pages->getAllIds(); + } + $pageIds = array_unique($pageIds); + $pages->addFieldToFilter('page_id', ['in' => $pageIds]); + + /** @var Page $page */ + foreach ($pages as $page) { + $newUrls = $this->cmsPageUrlRewriteGenerator->generate($page); + + try { + $this->urlPersist->replace($newUrls); + $counter += count($newUrls); + } catch (UrlAlreadyExistsException $e) { + $output->writeln( + sprintf( + 'Url for page %s (%d) already exists.' . PHP_EOL . '%s', + $page->getTitle(), + $page->getId(), + $e->getMessage() + ) + ); + } catch (Exception $e) { + $output->writeln( + 'Couldn\'t replace url for %s (%d)' . PHP_EOL . '%s' + ); + } } - } - $this->emulation->stopEnvironmentEmulation(); - $output->writeln( - sprintf( - 'Finished regenerating. Regenerated %d urls.', - $counter - ) - ); + $this->emulation->stopEnvironmentEmulation(); + $output->writeln( + sprintf( + 'Finished regenerating. Regenerated %d urls.', + $counter + ) + ); + } return Cli::RETURN_SUCCESS; } diff --git a/src/Console/Command/RegenerateProductUrlCommand.php b/src/Console/Command/RegenerateProductUrlCommand.php index ce870d3..f6df8ce 100644 --- a/src/Console/Command/RegenerateProductUrlCommand.php +++ b/src/Console/Command/RegenerateProductUrlCommand.php @@ -4,14 +4,11 @@ namespace Elgentos\RegenerateCatalogUrls\Console\Command; -use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; -use Magento\Framework\App\State; +use Magento\Framework\Console\Cli; use Magento\Framework\Exception\LocalizedException; -use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Question\ChoiceQuestion; class RegenerateProductUrlCommand extends AbstractRegenerateCommand { @@ -49,20 +46,14 @@ public function execute(InputInterface $input, OutputInterface $output) $this->state->setAreaCode('adminhtml'); } - $storeId = $this->getStoreId(); - $this->regenerateProductUrl->setOutput($output); - if ($storeId === 'all') { - $stores = array_map(fn($store) => $store->getId(), $this->getAllStores()); - } else { - $stores = [$storeId]; - } + $stores = $this->getChosenStores(); foreach ($stores as $storeId) { - $this->regenerateProductUrl->execute($input->getArgument('pids'), (int) $storeId, $output->isVerbose()); + $this->regenerateProductUrl->execute($input->getArgument('pids'), (int)$storeId, $output->isVerbose()); } - return 0; + return Cli::RETURN_SUCCESS; } } From fa9da3d6d0e9b84a4ae1547f32d2f57f3457555b Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sun, 20 Feb 2022 12:43:15 +0100 Subject: [PATCH 42/74] Updated readme, added support for single store mode --- README.md | 5 ++++- src/Console/Command/AbstractRegenerateCommand.php | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 221c85a..ae35110 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Arguments: cids Categories to regenerate Options: - --store (-s) Use the specific Store View (default: 0) + --store (-s) Use a specific store (store Id, store code or 'all') --help (-h) Display this help message ``` @@ -42,6 +42,9 @@ php bin/magento regenerate:product:url # Regenerate url for products with id (1, 2, 3, 4) for store 1 php bin/magento regenerate:product:url -s1 1 2 3 4 + +# Regenerate url for all CMS pages +php bin/magento regenerate:cms-page:url -s all ``` ## FAQ diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index ea86cc6..cdda95e 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -61,6 +61,10 @@ protected function getChosenStores() { $storeInput = $this->input->getOption('store'); + if ($this->storeManager->isSingleStoreMode()) { + return [0]; + } + $storeId = false; if (is_numeric($storeInput)) { $storeId = (int) $storeInput; From 93465c9ec52a9f6c348f7b8c2cd1018977f29ddc Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 20 Feb 2022 11:44:31 +0000 Subject: [PATCH 43/74] Apply fixes from StyleCI --- .../Command/AbstractRegenerateCommand.php | 11 +++++----- .../Command/RegenerateCategoryPathCommand.php | 18 ++++++++--------- .../Command/RegenerateCategoryUrlCommand.php | 20 +++++++++---------- .../Command/RegenerateCmsPageUrlCommand.php | 20 +++++++++---------- src/Controller/Adminhtml/Action/Product.php | 3 +-- 5 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index cdda95e..920b165 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -34,8 +34,7 @@ public function __construct( State $state, RegenerateProductUrl $regenerateProductUrl, QuestionHelper $questionHelper - ) - { + ) { $this->storeManager = $storeManager; $this->state = $state; $this->regenerateProductUrl = $regenerateProductUrl; @@ -68,11 +67,11 @@ protected function getChosenStores() $storeId = false; if (is_numeric($storeInput)) { $storeId = (int) $storeInput; - } else if ($storeInput === 'all') { + } elseif ($storeInput === 'all') { $storeId = $storeInput; - } else if (is_string($storeInput)) { + } elseif (is_string($storeInput)) { $storeId = $this->getStoreIdByCode($storeInput); - } else if (false === $storeInput) { + } elseif (false === $storeInput) { $choices = array_merge(['all'], array_map(fn ($store) => $store->getCode(), $this->getAllStores())); $question = new ChoiceQuestion(__('Pick a store')->getText(), $choices, 'all'); $storeCode = $this->questionHelper->ask($this->input, $this->output, $question); @@ -80,7 +79,7 @@ protected function getChosenStores() } if ($storeId === 'all') { - $stores = array_map(fn($store) => $store->getId(), $this->getAllStores()); + $stores = array_map(fn ($store) => $store->getId(), $this->getAllStores()); } else { $stores = [$storeId]; } diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index 5c67ed8..d050bfe 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -27,15 +27,15 @@ class RegenerateCategoryPathCommand extends AbstractRegenerateCommand private Emulation $emulation; - public function __construct(StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper, - CategoryCollectionFactory $categoryCollectionFactory, - EventManager $eventManager, - Emulation $emulation - ) - { + public function __construct( + StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, + CategoryCollectionFactory $categoryCollectionFactory, + EventManager $eventManager, + Emulation $emulation + ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryCollectionFactory = $categoryCollectionFactory; $this->eventManager = $eventManager; diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 977a3b7..d577b8f 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -31,16 +31,16 @@ class RegenerateCategoryUrlCommand extends AbstractRegenerateCommand private Emulation $emulation; - public function __construct(StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper, - CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, - UrlPersistInterface $urlPersist, - CategoryCollectionFactory $categoryCollectionFactory, - Emulation $emulation - ) - { + public function __construct( + StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, + CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, + UrlPersistInterface $urlPersist, + CategoryCollectionFactory $categoryCollectionFactory, + Emulation $emulation + ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; $this->urlPersist = $urlPersist; diff --git a/src/Console/Command/RegenerateCmsPageUrlCommand.php b/src/Console/Command/RegenerateCmsPageUrlCommand.php index fc7ccb4..acfea34 100644 --- a/src/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/src/Console/Command/RegenerateCmsPageUrlCommand.php @@ -32,16 +32,16 @@ class RegenerateCmsPageUrlCommand extends AbstractRegenerateCommand private CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator; - public function __construct(StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper, - Emulation $emulation, - PageCollectionFactory $pageCollectionFactory, - UrlPersistInterface $urlPersist, - CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator - ) - { + public function __construct( + StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, + Emulation $emulation, + PageCollectionFactory $pageCollectionFactory, + UrlPersistInterface $urlPersist, + CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator + ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->emulation = $emulation; $this->pageCollectionFactory = $pageCollectionFactory; diff --git a/src/Controller/Adminhtml/Action/Product.php b/src/Controller/Adminhtml/Action/Product.php index 9317652..28efac6 100644 --- a/src/Controller/Adminhtml/Action/Product.php +++ b/src/Controller/Adminhtml/Action/Product.php @@ -36,8 +36,7 @@ public function __construct( Filter $filter, RegenerateProductUrl $regenerateProductUrl, Context $context - ) - { + ) { $this->collectionFactory = $collectionFactory; $this->filter = $filter; $this->regenerateProductUrl = $regenerateProductUrl; From 152ce3d381270bb5f51f5028002bc86fd7bf10eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mohamed=20Ka=C3=AFd?= Date: Thu, 24 Feb 2022 16:11:39 +0100 Subject: [PATCH 44/74] Update RegenerateProductUrl.php you can't just regenerate a product or a group of product . --- src/Service/RegenerateProductUrl.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 553dfaf..ce166ab 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -86,9 +86,8 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); if (!is_null($productIds)) { - $productIds = $collection->getAllIds(); + $collection->addIdFilter($productIds); } - $collection->addIdFilter($productIds); $collection->addAttributeToSelect(['url_path', 'url_key']); From 52af578b064945e3bd4a9d40c4cccaa143cebe07 Mon Sep 17 00:00:00 2001 From: Timon Heuser <53174153+oneserv-heuser@users.noreply.github.com> Date: Tue, 8 Mar 2022 10:19:52 +0100 Subject: [PATCH 45/74] fix: typed property error on url regeneration via adminhtml --- src/Service/RegenerateProductUrl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 553dfaf..47c8a41 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -24,7 +24,7 @@ class RegenerateProductUrl { public const BATCH_SIZE = 500; - private ?OutputInterface $output; + private ?OutputInterface $output = null; private CollectionFactory $collectionFactory; From 765125a2db803ef966e76dac918f6930820a5f28 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Tue, 8 Mar 2022 15:38:11 +0100 Subject: [PATCH 46/74] Removed negation of is_null to fix regenerating specific products --- src/Service/RegenerateProductUrl.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index ce166ab..5ae06a4 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -85,9 +85,10 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v ->addFieldToFilter('status', ['eq' => Status::STATUS_ENABLED]) ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); - if (!is_null($productIds)) { - $collection->addIdFilter($productIds); + if (is_null($productIds)) { + $productIds = $collection->getAllIds(); } + $collection->addIdFilter($productIds); $collection->addAttributeToSelect(['url_path', 'url_key']); From 2ba397dbe3a9f766fa44deb9ee8131611f84ed0b Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Sat, 19 Mar 2022 09:15:47 +0100 Subject: [PATCH 47/74] Fix when no pids have been given but store has been set Closes #69 --- src/Service/RegenerateProductUrl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 4b2d9b5..66adffe 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -85,7 +85,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v ->addFieldToFilter('status', ['eq' => Status::STATUS_ENABLED]) ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); - if (is_null($productIds)) { + if (is_null($productIds) || (is_array($productIds) && empty($productIds))) { $productIds = $collection->getAllIds(); } $collection->addIdFilter($productIds); From e24250eb738d568dc5b7a49193871ed5d589c0aa Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Thu, 14 Apr 2022 19:37:59 +0200 Subject: [PATCH 48/74] Reset deleteProducts array after processing batches --- src/Service/RegenerateProductUrl.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 66adffe..f78b63e 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -99,11 +99,13 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v $deleteProducts[] = $product->getId(); if (count($deleteProducts) >= self::BATCH_SIZE) { $this->deleteUrls($deleteProducts, $store); + $deleteProducts = []; } } if (count($deleteProducts)) { $this->deleteUrls($deleteProducts, $store, true); + $deleteProducts = []; } $newUrls = []; From 582618e42aabf214da05b7a3d08d2e6171830ccd Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 14:47:32 +0100 Subject: [PATCH 49/74] Add option cfromrootid to regenerate:category:url "Regenerate for root category and its children only" --- .../Command/RegenerateCategoryUrlCommand.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index d577b8f..22e06e2 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -59,6 +59,12 @@ protected function configure(): void 'cids', InputArgument::IS_ARRAY, 'Categories to regenerate' + )->addOption( + 'cfromrootid', + 'r', + InputOption::VALUE_OPTIONAL, + 'Regenerate for root category and its children only', + false ); parent::configure(); @@ -94,9 +100,16 @@ public function execute(InputInterface $input, OutputInterface $output): int ->addAttributeToSelect(['name', 'url_path', 'url_key']) ->addAttributeToFilter('level', ['gt' => 1]); + $fromRootId = intval($input->getOption('cfromrootid')) ?? 0; $categoryIds = $input->getArgument('cids'); - - if (!empty($categoryIds)) { + if ($fromRootId) { + //path LIKE '1/rootcategory/%' OR path = '1/rootcategory' + $categories->addAttributeToFilter('path', [ + 'like' => '1/' . $fromRootId . '/%', + '=' => '1/' . $fromRootId + ]); + } + else if (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); } From 9aee63b53e643d96fea8e3f8720971e6f4a00bd2 Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 14:49:01 +0100 Subject: [PATCH 50/74] Add path to selection --- src/Console/Command/RegenerateCategoryUrlCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 22e06e2..20c3e5b 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -97,7 +97,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $categories = $this->categoryCollectionFactory->create() ->setStore($storeId) - ->addAttributeToSelect(['name', 'url_path', 'url_key']) + ->addAttributeToSelect(['name', 'url_path', 'url_key', 'path']) ->addAttributeToFilter('level', ['gt' => 1]); $fromRootId = intval($input->getOption('cfromrootid')) ?? 0; From 039ac647be272d22b4b8485d3151f152b5b86188 Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 14:52:32 +0100 Subject: [PATCH 51/74] Add option cfromrootid to regenerate:category:path Regenerate for root category and its children only --- .../Command/RegenerateCategoryPathCommand.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index d050bfe..f9077d3 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -53,6 +53,12 @@ protected function configure(): void 'cids', InputArgument::IS_ARRAY, 'Categories to regenerate' + )->addOption( + 'cfromrootid', + 'r', + InputOption::VALUE_OPTIONAL, + 'Regenerate for root category and its children only', + false ); parent::configure(); @@ -82,12 +88,19 @@ public function execute(InputInterface $input, OutputInterface $output) foreach ($stores as $storeId) { $categories = $this->categoryCollectionFactory->create() ->setStore($storeId) - ->addAttributeToSelect(['name', 'url_path', 'url_key']) + ->addAttributeToSelect(['name', 'url_path', 'url_key', 'path']) ->addAttributeToFilter('level', ['gt' => 1]); + $fromRootOnly = intval($input->getOption('cfromrootid')) ?? 0; $categoryIds = $input->getArgument('cids'); - - if (!empty($categoryIds)) { + if ($fromRootOnly) { + //path LIKE '1/rootcategory/%' OR path = '1/rootcategory' + $categories->addAttributeToFilter('path', [ + 'like' => '1/' . $fromRootOnly . '/%', + '=' => '1/' . $fromRootOnly + ]); + } + else if (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); } From 12a32e451abfdd13b9f17c2cfcf709a131fdfe03 Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 15:03:16 +0100 Subject: [PATCH 52/74] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ae35110..3480aeb 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ Or download and copy the `src` directory into `app/code/Elgentos/RegenerateCatal ``` Usage: regenerate:product:url [-s|--store="..."] [pids1] ... [pidsN] - regenerate:category:url [-s]--store="..."] [cids1] ... [cidsN] - regenerate:category:path [-s]--store="..."] [cids1] ... [cidsN] + regenerate:category:url [-s]--store="..."] [-r]--root="..."] [cids1] ... [cidsN] + regenerate:category:path [-s]--store="..."] [-r]--root="..."] [cids1] ... [cidsN] regenerate:cms-page:url [-s]--store="..."] [pids1] ... [pidsN] Arguments: @@ -32,6 +32,7 @@ Arguments: Options: --store (-s) Use a specific store (store Id, store code or 'all') + --root (-r) Regenerate for root category and its children, ignoring cids. --help (-h) Display this help message ``` @@ -45,6 +46,9 @@ php bin/magento regenerate:product:url -s1 1 2 3 4 # Regenerate url for all CMS pages php bin/magento regenerate:cms-page:url -s all + +# Regenerate url for root category 4 and its children for store 1 +php bin/magento regenerate:category:url -s1 -r4 ``` ## FAQ From b79bc21999236af68a0de76d31aaf30fc39c30ed Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 15:04:20 +0100 Subject: [PATCH 53/74] Update option name cfromrootid to root --- src/Console/Command/RegenerateCategoryUrlCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 20c3e5b..7c1a82a 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -60,7 +60,7 @@ protected function configure(): void InputArgument::IS_ARRAY, 'Categories to regenerate' )->addOption( - 'cfromrootid', + 'root', 'r', InputOption::VALUE_OPTIONAL, 'Regenerate for root category and its children only', @@ -100,7 +100,7 @@ public function execute(InputInterface $input, OutputInterface $output): int ->addAttributeToSelect(['name', 'url_path', 'url_key', 'path']) ->addAttributeToFilter('level', ['gt' => 1]); - $fromRootId = intval($input->getOption('cfromrootid')) ?? 0; + $fromRootId = intval($input->getOption('root')) ?? 0; $categoryIds = $input->getArgument('cids'); if ($fromRootId) { //path LIKE '1/rootcategory/%' OR path = '1/rootcategory' From bda9cbb43fddcef971e3bcb4bf7b16ed1477253a Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 15:04:48 +0100 Subject: [PATCH 54/74] Update option name cfromrootid to root --- src/Console/Command/RegenerateCategoryPathCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index f9077d3..ca12739 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -54,7 +54,7 @@ protected function configure(): void InputArgument::IS_ARRAY, 'Categories to regenerate' )->addOption( - 'cfromrootid', + 'root', 'r', InputOption::VALUE_OPTIONAL, 'Regenerate for root category and its children only', @@ -91,7 +91,7 @@ public function execute(InputInterface $input, OutputInterface $output) ->addAttributeToSelect(['name', 'url_path', 'url_key', 'path']) ->addAttributeToFilter('level', ['gt' => 1]); - $fromRootOnly = intval($input->getOption('cfromrootid')) ?? 0; + $fromRootOnly = intval($input->getOption('root')) ?? 0; $categoryIds = $input->getArgument('cids'); if ($fromRootOnly) { //path LIKE '1/rootcategory/%' OR path = '1/rootcategory' From 38b89db2fd92590e2472e95974f5ce13893509e4 Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 15:10:06 +0100 Subject: [PATCH 55/74] Fix styleci issues --- src/Console/Command/RegenerateCategoryUrlCommand.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 7c1a82a..48170aa 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -108,8 +108,7 @@ public function execute(InputInterface $input, OutputInterface $output): int 'like' => '1/' . $fromRootId . '/%', '=' => '1/' . $fromRootId ]); - } - else if (!empty($categoryIds)) { + } elseif (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); } From cded47ac694a9f9556702dc1ea5be2986ffa4875 Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 15:10:37 +0100 Subject: [PATCH 56/74] Fix styleci issues --- src/Console/Command/RegenerateCategoryPathCommand.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index ca12739..fc0a416 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -99,8 +99,7 @@ public function execute(InputInterface $input, OutputInterface $output) 'like' => '1/' . $fromRootOnly . '/%', '=' => '1/' . $fromRootOnly ]); - } - else if (!empty($categoryIds)) { + } elseif (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); } From 2ff8267942951ef9b9aed6947104ccb4bab7a078 Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 15:16:18 +0100 Subject: [PATCH 57/74] Add use statement --- src/Console/Command/RegenerateCategoryUrlCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 48170aa..540598b 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; From 502896884512c144ff0801962e44ae392ceefc01 Mon Sep 17 00:00:00 2001 From: Justin van Elst Date: Wed, 2 Nov 2022 15:16:45 +0100 Subject: [PATCH 58/74] Add use statement --- src/Console/Command/RegenerateCategoryPathCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index fc0a416..cc18f8d 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\Framework\App\State; From 4a35ff51c35c550ae8bc9721933d488a7565e97b Mon Sep 17 00:00:00 2001 From: Erfan Date: Tue, 13 Dec 2022 15:51:50 +0800 Subject: [PATCH 59/74] Improved product URL error message to show which URLs were not unique --- src/Service/RegenerateProductUrl.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index f78b63e..c90aa7e 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -134,6 +134,19 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v if (count($newUrls)) { $regeneratedForStore += $this->replaceUrls($newUrls, true); } + } catch (\Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException $e) { + $this->log(sprintf( + 'Couldn\'t insert duplicate URL rewrites for the following ' . + 'products on store ID %d:' . PHP_EOL . '%s', + $store->getId(), + implode(PHP_EOL, array_map(function ($url) { + return sprintf( + '- Product ID: %d, request path: %s', + $url['entity_id'], + $url['request_path'] + ); + }, $e->getUrls())) + )); } catch (Exception $e) { $this->log( sprintf( From df957342c39673ea52a4d41fbacd0327bb3acc31 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Wed, 14 Dec 2022 18:41:40 +0100 Subject: [PATCH 60/74] Clarified error message entire batch failed --- src/Service/RegenerateProductUrl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index c90aa7e..9fe4dac 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -137,7 +137,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v } catch (\Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException $e) { $this->log(sprintf( 'Couldn\'t insert duplicate URL rewrites for the following ' . - 'products on store ID %d:' . PHP_EOL . '%s', + 'products on store ID %d (current batch failed):' . PHP_EOL . '%s', $store->getId(), implode(PHP_EOL, array_map(function ($url) { return sprintf( From bed28405012b8709d58739cf47c73d3e19ac3e4c Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Thu, 28 Dec 2023 09:47:50 +0100 Subject: [PATCH 61/74] Proxy Emulation class in RegenerateCategoryPathCommand --- src/Console/Command/RegenerateCategoryPathCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index cc18f8d..1fde124 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -35,7 +35,7 @@ public function __construct( QuestionHelper $questionHelper, CategoryCollectionFactory $categoryCollectionFactory, EventManager $eventManager, - Emulation $emulation + Emulation\Proxy $emulation ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryCollectionFactory = $categoryCollectionFactory; From 2fd19d4dcc1053fcb110234592434f0ef5d8b8b9 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Thu, 28 Dec 2023 09:48:18 +0100 Subject: [PATCH 62/74] Proxy Emulation class in RegenerateCategoryUrlCommand --- src/Console/Command/RegenerateCategoryUrlCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 540598b..a7781af 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -40,7 +40,7 @@ public function __construct( CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, UrlPersistInterface $urlPersist, CategoryCollectionFactory $categoryCollectionFactory, - Emulation $emulation + Emulation\Proxy $emulation ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; From 36a8fc1133f44a30cf43026fd88e9ef26d4093de Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Thu, 28 Dec 2023 09:48:44 +0100 Subject: [PATCH 63/74] Proxy Emulation class in RegenerateCmsPageUrlCommand --- src/Console/Command/RegenerateCmsPageUrlCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Command/RegenerateCmsPageUrlCommand.php b/src/Console/Command/RegenerateCmsPageUrlCommand.php index acfea34..06380ce 100644 --- a/src/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/src/Console/Command/RegenerateCmsPageUrlCommand.php @@ -37,7 +37,7 @@ public function __construct( State $state, RegenerateProductUrl $regenerateProductUrl, QuestionHelper $questionHelper, - Emulation $emulation, + Emulation\Proxy $emulation, PageCollectionFactory $pageCollectionFactory, UrlPersistInterface $urlPersist, CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator From 5d150cae929a671ce2309d72dc4984e121c6dbb7 Mon Sep 17 00:00:00 2001 From: Pieter Hoste Date: Thu, 28 Dec 2023 21:33:36 +0100 Subject: [PATCH 64/74] Proxy Emulation class in RegenerateProductUrl --- src/Service/RegenerateProductUrl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 9fe4dac..d9b53f0 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -46,7 +46,7 @@ class RegenerateProductUrl */ public function __construct( CollectionFactory $collectionFactory, - ProductUrlRewriteGenerator $urlRewriteGenerator, + ProductUrlRewriteGenerator\Proxy $urlRewriteGenerator, UrlPersistInterface $urlPersist, StoreManagerInterface $storeManager ) { From 597f4fabfd4d21eb8fcf66cf9cc31f88b59dee97 Mon Sep 17 00:00:00 2001 From: Marvin Hinz <35603466+marvinhinz@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:11:26 +0100 Subject: [PATCH 65/74] improve RegenerateCategoryUrlCommand to only generate rewrites for categories of the correct root category --- .../Command/RegenerateCategoryUrlCommand.php | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index a7781af..3a0b2e6 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -93,7 +93,30 @@ public function execute(InputInterface $input, OutputInterface $output): int $stores = $this->getChosenStores(); + $rootIdOption = intval($input->getOption('root')) ?: false; + foreach ($stores as $storeId) { + $currentRootId = intval($this->storeManager->getGroup($this->storeManager->getStore($storeId)->getStoreGroupId())->getRootCategoryId()); + if($rootIdOption !== false) { + $fromRootId = $rootIdOption; + if($rootIdOption !== $currentRootId) { + $output->writeln( + sprintf('Skipping store %s because its root category id %s, differs from the passed root category %s', $storeId, $currentRootId, $fromRootId) + ); + continue; + } + } else { + $fromRootId = $currentRootId; + } + + $output->writeln( + sprintf('Processing store %s...', $storeId) + ); + + $rootCategory = $this->categoryCollectionFactory->create()->addAttributeToFilter('entity_id', $fromRootId)->addAttributeToSelect("name")->getFirstItem(); + if(!$rootCategory->getId()) { + throw new \Exception(sprintf("Root category with ID %d, was not found.", $fromRootId)); + } $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); $categories = $this->categoryCollectionFactory->create() @@ -101,7 +124,7 @@ public function execute(InputInterface $input, OutputInterface $output): int ->addAttributeToSelect(['name', 'url_path', 'url_key', 'path']) ->addAttributeToFilter('level', ['gt' => 1]); - $fromRootId = intval($input->getOption('root')) ?? 0; + $categoryIds = $input->getArgument('cids'); if ($fromRootId) { //path LIKE '1/rootcategory/%' OR path = '1/rootcategory' @@ -115,7 +138,12 @@ public function execute(InputInterface $input, OutputInterface $output): int foreach ($categories as $category) { $output->writeln( - sprintf('Regenerating urls for %s (%s)', $category->getName(), $category->getId()) + sprintf('Regenerating urls for %s (%s)', + implode('/', [ + $rootCategory->getName(), + ...array_map(fn($category) => $category->getName(), $category->getParentCategories()) + ]), + $category->getId()) ); $this->urlPersist->deleteByData( From dfe2a79489b78065d0d995af03445dff4e703835 Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Thu, 18 Apr 2024 09:33:29 +0200 Subject: [PATCH 66/74] Change store code for single store mode from 0 to 1 This fixes #82 --- src/Console/Command/AbstractRegenerateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index 920b165..908041f 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -61,7 +61,7 @@ protected function getChosenStores() $storeInput = $this->input->getOption('store'); if ($this->storeManager->isSingleStoreMode()) { - return [0]; + return [1]; } $storeId = false; From e69cdbb46bea415883ce4d0d77fdc6eb188b2e3a Mon Sep 17 00:00:00 2001 From: Peter Jaap Blaakmeer Date: Thu, 25 Apr 2024 12:19:06 +0200 Subject: [PATCH 67/74] Add PHP 8.2 and PHP 8.3 to constraints --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cce5e3b..c7bcfb7 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "AFL-3.0" ], "require": { - "php": "~7.4|~8.0|~8.1", + "php": "~7.4|~8.0|~8.1|~8.2|~8.3", "magento/framework": "^102.0|^103.0", "magento/module-catalog-url-rewrite": "^100.3|^100.4", "magento/module-url-rewrite": "^101.1|^102.0" From fcedd5a07bb76d009318400a332e258c3c50442e Mon Sep 17 00:00:00 2001 From: Simon Sprankel Date: Fri, 14 Jun 2024 13:15:03 +0200 Subject: [PATCH 68/74] Dynamically retrieve entity_type_id --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3480aeb..b7b21e8 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ php bin/magento regenerate:category:url -s1 -r4 ## FAQ ### What's the difference between url_key and url_path? -`url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. It also adds the category slugs of the parent categories so the `url_path` might become `bags/joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = 4)`. +`url_key` contains the key, like `joust-duffle-bag` for the product "Joust Duffle Bag". The `url_path` is generated by taking the `url_key` and adding the suffix (which for products is stored in `catalog/seo/product_url_suffix` and defaults to `.html`). So the `url_path` would by default become `joust-duffle-bag.html`. It also adds the category slugs of the parent categories so the `url_path` might become `bags/joust-duffle-bag.html`. However, the use of `url_path` has been deprecated since early Magento 2.1 versions (see [here](https://github.com/magento/magento2/issues/9113)). If you are running on a recent Magento 2 version, you can safely delete those values by running `DELETE FROM catalog_product_entity_varchar WHERE attribute_id = (SELECT attribute_id FROM eav_attribute WHERE attribute_code = 'url_path' AND entity_type_id = (SELECT entity_type_id FROM eav_entity_type WHERE entity_type_code = 'catalog_product'))`. URL paths are still used in categories though, so don't remove those. From c57a26e33c800e230cf346ac956d8bf8139389d1 Mon Sep 17 00:00:00 2001 From: Aad Mathijssen Date: Wed, 10 Jul 2024 11:45:03 +0200 Subject: [PATCH 69/74] Fix: take 'cids' argument into account The argument was always ignored, because the `$fromRootId` variable always contained a truthy value, after which the `elseif` was never reached. --- src/Console/Command/RegenerateCategoryUrlCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 3a0b2e6..1703cbf 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -132,7 +132,8 @@ public function execute(InputInterface $input, OutputInterface $output): int 'like' => '1/' . $fromRootId . '/%', '=' => '1/' . $fromRootId ]); - } elseif (!empty($categoryIds)) { + } + if (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); } From 9fd6c90f038a0488c0fab00e2ce461c8226a2ac8 Mon Sep 17 00:00:00 2001 From: Gerke Date: Wed, 24 Jul 2024 10:40:53 +0200 Subject: [PATCH 70/74] move error handling for duplicated urls to replce URLS method to prevent failing entire store URL regenration --- src/Service/RegenerateProductUrl.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index d9b53f0..12a1209 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -215,7 +215,17 @@ private function log(string $message): void private function replaceUrls(array &$urls, bool $last = false): int { $this->log(sprintf('replaceUrls%s batch: %d', $last ? ' last' : '', count($urls))); - $this->urlPersist->replace($urls); + + foreach ($urls as $url) { + try { + $this->urlPersist->replace([$url]); + } catch (UrlAlreadyExistsException $e) { + $this->log(sprintf($e->getMessage(). ' Entity id: %d Request path: %s', + $url->getEntityId(), + $url->getRequestPath() + )); + } + } $count = count($urls); $urls = []; From b742038686a029658a55954f4d6d4ce1dbfbad41 Mon Sep 17 00:00:00 2001 From: Gerke Date: Wed, 24 Jul 2024 11:28:07 +0200 Subject: [PATCH 71/74] remove try/catch because error is no longer thrown by replaceUrls method --- src/Service/RegenerateProductUrl.php | 87 ++++++++++------------------ 1 file changed, 30 insertions(+), 57 deletions(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 12a1209..6259c43 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -109,56 +109,29 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v } $newUrls = []; - try { - /** @var Product $product */ - foreach ($collection as $product) { - if ($verbose) { - $this->log( - sprintf( - 'Regenerating urls for %s (%s) in store (%s)', - $product->getSku(), - $product->getId(), - $store->getName() - ) - ); - } - - $product->setStoreId($store->getId()); - - $newUrls = array_merge($newUrls, $this->urlRewriteGenerator->generate($product)); - if (count($newUrls) >= self::BATCH_SIZE) { - $regeneratedForStore += $this->replaceUrls($newUrls); - } + /** @var Product $product */ + foreach ($collection as $product) { + if ($verbose) { + $this->log( + sprintf( + 'Regenerating urls for %s (%s) in store (%s)', + $product->getSku(), + $product->getId(), + $store->getName() + ) + ); } - if (count($newUrls)) { - $regeneratedForStore += $this->replaceUrls($newUrls, true); + $product->setStoreId($store->getId()); + + $newUrls = array_merge($newUrls, $this->urlRewriteGenerator->generate($product)); + if (count($newUrls) >= self::BATCH_SIZE) { + $regeneratedForStore += $this->replaceUrls($newUrls); } - } catch (\Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException $e) { - $this->log(sprintf( - 'Couldn\'t insert duplicate URL rewrites for the following ' . - 'products on store ID %d (current batch failed):' . PHP_EOL . '%s', - $store->getId(), - implode(PHP_EOL, array_map(function ($url) { - return sprintf( - '- Product ID: %d, request path: %s', - $url['entity_id'], - $url['request_path'] - ); - }, $e->getUrls())) - )); - } catch (Exception $e) { - $this->log( - sprintf( - 'Duplicated url for store ID %d, product %d (%s) - %s Generated URLs:' . - PHP_EOL . '%s' . PHP_EOL, - $store->getId(), - $product->getId(), - $product->getSku(), - $e->getMessage(), - implode(PHP_EOL, array_keys($newUrls)) - ) - ); + } + + if (count($newUrls)) { + $regeneratedForStore += $this->replaceUrls($newUrls, true); } $this->log( @@ -204,13 +177,10 @@ private function log(string $message): void } /** - * Add product ulrs - * * @param array $urls - * @param bool $last + * @param bool $last * * @return int - * @throws UrlAlreadyExistsException */ private function replaceUrls(array &$urls, bool $last = false): int { @@ -219,11 +189,14 @@ private function replaceUrls(array &$urls, bool $last = false): int foreach ($urls as $url) { try { $this->urlPersist->replace([$url]); - } catch (UrlAlreadyExistsException $e) { - $this->log(sprintf($e->getMessage(). ' Entity id: %d Request path: %s', - $url->getEntityId(), - $url->getRequestPath() - )); + } catch (\Exception $e) { + $this->log( + sprintf( + $e->getMessage() . ' Entity id: %d Request path: %s', + $url->getEntityId(), + $url->getRequestPath() + ) + ); } } $count = count($urls); @@ -243,7 +216,7 @@ private function replaceUrls(array &$urls, bool $last = false): int */ private function deleteUrls(array &$productIds, StoreInterface $store, bool $last = false): void { - $this->log(sprintf('deleteUrls%s batch: %d', $last ? 'last' : '', count($productIds))); + $this->log(sprintf('deleteUrls%s batch: %d', $last ? ' last' : '', count($productIds))); $this->urlPersist->deleteByData([ UrlRewrite::ENTITY_ID => $productIds, UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, From aafddb776a0c342f4d8db3c61855f6ba01222958 Mon Sep 17 00:00:00 2001 From: tuyennn Date: Wed, 31 Jul 2024 17:53:42 +0700 Subject: [PATCH 72/74] [feat] Add coding standard for current version --- .../Command/AbstractRegenerateCommand.php | 57 +++++++++++-- .../Command/RegenerateCategoryPathCommand.php | 46 ++++++---- .../Command/RegenerateCategoryUrlCommand.php | 85 +++++++++++++------ .../Command/RegenerateCmsPageUrlCommand.php | 50 +++++++---- .../Command/RegenerateProductUrlCommand.php | 8 +- src/Controller/Adminhtml/Action/Product.php | 19 +++-- src/Model/CategoryUrlPathGenerator.php | 2 + src/Service/RegenerateProductUrl.php | 64 ++++++++++---- src/etc/di.xml | 22 +++++ 9 files changed, 257 insertions(+), 96 deletions(-) diff --git a/src/Console/Command/AbstractRegenerateCommand.php b/src/Console/Command/AbstractRegenerateCommand.php index 908041f..8375d8d 100644 --- a/src/Console/Command/AbstractRegenerateCommand.php +++ b/src/Console/Command/AbstractRegenerateCommand.php @@ -17,23 +17,49 @@ abstract class AbstractRegenerateCommand extends Command { + /** + * @var InputInterface + */ protected InputInterface $input; + /** + * @var OutputInterface + */ protected OutputInterface $output; + /** + * @var StoreManagerInterface + */ protected StoreManagerInterface $storeManager; + /** + * @var State + */ protected State $state; + /** + * @var RegenerateProductUrl + */ protected RegenerateProductUrl $regenerateProductUrl; + /** + * @var QuestionHelper + */ protected QuestionHelper $questionHelper; + /** + * Constructor + * + * @param StoreManagerInterface $storeManager + * @param State $state + * @param RegenerateProductUrl $regenerateProductUrl + * @param QuestionHelper $questionHelper + */ public function __construct( StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper ) { $this->storeManager = $storeManager; $this->state = $state; @@ -42,6 +68,9 @@ public function __construct( parent::__construct(); } + /** + * @inheritdoc + */ protected function configure() { $this->addOption( @@ -54,9 +83,12 @@ protected function configure() } /** + * Get chosen stores + * + * @return array * @throws LocalizedException */ - protected function getChosenStores() + protected function getChosenStores(): array { $storeInput = $this->input->getOption('store'); @@ -66,7 +98,7 @@ protected function getChosenStores() $storeId = false; if (is_numeric($storeInput)) { - $storeId = (int) $storeInput; + $storeId = (int)$storeInput; } elseif ($storeInput === 'all') { $storeId = $storeInput; } elseif (is_string($storeInput)) { @@ -87,12 +119,20 @@ protected function getChosenStores() return $stores; } - protected function getAllStores($withDefault = false): array + /** + * Get all stores + * + * @param bool|null $withDefault + * @return array + */ + protected function getAllStores(?bool $withDefault = false): array { return $this->storeManager->getStores($withDefault); } /** + * Get Store ID by code + * * @param string $storeCode * @return null|int * @throws LocalizedException @@ -105,6 +145,9 @@ protected function getStoreIdByCode(string $storeCode): ?int } } - throw new LocalizedException(__('The store that was requested (%1) wasn\'t found. Verify the store and try again.', $storeCode)); + throw new LocalizedException(__( + 'The store that was requested (%1) wasn\'t found. Verify the store and try again.', + $storeCode + )); } } diff --git a/src/Console/Command/RegenerateCategoryPathCommand.php b/src/Console/Command/RegenerateCategoryPathCommand.php index 1fde124..8497379 100644 --- a/src/Console/Command/RegenerateCategoryPathCommand.php +++ b/src/Console/Command/RegenerateCategoryPathCommand.php @@ -5,7 +5,6 @@ namespace Elgentos\RegenerateCatalogUrls\Console\Command; use Elgentos\RegenerateCatalogUrls\Service\RegenerateProductUrl; -use Exception; use Magento\Framework\App\Area; use Magento\Framework\Console\Cli; use Magento\Framework\EntityManager\EventManager; @@ -22,20 +21,38 @@ class RegenerateCategoryPathCommand extends AbstractRegenerateCommand { + /** + * @var CategoryCollectionFactory + */ private CategoryCollectionFactory $categoryCollectionFactory; + /** + * @var EventManager + */ private EventManager $eventManager; - private Emulation $emulation; + /** + * @var ?Emulation + */ + private ?Emulation $emulation = null; + /** + * @param StoreManagerInterface $storeManager + * @param State $state + * @param RegenerateProductUrl $regenerateProductUrl + * @param QuestionHelper $questionHelper + * @param CategoryCollectionFactory $categoryCollectionFactory + * @param EventManager $eventManager + * @param Emulation $emulation + */ public function __construct( - StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper, + StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, CategoryCollectionFactory $categoryCollectionFactory, - EventManager $eventManager, - Emulation\Proxy $emulation + EventManager $eventManager, + Emulation $emulation ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryCollectionFactory = $categoryCollectionFactory; @@ -44,7 +61,7 @@ public function __construct( } /** - * @return void + * @inheritdoc */ protected function configure(): void { @@ -66,12 +83,7 @@ protected function configure(): void } /** - * @param InputInterface $input - * @param OutputInterface $output - * - * @return int - * @throws LocalizedException - * @throws Exception + * @inheritdoc */ public function execute(InputInterface $input, OutputInterface $output) { @@ -92,13 +104,13 @@ public function execute(InputInterface $input, OutputInterface $output) ->addAttributeToSelect(['name', 'url_path', 'url_key', 'path']) ->addAttributeToFilter('level', ['gt' => 1]); - $fromRootOnly = intval($input->getOption('root')) ?? 0; + $fromRootOnly = (int)$input->getOption('root') ?? 0; $categoryIds = $input->getArgument('cids'); if ($fromRootOnly) { //path LIKE '1/rootcategory/%' OR path = '1/rootcategory' $categories->addAttributeToFilter('path', [ 'like' => '1/' . $fromRootOnly . '/%', - '=' => '1/' . $fromRootOnly + '=' => '1/' . $fromRootOnly ]); } elseif (!empty($categoryIds)) { $categories->addAttributeToFilter('entity_id', ['in' => $categoryIds]); diff --git a/src/Console/Command/RegenerateCategoryUrlCommand.php b/src/Console/Command/RegenerateCategoryUrlCommand.php index 1703cbf..8272af8 100644 --- a/src/Console/Command/RegenerateCategoryUrlCommand.php +++ b/src/Console/Command/RegenerateCategoryUrlCommand.php @@ -24,23 +24,45 @@ class RegenerateCategoryUrlCommand extends AbstractRegenerateCommand { + /** + * @var CategoryUrlRewriteGenerator + */ private CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator; + /** + * @var UrlPersistInterface + */ private UrlPersistInterface $urlPersist; + /** + * @var CategoryCollectionFactory + */ private CategoryCollectionFactory $categoryCollectionFactory; - private Emulation $emulation; + /** + * @var ?Emulation + */ + private ?Emulation $emulation = null; + /** + * @param StoreManagerInterface $storeManager + * @param State $state + * @param RegenerateProductUrl $regenerateProductUrl + * @param QuestionHelper $questionHelper + * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator + * @param UrlPersistInterface $urlPersist + * @param CategoryCollectionFactory $categoryCollectionFactory + * @param Emulation $emulation + */ public function __construct( - StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper, + StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator, - UrlPersistInterface $urlPersist, - CategoryCollectionFactory $categoryCollectionFactory, - Emulation\Proxy $emulation + UrlPersistInterface $urlPersist, + CategoryCollectionFactory $categoryCollectionFactory, + Emulation $emulation ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; @@ -50,7 +72,7 @@ public function __construct( } /** - * @return void + * @inheritdoc */ protected function configure(): void { @@ -72,11 +94,7 @@ protected function configure(): void } /** - * @param InputInterface $input - * @param OutputInterface $output - * - * @return int - * @throws LocalizedException + * @inheritdoc */ public function execute(InputInterface $input, OutputInterface $output): int { @@ -93,15 +111,22 @@ public function execute(InputInterface $input, OutputInterface $output): int $stores = $this->getChosenStores(); - $rootIdOption = intval($input->getOption('root')) ?: false; + $rootIdOption = (int)$input->getOption('root') ?: false; foreach ($stores as $storeId) { - $currentRootId = intval($this->storeManager->getGroup($this->storeManager->getStore($storeId)->getStoreGroupId())->getRootCategoryId()); - if($rootIdOption !== false) { + $currentRootId = $this->storeManager->getGroup( + $this->storeManager->getStore($storeId)->getStoreGroupId() + )->getRootCategoryId(); + if ($rootIdOption !== false) { $fromRootId = $rootIdOption; - if($rootIdOption !== $currentRootId) { + if ($rootIdOption !== $currentRootId) { $output->writeln( - sprintf('Skipping store %s because its root category id %s, differs from the passed root category %s', $storeId, $currentRootId, $fromRootId) + sprintf( + 'Skipping store %s because its root category id %s, differs from the passed root category %s', //phpcs:ignore Generic.Files.LineLength.TooLong + $storeId, + $currentRootId, + $fromRootId + ) ); continue; } @@ -113,9 +138,12 @@ public function execute(InputInterface $input, OutputInterface $output): int sprintf('Processing store %s...', $storeId) ); - $rootCategory = $this->categoryCollectionFactory->create()->addAttributeToFilter('entity_id', $fromRootId)->addAttributeToSelect("name")->getFirstItem(); - if(!$rootCategory->getId()) { - throw new \Exception(sprintf("Root category with ID %d, was not found.", $fromRootId)); + $rootCategory = $this->categoryCollectionFactory->create()->addAttributeToFilter( + 'entity_id', + $fromRootId + )->addAttributeToSelect("name")->getFirstItem(); + if (!$rootCategory->getId()) { + throw new Exception(sprintf("Root category with ID %d, was not found.", $fromRootId)); } $this->emulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); @@ -124,13 +152,12 @@ public function execute(InputInterface $input, OutputInterface $output): int ->addAttributeToSelect(['name', 'url_path', 'url_key', 'path']) ->addAttributeToFilter('level', ['gt' => 1]); - $categoryIds = $input->getArgument('cids'); if ($fromRootId) { //path LIKE '1/rootcategory/%' OR path = '1/rootcategory' $categories->addAttributeToFilter('path', [ 'like' => '1/' . $fromRootId . '/%', - '=' => '1/' . $fromRootId + '=' => '1/' . $fromRootId ]); } if (!empty($categoryIds)) { @@ -139,12 +166,14 @@ public function execute(InputInterface $input, OutputInterface $output): int foreach ($categories as $category) { $output->writeln( - sprintf('Regenerating urls for %s (%s)', - implode('/', [ + sprintf( + 'Regenerating urls for %s (%s)', + implode('/', [ $rootCategory->getName(), - ...array_map(fn($category) => $category->getName(), $category->getParentCategories()) + ...array_map(fn ($category) => $category->getName(), $category->getParentCategories()) ]), - $category->getId()) + $category->getId() + ) ); $this->urlPersist->deleteByData( diff --git a/src/Console/Command/RegenerateCmsPageUrlCommand.php b/src/Console/Command/RegenerateCmsPageUrlCommand.php index 06380ce..b37b947 100644 --- a/src/Console/Command/RegenerateCmsPageUrlCommand.php +++ b/src/Console/Command/RegenerateCmsPageUrlCommand.php @@ -24,33 +24,55 @@ class RegenerateCmsPageUrlCommand extends AbstractRegenerateCommand { - private Emulation $emulation; + /** + * @var ?Emulation + */ + private ?Emulation $emulation = null; + /** + * @var PageCollectionFactory + */ private PageCollectionFactory $pageCollectionFactory; + /** + * @var UrlPersistInterface + */ private UrlPersistInterface $urlPersist; + /** + * @var CmsPageUrlRewriteGenerator + */ private CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator; + /** + * @param StoreManagerInterface $storeManager + * @param State $state + * @param RegenerateProductUrl $regenerateProductUrl + * @param QuestionHelper $questionHelper + * @param PageCollectionFactory $pageCollectionFactory + * @param UrlPersistInterface $urlPersist + * @param CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator + * @param Emulation $emulation + */ public function __construct( - StoreManagerInterface $storeManager, - State $state, - RegenerateProductUrl $regenerateProductUrl, - QuestionHelper $questionHelper, - Emulation\Proxy $emulation, - PageCollectionFactory $pageCollectionFactory, - UrlPersistInterface $urlPersist, - CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator + StoreManagerInterface $storeManager, + State $state, + RegenerateProductUrl $regenerateProductUrl, + QuestionHelper $questionHelper, + PageCollectionFactory $pageCollectionFactory, + UrlPersistInterface $urlPersist, + CmsPageUrlRewriteGenerator $cmsPageUrlRewriteGenerator, + Emulation $emulation ) { parent::__construct($storeManager, $state, $regenerateProductUrl, $questionHelper); - $this->emulation = $emulation; $this->pageCollectionFactory = $pageCollectionFactory; $this->urlPersist = $urlPersist; $this->cmsPageUrlRewriteGenerator = $cmsPageUrlRewriteGenerator; + $this->emulation = $emulation; } /** - * @return void + * @inheritdoc */ protected function configure() { @@ -66,11 +88,7 @@ protected function configure() } /** - * @param InputInterface $input - * @param OutputInterface $output - * - * @return int - * @throws LocalizedException + * @inheritdoc */ protected function execute(InputInterface $input, OutputInterface $output): int { diff --git a/src/Console/Command/RegenerateProductUrlCommand.php b/src/Console/Command/RegenerateProductUrlCommand.php index f6df8ce..755b26e 100644 --- a/src/Console/Command/RegenerateProductUrlCommand.php +++ b/src/Console/Command/RegenerateProductUrlCommand.php @@ -13,7 +13,7 @@ class RegenerateProductUrlCommand extends AbstractRegenerateCommand { /** - * @return void + * @inheritdoc */ protected function configure(): void { @@ -29,11 +29,7 @@ protected function configure(): void } /** - * @param InputInterface $input - * @param OutputInterface $output - * - * @return int - * @throws LocalizedException + * @inheritdoc */ public function execute(InputInterface $input, OutputInterface $output) { diff --git a/src/Controller/Adminhtml/Action/Product.php b/src/Controller/Adminhtml/Action/Product.php index 28efac6..a0a4d7b 100644 --- a/src/Controller/Adminhtml/Action/Product.php +++ b/src/Controller/Adminhtml/Action/Product.php @@ -9,18 +9,25 @@ use Magento\Backend\App\Action; use Magento\Backend\App\Action\Context; use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; -use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\Result\Redirect; -use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Ui\Component\MassAction\Filter; class Product extends Action { + /** + * @var RegenerateProductUrl + */ private RegenerateProductUrl $regenerateProductUrl; + /** + * @var Filter + */ private Filter $filter; + /** + * @var CollectionFactory + */ private CollectionFactory $collectionFactory; /** @@ -32,10 +39,10 @@ class Product extends Action * @param Context $context */ public function __construct( - CollectionFactory $collectionFactory, - Filter $filter, + CollectionFactory $collectionFactory, + Filter $filter, RegenerateProductUrl $regenerateProductUrl, - Context $context + Context $context ) { $this->collectionFactory = $collectionFactory; $this->filter = $filter; @@ -84,6 +91,8 @@ public function execute(): Redirect } /** + * Get selected Product IDs + * * @return array * @throws LocalizedException */ diff --git a/src/Model/CategoryUrlPathGenerator.php b/src/Model/CategoryUrlPathGenerator.php index c20244c..e9659ff 100644 --- a/src/Model/CategoryUrlPathGenerator.php +++ b/src/Model/CategoryUrlPathGenerator.php @@ -9,6 +9,8 @@ class CategoryUrlPathGenerator extends \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator { /** + * Determine whether generation url path for parent needed + * * @param Category $category * * @return bool diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 6259c43..791e7ee 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -5,7 +5,6 @@ namespace Elgentos\RegenerateCatalogUrls\Service; use Exception; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\Product\Visibility; @@ -13,27 +12,47 @@ use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Api\Data\StoreInterface; -use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; -use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Symfony\Component\Console\Output\OutputInterface; class RegenerateProductUrl { + /**#@+ + * Constants + */ public const BATCH_SIZE = 500; + /**#@-*/ + /** + * @var OutputInterface|null + */ private ?OutputInterface $output = null; + /** + * @var CollectionFactory + */ private CollectionFactory $collectionFactory; - private ProductUrlRewriteGenerator $urlRewriteGenerator; + /** + * @var ?ProductUrlRewriteGenerator + */ + private ?ProductUrlRewriteGenerator $urlRewriteGenerator = null; + /** + * @var UrlPersistInterface + */ private UrlPersistInterface $urlPersist; + /** + * @var StoreManagerInterface + */ private StoreManagerInterface $storeManager; + /** + * @var int + */ private int $regeneratedCount = 0; /** @@ -46,17 +65,19 @@ class RegenerateProductUrl */ public function __construct( CollectionFactory $collectionFactory, - ProductUrlRewriteGenerator\Proxy $urlRewriteGenerator, + ProductUrlRewriteGenerator $urlRewriteGenerator, UrlPersistInterface $urlPersist, StoreManagerInterface $storeManager ) { - $this->collectionFactory = $collectionFactory; + $this->collectionFactory = $collectionFactory; $this->urlRewriteGenerator = $urlRewriteGenerator; - $this->urlPersist = $urlPersist; - $this->storeManager = $storeManager; + $this->urlPersist = $urlPersist; + $this->storeManager = $storeManager; } /** + * Process + * * @param int[]|null $productIds * @param int|null $storeId * @param bool $verbose @@ -68,7 +89,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v { $this->regeneratedCount = 0; - $stores = !is_null($storeId) + $stores = null !== $storeId ? [$this->storeManager->getStore($storeId)] : $this->storeManager->getStores(false); @@ -85,7 +106,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v ->addFieldToFilter('status', ['eq' => Status::STATUS_ENABLED]) ->addFieldToFilter('visibility', ['gt' => Visibility::VISIBILITY_NOT_VISIBLE]); - if (is_null($productIds) || (is_array($productIds) && empty($productIds))) { + if ($productIds == null || (is_array($productIds) && empty($productIds))) { $productIds = $collection->getAllIds(); } $collection->addIdFilter($productIds); @@ -124,6 +145,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v $product->setStoreId($store->getId()); + //phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge $newUrls = array_merge($newUrls, $this->urlRewriteGenerator->generate($product)); if (count($newUrls) >= self::BATCH_SIZE) { $regeneratedForStore += $this->replaceUrls($newUrls); @@ -147,6 +169,8 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v } /** + * Generate output + * * @param OutputInterface $output * * @return void @@ -157,6 +181,8 @@ public function setOutput(OutputInterface $output): void } /** + * Generate count + * * @return int */ public function getRegeneratedCount(): int @@ -165,6 +191,8 @@ public function getRegeneratedCount(): int } /** + * Log output + * * @param string $message * * @return void @@ -177,8 +205,10 @@ private function log(string $message): void } /** + * Replace urls + * * @param array $urls - * @param bool $last + * @param bool $last * * @return int */ @@ -189,7 +219,7 @@ private function replaceUrls(array &$urls, bool $last = false): int foreach ($urls as $url) { try { $this->urlPersist->replace([$url]); - } catch (\Exception $e) { + } catch (Exception $e) { $this->log( sprintf( $e->getMessage() . ' Entity id: %d Request path: %s', @@ -200,7 +230,7 @@ private function replaceUrls(array &$urls, bool $last = false): int } } $count = count($urls); - $urls = []; + $urls = []; return $count; } @@ -214,14 +244,14 @@ private function replaceUrls(array &$urls, bool $last = false): int * * @return void */ - private function deleteUrls(array &$productIds, StoreInterface $store, bool $last = false): void + private function deleteUrls(array $productIds, StoreInterface $store, bool $last = false): void { $this->log(sprintf('deleteUrls%s batch: %d', $last ? ' last' : '', count($productIds))); $this->urlPersist->deleteByData([ - UrlRewrite::ENTITY_ID => $productIds, - UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::ENTITY_ID => $productIds, + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, UrlRewrite::REDIRECT_TYPE => 0, - UrlRewrite::STORE_ID => $store->getId() + UrlRewrite::STORE_ID => $store->getId() ]); } } diff --git a/src/etc/di.xml b/src/etc/di.xml index d473cc5..cfe69e6 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -11,5 +11,27 @@ + + + + Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator\Proxy + + + + + Magento\Store\Model\App\Emulation\Proxy + + + + + Magento\Store\Model\App\Emulation\Proxy + + + + + Magento\Store\Model\App\Emulation\Proxy + + + From cda87fc8b79615718cf81eb919a9725c73b7e61c Mon Sep 17 00:00:00 2001 From: tuyennn Date: Thu, 8 Aug 2024 17:30:37 +0700 Subject: [PATCH 73/74] [feat] Move logging detect Duplicated url on current product to verbose mode --- src/Service/RegenerateProductUrl.php | 38 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Service/RegenerateProductUrl.php b/src/Service/RegenerateProductUrl.php index 791e7ee..c7f2d17 100644 --- a/src/Service/RegenerateProductUrl.php +++ b/src/Service/RegenerateProductUrl.php @@ -55,6 +55,11 @@ class RegenerateProductUrl */ private int $regeneratedCount = 0; + /** + * @var bool + */ + private bool $isVerboseMode = false; + /** * Constructor. * @@ -87,11 +92,12 @@ public function __construct( */ public function execute(?array $productIds = null, ?int $storeId = null, bool $verbose = false): void { + $this->isVerboseMode = $verbose; $this->regeneratedCount = 0; $stores = null !== $storeId ? [$this->storeManager->getStore($storeId)] - : $this->storeManager->getStores(false); + : $this->storeManager->getStores(); foreach ($stores as $store) { $regeneratedForStore = 0; @@ -132,7 +138,7 @@ public function execute(?array $productIds = null, ?int $storeId = null, bool $v $newUrls = []; /** @var Product $product */ foreach ($collection as $product) { - if ($verbose) { + if ($this->isVerboseMode) { $this->log( sprintf( 'Regenerating urls for %s (%s) in store (%s)', @@ -216,19 +222,27 @@ private function replaceUrls(array &$urls, bool $last = false): int { $this->log(sprintf('replaceUrls%s batch: %d', $last ? ' last' : '', count($urls))); - foreach ($urls as $url) { + if ($this->isVerboseMode) { + foreach ($urls as $url) { + try { + $this->urlPersist->replace([$url]); + } catch (Exception $e) { + $this->log( + sprintf( + $e->getMessage() . ' Entity id: %d Request path: %s', + $url->getEntityId(), + $url->getRequestPath() + ) + ); + } + } + } else { try { - $this->urlPersist->replace([$url]); - } catch (Exception $e) { - $this->log( - sprintf( - $e->getMessage() . ' Entity id: %d Request path: %s', - $url->getEntityId(), - $url->getRequestPath() - ) - ); + $this->urlPersist->replace($urls); + } catch (Exception $e) {//phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch } } + $count = count($urls); $urls = []; From 3e470251bcbbefa3b7459c9b80e6aa2a2048d17e Mon Sep 17 00:00:00 2001 From: tuyennn Date: Thu, 8 Aug 2024 17:39:51 +0700 Subject: [PATCH 74/74] [feat] Update readme for the verbose option purpose --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b7b21e8..440eea4 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Arguments: Options: --store (-s) Use a specific store (store Id, store code or 'all') --root (-r) Regenerate for root category and its children, ignoring cids. + --verbose (-v) Verbose mode to display the errors. Eg: duplicated product urls --help (-h) Display this help message ```