setono / sylius-redirect-plugin
Sylius plugin for managing redirects
Package info
github.com/Setono/SyliusRedirectPlugin
Type:sylius-plugin
pkg:composer/setono/sylius-redirect-plugin
Fund package maintenance!
Requires
- php: >=8.2
- doctrine/collections: ^2.2
- doctrine/persistence: ^1.3 || ^2.1 || ^3.0 || ^4.0
- league/uri: ^6.0 || ^7.5
- league/uri-components: ^2.3 || ^7.5
- ocramius/doctrine-batch-utils: ^2.4
- psr/log: ^1.1 || ^2.0 || ^3.0
- setono/composite-compiler-pass: ^1.2
- setono/doctrine-orm-trait: ^1.2
- sylius/channel: ^2.0
- sylius/channel-bundle: ^2.0
- sylius/core-bundle: ^2.0
- sylius/product: ^2.0
- sylius/resource-bundle: ^1.12
- sylius/taxonomy: ^2.0
- sylius/ui-bundle: ^2.0
- symfony/config: ^6.4 || ^7.1
- symfony/console: ^6.4 || ^7.1
- symfony/dependency-injection: ^6.4 || ^7.1
- symfony/event-dispatcher: ^6.4 || ^7.1
- symfony/form: ^6.4 || ^7.1
- symfony/http-foundation: ^6.4 || ^7.1
- symfony/http-kernel: ^6.4 || ^7.1
- symfony/routing: ^6.4 || ^7.1
- symfony/validator: ^6.4 || ^7.1
- twig/twig: ^3.0
- webmozart/assert: ^1.11
Requires (Dev)
- api-platform/core: ^4.0.3
- dama/doctrine-test-bundle: ^8.6
- lexik/jwt-authentication-bundle: ^3.1
- php-http/message-factory: ^1.1
- setono/sylius-plugin: ^2.1
- sylius/sylius: ~2.2.5
- symfony/debug-bundle: ^6.4 || ^7.1
- symfony/dotenv: ^6.4 || ^7.1
- symfony/intl: ^6.4 || ^7.1
- symfony/web-profiler-bundle: ^6.4 || ^7.1
- symfony/webpack-encore-bundle: ^2.2
This package is auto-updated.
Last update: 2026-05-04 09:13:54 UTC
README
Gives you the ability to manage redirects in your Sylius shop.
Installation
Step 1: Download the plugin
Open a command console, enter your project directory and execute the following command to download the latest stable version of this plugin:
composer require setono/sylius-redirect-plugin
This command requires you to have Composer installed globally, as explained in the installation chapter of the Composer documentation.
Step 2: Enable the plugin
Then, enable the plugin by adding it to the list of registered plugins/bundles
in the config/bundles.php file of your project:
<?php return [ // ... // Add before SyliusGridBundle Setono\SyliusRedirectPlugin\SetonoSyliusRedirectPlugin::class => ['all' => true], Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true], // ... ];
It is IMPORTANT to add the plugin before the grid bundle, otherwise you will get an exception saying You have requested a non-existent parameter "setono_sylius_redirect.model.redirect.class".
Step 3: Import routes
# config/routes/setono_sylius_redirect.yaml setono_sylius_redirect: resource: "@SetonoSyliusRedirectPlugin/config/routes.yaml"
The imported file already prefixes the admin routes with /%sylius_admin.path_name%, so no extra prefix: is needed.
Step 4: Update database
Use Doctrine migrations to create a migration file and update the database.
bin/console doctrine:migrations:diff bin/console doctrine:migrations:migrate
Step 5: Install assets
bin/console assets:install
What it does
This plugin allows you to create new redirects.
Under the new menu entry Redirects under Configuration, you can manage redirects.
Redirection entry
An entry is composed by:
- Source URL — must start with
/(relative to your shop) - Destination URL — relative or absolute (you can redirect to another host)
- Permanent / Temporary — drives the HTTP status code (301 or 302)
- Enabled — disabled rows are ignored at request time
- Only when 404 — only fire the redirect when the request would otherwise 404, useful for cleaning up dead inbound links
- Keep query string — append the inbound query string to the destination (defaults to
true) - Channels — restrict the redirect to one or more channels; leave empty to match every channel
Security
There is a built-in safeguard when creating/modifying a redirect that prevents infinite loops. It walks the redirect chain recursively and refuses to save a redirect that would cycle.
A second safeguard prevents two enabled redirects from sharing the same
source URL within the same channel scope (or globally, if neither uses
channels) — otherwise the runtime would have to pick between two
inconsistent destinations. The admin form additionally probes the AJAX
endpoint /admin/ajax/redirects/check-source while you type and surfaces
a warning with a link to the conflicting redirect, so you can spot the
collision before submitting.
Disabling non-404 redirects
By default the plugin checks for a matching redirect on every request, so a
only404 = false redirect can intercept the request before the controller
runs. That convenience comes at the cost of one database query per request,
even on installations that exclusively use only404 = true redirects.
If you only ever use 404-only redirects, disable the always-on listener:
# config/packages/setono_sylius_redirect.yaml setono_sylius_redirect: allow_non_404_redirects: false # default: true
When false, the plugin no longer registers its KernelEvents::REQUEST
listener — there is zero per-request overhead. Redirects continue to work
on the 404 path, which is all you need when every redirect has
only404 = true. The admin form drops the Only when 404 checkbox and a
class-level constraint on Redirect rejects any save (form, API, CLI,
fixtures) where only404 = false, so you can't accidentally create a row
that would never fire. Pre-existing only404 = false rows go dormant until
you either flip the option back, delete them, or set their only404 to
true.
Pruning unused redirects
The bundled setono:sylius-redirect:prune command deletes redirects that
have not been accessed in the last N days. Configure the threshold under
remove_after:
# config/packages/setono_sylius_redirect.yaml setono_sylius_redirect: remove_after: 90 # days; 0 (the default) disables pruning
Then schedule the command (cron, messenger cron, deploy hook, …):
bin/console setono:sylius-redirect:prune
Pruning iterates eligible redirects in batches via
ocramius/doctrine-batch-utils, so it stays
memory-safe even on tables with millions of rows.
Automatic redirects on slug changes
When you opt in, the plugin creates a redirect every time an admin renames the slug of a configured Sylius resource. Opt-in is per resource alias and defaults to off everywhere:
# config/packages/setono_sylius_redirect.yaml setono_sylius_redirect: automatic_redirects: sylius.product: true sylius.taxon: true
Aliases must implement
Sylius\Component\Resource\Model\SlugAwareInterface. Unknown aliases or
non-slug-aware models fail container compilation with a clear error.
Each automatic redirect is created with these fixed defaults:
permanent = true, only404 = true, no channel scope, enabled = true.
only404 = true means the redirect kicks in only when the request would
otherwise 404 — so if the slug is later rolled back to its original value,
the entry self-heals without you having to clean up. The plugin also
collapses chains: if you rename a → b and then b → c, the earlier
a → b redirect is replaced with a → c.
The trigger is admin-only: only edits that flow through Sylius's
ResourceController::updateAction create redirects. API edits, fixture
loads, CLI scripts, and direct repository writes do not.
Adding coverage for your own resources
To redirect on slug changes for a custom resource, register a service
implementing Setono\SyliusRedirectPlugin\UrlResolver\AutomaticRedirectUrlResolverInterface
and tag it setono_sylius_redirect.automatic_redirect_url_resolver:
final class BlogPostAutomaticRedirectUrlResolver implements AutomaticRedirectUrlResolverInterface { public function __construct(private readonly UrlGeneratorInterface $urlGenerator) { } public function supports(string $class): bool { return is_a($class, BlogPostInterface::class, true); } public function resolve(object $resource, string $slug, string $locale): string { return $this->urlGenerator->generate('app_blog_post_show', [ 'slug' => $slug, '_locale' => $locale, ]); } }
...then enable the alias under automatic_redirects: app.blog_post: true.
Development
This project uses OpenSpec to plan and track non-trivial features. Active proposals live under openspec/changes/<name>/; the current accepted specs live under openspec/specs/<capability>/; completed proposals move to openspec/changes/archive/. If you're contributing a sizeable change, write the proposal / design / spec / tasks artifacts there first so the why and the contract are clear before any code lands.