opifer/redirect-bundle
Opifer RedirectBundle is a Symfony package for creating and managing URL redirects. It lets applications map old paths to new destinations, handle redirect responses, and avoid broken links when content moves. Useful for SEO-friendly URL changes and legacy route support.
This is a read-only subtree split of the Opifer CMS monorepo. The last release is 0.1.4, a patch that swaps the bundle's RedirectableUrlMatcher for a plain UrlMatcher — an internal routing refactor, not a sign of renewed maintenance. The package remains archived with zero dependents, so treat it as reference source code, not a maintained dependency. If you are on a legacy Symfony 2.x app and want a working redirect manager, the practical path is: install it if it still resolves, read it, and be ready to vendor it into your own namespace.
Minimal steps (Symfony 2/3 conventions, since it predates Flex):
composer require opifer/redirect-bundle — expect friction on PHP 7.2+ and Symfony 3.4+; check composer.json constraints first.app/AppKernel.php:
new Opifer\RedirectBundle\OpiferRedirectBundle(),
Resources/config/routing.xml) under a firewall-protected prefix.php app/console doctrine:schema:update --force (or generate a migration) — you should see a redirect table appear.old-path → new-path. Hit the old path and confirm the 3xx + Location header.Where to look first, in order:
Entity/ — the Redirect entity fields (from/to path, status code, possibly host or regex flags). This defines what the bundle can actually do.Routing/ — the matcher. As of 0.1.4 this is a plain UrlMatcher; the older RedirectableUrlMatcher variant is gone. Confirm whether anything still touches routing at all, or whether interception is purely kernel.request / kernel.exception.EventListener/ or EventSubscriber/ — how interception works. Most bundles of this era listen on kernel.request (match path, redirect) and/or kernel.exception (catch NotFoundHttpException, i.e. redirect-on-404). With the matcher simplified in 0.1.4, this is where the real logic lives.Resources/config/ — service wiring and the bundle's DI alias (likely opifer_redirect). This is where you learn what is configurable vs. hardcoded.Controller/ / Form/ — the admin CRUD, if you plan to reuse it or replace it with your own UI.First real use case: a CMS migration where old article URLs changed. Seed the table from a CSV of old_path,new_path,status and verify a sample of 301s plus the absence of redirect loops.
Admin-managed redirects. Reuse the bundled controller and form type behind ROLE_ADMIN, or write a thin CRUD in your own admin to avoid coupling to the bundle's templates (which will look dated). Either way, keep the entity — it's the only part you genuinely want.
Seed/bulk import instead of manual entry. For anything beyond a handful of URLs, load redirects via Doctrine fixtures or a migration from a spreadsheet export. Add a unique constraint check on the "from" path in application code before insert — duplicate source paths are the single most common source of confusing behavior.
Interception strategy. Two viable patterns:
$request->getPathInfo() (trim trailing slash, lowercase, strip query string consistently) and look up an exact match. Cheap, predictable.NotFoundHttpException and redirect. This catches only unhandled 404s and preserves normal routing priorities, but it silently loses the redirect if another listener converts the exception first.Pick one and be consistent; running both without coordination leads to double-handling.
Don't rebuild routing-level interception. Prior versions of this bundle resolved redirects inside a custom matcher (the RedirectableUrlMatcher removed in 0.1.4). If your app depended on that — a matcher_class / router option pointing at the bundle's matcher, or an expectation that redirects happen before controller resolution — 0.1.4 silently drops that behavior. The listener patterns above are the stable, matcher-independent approach; prefer them even if you pin an older tag.
Cache the map. A database query on every request is wasteful, and a query on every 404 still adds latency during a bad-crawl event. Load all redirects into an array keyed by normalized path at container/cache warmup, or memoize per-request. Invalidate via a Doctrine postPersist/postUpdate/postRemove listener that clears the cache key.
Push hot paths out of PHP. For high-traffic or SEO-critical redirects, generate nginx rewrite/return 301 rules from the same table as a build step. Keep the bundle as the source of truth, not the runtime path.
Wrap it. Put your own Redirector service in front of the bundle's lookup logic so you can add analytics logging, loop detection, and host matching without patching vendor code. This is also your escape hatch when you eventually replace the bundle.
Multi-domain caveat. If the entity only stores a path, you cannot distinguish example.com/foo from example.org/foo. If you host multiple sites, either add a host column in your own subclass/mapping or scope redirects per site ID.
[READ-ONLY] subtree split. Contribute to the Opifer CMS monorepo (which itself is effectively dormant) or fork.RedirectableUrlMatcher (the bundle's or a subclass), that class is gone — expect a hard class not found or a silent behavior change where redirects stop firing before the controller. Move to an event listener, or pin the previous tag and accept the old constraints.RedirectableUrlMatcher threw its own redirect when a route matched only after adding/removing a trailing slash; a plain UrlMatcher does not. If you previously got /foo/ → /foo for free, you must normalize explicitly now.composer require --ignore-platform-reqs plus manual fixes, or copy the entity + listener into your app.?utm_source=... should be preserved on the target and append it yourself; most bundles of this vintage do not.Redirect entity and lookup logic behind your own interface so swapping in a maintained alternative (or a handful of nginx rules) is a one-file change rather than a refactor.How can I help you explore Laravel packages today?