Route Matching Process
Magento's route matching process resolves URLs to controllers.
Complete matching flow:
1. HTTP Request arrives
2. FrontController receives request
3. Router chain iterates
4. URL rewrites checked (url_rewrite table)
5. Route matching (routes.xml)
6. Controller resolution (frontName/controller/action)
7. Action execution
URL structure:
https://example.com/blog/post/view/id/5
└─ domain ─┘└─ frontName ─┘└─ controller ─┘└─ action ─┘└─ params ─┘
Controller class resolution:
frontName: blog
controller: post
action: view
→ Vendor/Blog/Controller/Post/View.php
→ Class: Vendor\Blog\Controller\Post\View
Parameter extraction:
blog/post/view/id/5/key/value
→ id = 5
→ key = value
All URL segments after action become parameters
Route matching priority:
1. Exact URL rewrite match
2. Route with matching frontName
3. Default route (cms/index/index)
4. 404 Not Found
Custom Routers
Custom routers handle non-standard URL patterns.
Custom router class:
<?php
namespace Vendor\Module\Router;
use Magento\Framework\App\Action\Forward;
use Magento\Framework\App\ActionFactory;
use Magento\Framework\App\RouterInterface;
use Magento\Framework\App\RequestInterface;
class CustomRouter implements RouterInterface
{
public function __construct(
private ActionFactory $actionFactory
) {}
public function match(RequestInterface $request): ?ActionInterface
{
$path = trim($request->getPathInfo(), '/');
// Match: product/URL_KEY
if (preg_match('/^product\/([\w-]+)$/', $path, $matches)) {
$request->setRouteName('catalog');
$request->setControllerName('product');
$request->setActionName('view');
$request->setParam('url_key', $matches[1]);
return $this->actionFactory->create(
\Magento\Catalog\Controller\Product\View::class
);
}
// Match: category/URL_KEY
if (preg_match('/^category\/([\w-]+)$/', $path, $matches)) {
$request->setRouteName('catalog');
$request->setControllerName('category');
$request->setActionName('view');
$request->setParam('url_key', $matches[1]);
return $this->actionFactory->create(
\Magento\Catalog\Controller\Category\View::class
);
}
return null; // Let next router try
}
}
Register custom router:
<!-- etc/di.xml -->
<type name="Magento\Framework\App\Router\Base">
<arguments>
<argument name="actionList" xsi:type="array">
<item name="custom_router" xsi:type="object">
Vendor\Module\Router\CustomRouter
</item>
</argument>
</arguments>
</type>
Router priority:
// Routers execute in order of registration
// First match wins
// Priority order:
1. UrlRewriteRouter (URL rewrites)
2. DefaultRouter (standard routing)
3. Custom routers (registered via di.xml)
URL Rewrite Integration
URL rewrites intercept routing before standard route matching.
URL rewrite flow:
1. Request: /my-product.html
2. UrlRewriteRouter checks url_rewrite table
3. Found: target_path = catalog/product/view/id/5
4. Request is rewritten internally
5. Standard routing continues with rewritten path
Custom URL rewrite via code:
<?php
namespace Vendor\SEO\Service;
class UrlRewriteService
{
public function __construct(
private \Magento\UrlRewrite\Model\UrlRewriteFactory $rewriteFactory,
private \Magento\UrlRewrite\Model\ResourceModel\UrlRewrite $rewriteResource
) {}
public function createRewrite(
string $requestPath,
string $targetPath,
int $storeId = 0,
int $redirectType = 0
): void {
$rewrite = $this->rewriteFactory->create();
$rewrite->setStoreId($storeId);
$rewrite->setRequestPath($requestPath);
$rewrite->setTargetPath($targetPath);
$rewrite->setRedirectType($redirectType);
$rewrite->setEntityType('custom');
$rewrite->setEntityId(0);
$this->rewriteResource->save($rewrite);
}
}
URL rewrite with router:
public function match(RequestInterface $request): ?ActionInterface
{
$path = trim($request->getPathInfo(), '/');
// Check for custom URL pattern
if ($this->isCustomPattern($path)) {
// Create URL rewrite on-the-fly
$this->createRewrite($path, $this->getTargetPath($path));
// Return forward to trigger rewrite
$this->request->setPathInfo('/' . $this->getTargetPath($path));
return null; // Let URL rewrite handle it
}
return null;
}
Debugging rewrites:
# Check rewrites
php bin/magento urlrewrite:index
# Clear rewrites
php bin/magento urlrewrite:reindex
# Check specific rewrite
mysql -u root -p magento -e "SELECT * FROM url_rewrite WHERE request_path = '/my-product.html';"
Advanced Routing Patterns
Advanced routing patterns for complex URL requirements.
Route with multiple parameters:
<route url="/V1/blog/:blogId/posts/:postId" method="GET">
<service class="Vendor\Blog\Api\PostRepositoryInterface" method="getBlogPost"/>
</route>
Catch-all routes:
// Router that catches unmatched URLs
public function match(RequestInterface $request): ?ActionInterface
{
$path = trim($request->getPathInfo(), '/');
// If no route matched, try custom resolution
if (!$request->getRouteName()) {
$request->setRouteName('vendor_custom');
$request->setControllerName('index');
$request->setActionName('view');
$request->setParam('path', $path);
return $this->actionFactory->create(
\Vendor\Custom\Controller\Index\View::class
);
}
return null;
}
Route with condition:
public function match(RequestInterface $request): ?ActionInterface
{
// Only route if specific condition is met
if (!$this->isEnabled()) {
return null;
}
$path = trim($request->getPathInfo(), '/');
if ($this->matchesPattern($path)) {
// Handle routing
}
return null;
}
Performance considerations:
1. Keep router logic simple (regex is expensive)
2. Return null quickly for non-matching paths
3. Use URL rewrites for static patterns
4. Cache router results when possible
Quiz
1. What is the first thing Magento checks during route matching?
2. What should a custom router return if it doesn't match?
3. How are URL parameters extracted from a path like blog/post/view/id/5?
4. Where are URL rewrites stored?
Flashcards
Question
What is the route matching priority?
Click to reveal answer
Answer
1. URL rewrites 2. Route matching 3. Default route 4. 404
Question
How does a custom router signal no match?
Click to reveal answer
Answer
Return null from the match() method
Question
What URL segments become parameters?
Click to reveal answer
Answer
All segments after the action become key/value pairs
Question
What class is the entry point for routing?
Click to reveal answer
Answer
Magento\Framework\App\FrontController
Question
How do you register a custom router?
Click to reveal answer
Answer
Via di.xml argument for Magento\Framework\App\Router\Base
Revision Notes
Key Takeaways
- 1. Route matching checks URL rewrites first, then routes.xml
- 2. Custom routers implement RouterInterface and return null for no match
- 3. URL segments after action become key/value parameters
- 4. Router priority: URL rewrites > default router > custom routers
- 5. URL rewrites intercept routing before standard matching
- 6. Keep router logic simple for performance
Interview Tips
- • Explain the complete route matching flow
- • Describe how to create a custom router
- • Discuss URL rewrite integration with routing
- • Know performance implications of routing
Cheat Sheet
Routes Deep Dive Cheat Sheet
Matching flow:
- URL rewrites (url_rewrite table)
- Route matching (routes.xml)
- Controller resolution
- Action execution
Custom router:
class MyRouter implements RouterInterface
{
public function match(RequestInterface $request): ?ActionInterface
{
// Return action or null
}
}
Register:
<type name="Magento\Framework\App\Router\Base">
<arguments>
<argument name="actionList" xsi:type="array">
<item name="my_router" xsi:type="object">MyRouter</item>
</argument>
</arguments>
</type>
URL structure:
/frontName/controller/action/param1/value1/param2/value2