Exponential 5.x Kernel 7.5.40 Exponential New Stack (Symfony-based) Kernel — PHP Class Reference

EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php
Go to the documentation of this file.
1 <?php
2
7 namespace eZ\Bundle\EzPublishCoreBundle\Command;
8
9 use Exception;
10 use eZ\Publish\API\Repository\Repository;
11 use eZ\Publish\API\Repository\Values\Content\Language;
12 use eZ\Publish\API\Repository\Values\Content\Location;
13 use Psr\Log\LoggerInterface;
14 use Psr\Log\NullLogger;
15 use Symfony\Component\Console\Command\Command;
16 use Symfony\Component\Console\Helper\ProgressBar;
17 use Symfony\Component\Console\Input\InputInterface;
18 use Symfony\Component\Console\Input\InputOption;
19 use Symfony\Component\Console\Output\OutputInterface;
20 use Symfony\Component\Console\Question\ConfirmationQuestion;
21
26 class RegenerateUrlAliasesCommand extends Command
27 {
28 const DEFAULT_ITERATION_COUNT = 1000;
29
30 const BEFORE_RUNNING_HINTS = <<<EOT
31 <error>Before you continue:</error>
32 - Make sure to back up your database.
33 - If you are regenerating URL aliases for all Locations, take installation offline, during the script execution the database should not be modified.
34 - Run this command without memory limit, i.e. processing of 300k Locations can take up to 1 GB of RAM.
35 - Run this command in production environment using <info>--env=prod</info>
36 - Manually clear HTTP cache after running this command.
37 EOT;
38
40 private $repository;
41
43 private $logger;
44
49 public function __construct(Repository $repository, LoggerInterface $logger = null)
50 {
51 parent::__construct();
52
53 $this->repository = $repository;
54 $this->logger = null !== $logger ? $logger : new NullLogger();
55 }
56
60 protected function configure()
61 {
62 $beforeRunningHints = self::BEFORE_RUNNING_HINTS;
63 $this
64 ->setName('ezplatform:urls:regenerate-aliases')
65 ->setDescription(
66 'Regenerates Location URL aliases (autogenerated) and cleans up custom Location ' .
67 'and global URL aliases stored in the Legacy Storage Engine'
68 )
69 ->addOption(
70 'iteration-count',
71 'c',
72 InputOption::VALUE_OPTIONAL,
73 'Number of Locations fetched into memory and processed at once',
74 self::DEFAULT_ITERATION_COUNT
75 )->addOption(
76 'location-id',
77 null,
78 InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
79 'Only Locations with provided id\'s will have URL aliases regenerated',
80 []
81 )->setHelp(
82 <<<EOT
83 {$beforeRunningHints}
84
85 The command <info>%command.name%</info> regenerates URL aliases for Locations and cleans up
86 corrupted URL aliases (pointing to non-existent Locations).
87 Existing aliases are archived (will redirect to the new ones).
88
89 Note: This script can potentially run for a very long time.
90
91 Due to performance issues the command does not send any Signals.
92
93 <comment>HTTP cache needs to be cleared manually after executing this command.</comment>
94
95 EOT
96 );
97 }
98
104 protected function execute(InputInterface $input, OutputInterface $output)
105 {
106 $iterationCount = (int)$input->getOption('iteration-count');
107 $locationIds = $input->getOption('location-id');
108
109 if (!empty($locationIds)) {
110 $locationIds = $this->getFilteredLocationList($locationIds);
111 $locationsCount = count($locationIds);
112 } else {
113 $locationsCount = $this->repository->sudo(
114 function (Repository $repository) {
115 return $repository->getLocationService()->getAllLocationsCount();
116 }
117 );
118 }
119
120 if ($locationsCount === 0) {
121 $output->writeln('<info>No location was found. Exiting.</info>');
122
123 return;
124 }
125
126 $helper = $this->getHelper('question');
127 $question = new ConfirmationQuestion(
128 sprintf(
129 "<info>Found %d Locations.</info>\n%s\n<info>Do you want to proceed? [y/N] </info>",
130 $locationsCount,
131 self::BEFORE_RUNNING_HINTS
132 ),
133 false
134 );
135 if (!$helper->ask($input, $output, $question)) {
136 return;
137 }
138
139 $this->regenerateSystemUrlAliases($output, $locationsCount, $locationIds, $iterationCount);
140
141 $output->writeln('<info>Cleaning up corrupted URL aliases...</info>');
142 $corruptedAliasesCount = $this->repository->sudo(
143 function (Repository $repository) {
144 return $repository->getURLAliasService()->deleteCorruptedUrlAliases();
145 }
146 );
147 $output->writeln("<info>Done. Deleted {$corruptedAliasesCount} entries.</info>");
148 $output->writeln('<comment>Make sure to clear HTTP cache afterwards.</comment>');
149 }
150
159 protected function getProgressBar($maxSteps, OutputInterface $output)
160 {
161 $progressBar = new ProgressBar($output, $maxSteps);
162 $progressBar->setFormat(
163 ' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%'
164 );
165
166 return $progressBar;
167 }
168
175 private function processLocations(array $locations, ProgressBar $progressBar)
176 {
177 $contentList = $this->repository->sudo(
178 function (Repository $repository) use ($locations) {
179 $contentInfoList = array_map(
180 function (Location $location) {
181 return $location->contentInfo;
182 },
183 $locations
184 );
185
186 // load Content list in all languages
187 return $repository->getContentService()->loadContentListByContentInfo(
188 $contentInfoList,
189 Language::ALL,
190 false
191 );
192 }
193 );
194 foreach ($locations as $location) {
195 try {
196 // ignore missing Content items
197 if (!isset($contentList[$location->contentId])) {
198 continue;
199 }
200
201 $this->repository->sudo(
202 function (Repository $repository) use ($location) {
203 $repository->getURLAliasService()->refreshSystemUrlAliasesForLocation(
204 $location
205 );
206 }
207 );
208 } catch (Exception $e) {
209 $contentInfo = $location->getContentInfo();
210 $msg = sprintf(
211 'Failed processing location %d - [%d] %s (%s: %s)',
212 $location->id,
213 $contentInfo->id,
214 $contentInfo->name,
215 get_class($e),
216 $e->getMessage()
217 );
218 $this->logger->warning($msg);
219 // in debug mode log full exception with a trace
220 $this->logger->debug($e);
221 } finally {
222 $progressBar->advance(1);
223 }
224 }
225 }
226
235 private function loadAllLocations(int $offset, int $iterationCount): array
236 {
237 return $this->repository->sudo(
238 function (Repository $repository) use ($offset, $iterationCount) {
239 return $repository->getLocationService()->loadAllLocations($offset, $iterationCount);
240 }
241 );
242 }
243
253 private function loadSpecificLocations(array $locationIds, int $offset, int $iterationCount): array
254 {
255 $locationIds = array_slice($locationIds, $offset, $iterationCount);
256
257 return $this->repository->sudo(
258 function (Repository $repository) use ($locationIds) {
259 return $repository->getLocationService()->loadLocationList($locationIds);
260 }
261 );
262 }
263
271 private function getFilteredLocationList(array $locationIds): array
272 {
273 $locations = $this->repository->sudo(
274 function (Repository $repository) use ($locationIds) {
275 $locationService = $repository->getLocationService();
276
277 return $locationService->loadLocationList($locationIds);
278 }
279 );
280
281 return array_map(
282 function (Location $location) {
283 return $location->id;
284 },
285 $locations
286 );
287 }
288
295 private function regenerateSystemUrlAliases(
296 OutputInterface $output,
297 int $locationsCount,
298 array $locationIds,
299 int $iterationCount
300 ): void {
301 $output->writeln('Regenerating System URL aliases...');
302
303 $progressBar = $this->getProgressBar($locationsCount, $output);
304 $progressBar->start();
305
306 for ($offset = 0; $offset <= $locationsCount; $offset += $iterationCount) {
307 gc_disable();
308 if (!empty($locationIds)) {
309 $locations = $this->loadSpecificLocations($locationIds, $offset, $iterationCount);
310 } else {
311 $locations = $this->loadAllLocations($offset, $iterationCount);
312 }
313 $this->processLocations($locations, $progressBar);
314 gc_enable();
315 }
316 $progressBar->finish();
317 $output->writeln('');
318 $output->writeln('<info>Done.</info>');
319 }
320 }
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand
The ezplatform:urls:regenerate-aliases Symfony command implementation.
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:27
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\regenerateSystemUrlAliases
regenerateSystemUrlAliases(OutputInterface $output, int $locationsCount, array $locationIds, int $iterationCount)
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:295
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\DEFAULT_ITERATION_COUNT
const DEFAULT_ITERATION_COUNT
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:28
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\$repository
$repository
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:40
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\configure
configure()
{}
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:60
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\BEFORE_RUNNING_HINTS
const BEFORE_RUNNING_HINTS
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:30
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\getProgressBar
getProgressBar($maxSteps, OutputInterface $output)
Return configured progress bar helper.
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:159
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\loadAllLocations
loadAllLocations(int $offset, int $iterationCount)
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:235
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\execute
execute(InputInterface $input, OutputInterface $output)
Regenerate URL aliases.
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:104
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\processLocations
processLocations(array $locations, ProgressBar $progressBar)
Process single results page of fetched Locations.
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:175
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\loadSpecificLocations
loadSpecificLocations(array $locationIds, int $offset, int $iterationCount)
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:253
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\__construct
__construct(Repository $repository, LoggerInterface $logger=null)
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:49
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\$logger
$logger
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:43
eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand\getFilteredLocationList
getFilteredLocationList(array $locationIds)
Definition: EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php:271
eZ\Publish\API\Repository\Values\Content\Language
Definition: API/Repository/Values/Content/Language.php:20
eZ\Publish\API\Repository\Values\Content\Language\ALL
const ALL
Constant for use in API's to specify that you want to load all languages.
Definition: API/Repository/Values/Content/Language.php:24
eZ\Publish\API\Repository\Values\Content\Location
Definition: API/Repository/Values/Content/Location.php:32
eZ\Publish\API\Repository\Values\URL\Query\SortClause\URL
Definition: API/Repository/Values/URL/Query/SortClause/URL.php:12
eZ\Publish\API\Repository\Repository
Repository interface.
Definition: API/Repository/Repository.php:16
eZ\Bundle\EzPublishCoreBundle\Command
Definition: CheckURLsCommand.php:7
Copyright © 1998 – 2026 7x. All rights reserved. Licensed under the GNU GPLv2+.