diff --git a/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php b/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php index 84741bbe..ee8dc328 100644 --- a/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php +++ b/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php @@ -76,6 +76,7 @@ class ManageRedirectRulesCommand extends Command $rulesToSave = $this->processRules($shortUrl, $io, $this->ruleService->rulesForShortUrl($shortUrl)); if ($rulesToSave !== null) { $this->ruleService->saveRulesForShortUrl($shortUrl, $rulesToSave); + $io->success('Rules properly saved'); } return ExitCode::EXIT_SUCCESS; diff --git a/module/Core/src/RedirectRule/Entity/ShortUrlRedirectRule.php b/module/Core/src/RedirectRule/Entity/ShortUrlRedirectRule.php index 57ad7092..5f76d998 100644 --- a/module/Core/src/RedirectRule/Entity/ShortUrlRedirectRule.php +++ b/module/Core/src/RedirectRule/Entity/ShortUrlRedirectRule.php @@ -25,6 +25,16 @@ class ShortUrlRedirectRule extends AbstractEntity implements JsonSerializable ) { } + public function withPriority(int $newPriority): self + { + return new self( + $this->shortUrl, + $newPriority, + $this->longUrl, + $this->conditions, + ); + } + /** * Tells if this condition matches provided request */ diff --git a/module/Core/src/RedirectRule/ShortUrlRedirectRuleService.php b/module/Core/src/RedirectRule/ShortUrlRedirectRuleService.php index 40bbb0de..01ba0a8f 100644 --- a/module/Core/src/RedirectRule/ShortUrlRedirectRuleService.php +++ b/module/Core/src/RedirectRule/ShortUrlRedirectRuleService.php @@ -11,6 +11,7 @@ use Shlinkio\Shlink\Core\RedirectRule\Model\Validation\RedirectRulesInputFilter; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use function array_map; +use function Shlinkio\Shlink\Core\ArrayUtils\map; readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServiceInterface { @@ -49,7 +50,7 @@ readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServic $rules[] = $rule; } - $this->saveRulesForShortUrl($shortUrl, $rules); + $this->doSetRulesForShortUrl($shortUrl, $rules); return $rules; } @@ -57,6 +58,23 @@ readonly class ShortUrlRedirectRuleService implements ShortUrlRedirectRuleServic * @param ShortUrlRedirectRule[] $rules */ public function saveRulesForShortUrl(ShortUrl $shortUrl, array $rules): void + { + $normalizedAndDetachedRules = map($rules, function (ShortUrlRedirectRule $rule, int|string|float $priority) { + // Make sure all rules and conditions are detached so that the EM considers them new. + $rule->mapConditions(fn (RedirectCondition $cond) => $this->em->detach($cond)); + $this->em->detach($rule); + + // Normalize priorities so that they are sequential + return $rule->withPriority(((int) $priority) + 1); + }); + + $this->doSetRulesForShortUrl($shortUrl, $normalizedAndDetachedRules); + } + + /** + * @param ShortUrlRedirectRule[] $rules + */ + public function doSetRulesForShortUrl(ShortUrl $shortUrl, array $rules): void { $this->em->wrapInTransaction(function () use ($shortUrl, $rules): void { // First, delete existing rules for the short URL diff --git a/module/Core/test/RedirectRule/Entity/ShortUrlRedirectRuleTest.php b/module/Core/test/RedirectRule/Entity/ShortUrlRedirectRuleTest.php index 15df256f..d61bc6fa 100644 --- a/module/Core/test/RedirectRule/Entity/ShortUrlRedirectRuleTest.php +++ b/module/Core/test/RedirectRule/Entity/ShortUrlRedirectRuleTest.php @@ -6,7 +6,6 @@ use Doctrine\Common\Collections\ArrayCollection; use Laminas\Diactoros\ServerRequestFactory; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; -use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition; use Shlinkio\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRule;