benjam1/object-routing-bundle
Installation
Add the bundle to composer.json under require:
"benjam1/object-routing-bundle": "^1.0"
Run:
composer update benjam1/object-routing-bundle
Enable the Bundle
Register BGObjectRoutingBundle in config/bundles.php:
return [
// ...
Benjam1\ObjectRoutingBundle\BGObjectRoutingBundle::class => ['all' => true],
];
First Use Case: Route by Object
Define a route that accepts an object (e.g., App\Entity\Product) as a parameter:
# config/routes.yaml
product_show:
path: /product/{product}
controller: App\Controller\ProductController::show
requirements:
product: ^\d+$ # Regex to match object ID (or custom logic)
In your controller, type-hint the object:
use App\Entity\Product;
public function show(Product $product): Response
{
return $this->render('product/show.html.twig', ['product' => $product]);
}
Configure the Router
Add the object_router to your router configuration (e.g., config/packages/framework.yaml):
framework:
router:
object_router:
enabled: true
resolver: 'doctrine' # or 'custom'
Define Routes with Object Parameters
Use object type-hinting in route definitions (e.g., {product}) and let the bundle resolve the object from the request (e.g., via Doctrine or a custom resolver).
Resolver Integration
# config/packages/bg_object_routing.yaml
bg_object_routing:
resolvers:
doctrine:
enabled: true
entity_manager: 'default'
JMS\Router\ResolverInterface for bespoke logic (e.g., API clients, external services).Dynamic Route Generation Generate URLs from objects in templates or controllers:
{{ path('product_show', { product: product }) }}
$url = $this->generateUrl('product_show', ['product' => $product]);
Nested Objects
Route to nested resources (e.g., product/{product}/review/{review}):
product_review_show:
path: /product/{product}/review/{review}
controller: App\Controller\ReviewController::show
public function show(Product $product, Review $review): Response
Validation and Constraints Use Symfony’s validator to enforce constraints on routed objects:
use Symfony\Component\Validator\Constraints as Assert;
#[Assert\Type(type: Product::class)]
public function show(Product $product): Response
Symfony Forms Bind routed objects to forms:
$form = $this->createForm(ProductType::class, $product);
API Platform Combine with API Platform for automatic route generation:
# config/packages/api_platform.yaml
api_platform:
routing:
resource_collection: true
resource_item: true
Event Listeners Intercept object resolution with events:
// src/EventListener/ObjectResolverListener.php
public function onResolve(JMS\Router\Event\ResolveEvent $event): void
{
if ($event->getParameter('product') instanceof Product) {
// Custom logic (e.g., caching, permissions)
}
}
Register in services.yaml:
services:
App\EventListener\ObjectResolverListener:
tags:
- { name: kernel.event_listener, event: jms.router.resolve, method: onResolve }
Testing Mock resolvers in tests:
$container->get('jms.router.object_resolver')->setResolver(
new TestResolver($mockProduct)
);
Resolver Configuration
bg_object_routing.yaml will silently fail to resolve objects.
Fix: Explicitly define resolvers under bg_object_routing.resolvers.Circular Dependencies
Product → Category → Product) causes infinite loops.
Fix: Use lazy-loading or break cycles with custom resolvers.ID Mismatches
^\d+$) doesn’t match the object’s ID field (e.g., UUID).
Fix: Customize the resolver to handle non-integer IDs or adjust route requirements.Doctrine Proxy Issues
entity_manager is properly configured and injected.Case Sensitivity
{Product} vs {product}).
Fix: Standardize parameter names in routes and controllers.Enable Router Debugging
Add to config/packages/dev/framework.yaml:
framework:
router:
debug: true
Check logs for resolution errors in var/log/dev.log.
Dump Resolved Objects Use Symfony’s var dumper in controllers:
dump($product); // Inspect resolved object
Resolver Priority
bg_object_routing.yaml:
bg_object_routing:
resolvers:
doctrine: ~
custom: ~
priority: ['doctrine', 'custom'] # Lower priority runs first
Custom Resolver
Implement JMS\Router\ResolverInterface:
class CustomResolver implements ResolverInterface
{
public function resolve(Request $request, Parameter $parameter): mixed
{
// Custom logic (e.g., API call, cache lookup)
return $this->fetchFromExternalService($parameter->getName());
}
}
Register in services.yaml:
services:
App\Resolver\CustomResolver:
tags:
- { name: jms.router.resolver, priority: 10 }
Override Default Router Extend the router to add custom logic:
class CustomRouter extends JMS\Router\Router
{
protected function doGenerate(array $parameters, array $context = []): string
{
// Custom URL generation
}
}
Register as a service and replace the default router.
Event Subscribers Extend resolution with subscribers:
class ObjectResolverSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
JMS\Router\Event\ResolveEvent::class => 'onResolve',
];
}
public function onResolve(ResolveEvent $event): void
{
// Modify resolved object or parameters
}
}
Entity Manager Aliases
custom_em) requires explicit configuration:
bg_object_routing:
resolvers:
doctrine:
entity_manager: 'custom_em'
Parameter Binding
product_show:
path: /product/{App\Entity\Product}
Caching
framework:
router:
cache_warmer: true
How can I help you explore Laravel packages today?