kutny/no-bundle-controllers-bundle
A Symfony bundle that lets you use controllers without creating a bundle for them. Register plain controller classes and their routes directly in your application config, cutting boilerplate bundle scaffolding and keeping small projects and prototypes simple to structure.
What it solves. On Symfony 2.x, controllers were tied to bundles through string notation (AcmeDemoBundle:Default:index) and the framework's bundle-aware controller resolver. This bundle relaxes that constraint, letting you register and route plain PHP controllers that live outside any bundle directory — useful for keeping application code (or shared libraries) out of the bundle namespace.
Install (historical context). The package is Composer-distributed but has had no release since January 2016 and pulls in zero dependents, so pin explicitly and expect to vendor-fork it:
"require": {
"kutny/no-bundle-controllers-bundle": "dev-master"
}
Register the bundle in app/AppKernel.php (or Kernel.php on early Symfony 3):
public function registerBundles()
{
$bundles = [
// ...
new Kutny\NoBundleControllersBundle\KutnyNoBundleControllersBundle(),
];
return $bundles;
}
Where to look first. Open Resources/config/services.yml inside the bundle — that is where the custom controller resolver / routing loader is wired. Everything this bundle does is a small decorator around ControllerResolver and the routing loader, so reading those two service definitions tells you more than any docs (there are none).
First use case. Create a controller outside src/, e.g. app/Controller/HealthController.php, point a route at it using the class notation, and hit it. If the route resolves and the action executes without a Bundle in the namespace, the bundle is doing its job.
Route definition. The whole point is to skip the Bundle:Controller:action shorthand. Use FQCN or service notation in app/config/routing.yml:
health_check:
path: /health
defaults:
_controller: 'App\Controller\HealthController::checkAction'
Service controllers as the default. Wire controllers as services and reference them by id. This is the pattern that ages best and the one Symfony itself later adopted:
services:
app.controller.health:
class: App\Controller\HealthController
arguments: ['@logger']
health_check:
path: /health
defaults:
_controller: 'app.controller.health:checkAction'
Directory layout that stays out of bundles. Keep controllers under app/Controller/ or a PSR-4 App\ namespace in composer.json, then let the loader map them. This keeps business logic out of src/Acme/*Bundle and reduces the “which bundle owns this?” friction when you decompose a monolith.
Incremental migration. Don't rewrite routing in one pass. Add the bundle, move one controller out of its bundle at a time, and update only its route entry. Because the loader falls back to Symfony's native resolution for existing Bundle:Controller:action routes, the two styles coexist — so the migration is reversible per-route.
Shared base controllers. The real win is that controllers can live in a plain Composer library (no *Bundle suffix, no DependencyInjection extension class). Install the library, register its controllers as services, and route to them by FQCN — no bundle skeleton required.
This is essentially unmaintained. Last commit/release was 2016, zero dependents, zero stars. Treat it as a reference implementation or something you vendor-fork, not something to composer require on a live project. Modern Symfony resolves FQCN controllers natively (App\Controller\HealthController::checkAction) and has done so since 3.1, which makes this bundle redundant — port the pattern rather than the dependency.
Version coupling. The bundle targets Symfony 2.x's ControllerResolver / routing loader APIs. That interface changed in Symfony 3 and again in 4 (container.service_subscriber, AbstractController, autowiring). Installing it against a modern Symfony will fail at container-compile time, usually with a missing-service error rather than a clear version conflict — check composer show kutny/* and read the service config before chasing the error.
Cache is brutal here. Custom routing loaders are cached aggressively. After moving a controller or editing the resolver config, clear with bin/console cache:clear --no-warmup, and if routes still 404, delete var/cache/* by hand — a stale url_matching_routes.php or appDevUrlGenerator.php will make you think the loader is broken.
Debug the resolver, not the route. If routes appear in debug:router but the request 404s or throws “Unable to find controller,” the route is fine and the controller resolver failed. Dump the tagged controller_resolver service and confirm the bundle's decorator is registered — a missing kernel.event_listener or a decorates typo is the usual culprit.
Extension point. The bundle's contribution is small and self-contained. If you need this capability, copy the loader/resolver decorator into your own tiny bundle (or a compiler pass) under your own namespace. That gives you a supported upgrade path and lets you add autowiring, which the original predates.
Test the negative case. The whole point is resolution without a bundle, so a test that only hits an existing route proves nothing. Add a functional test that asserts a plainly-namespaced controller resolves, and a second that asserts a Bundle:Controller:action string still works — the fallback is the part most likely to regress.
How can I help you explore Laravel packages today?