Advanced Routing
Advanced routing features in Canvas provide powerful pattern matching, route organization, and request handling capabilities for complex applications.
Wildcard Routes
Match variable numbers of URL segments using wildcards:
Single-Segment Wildcards
Use * to match exactly one URL segment:
/**
* @Route("/files/*")
*/
public function singleFile(Request $request): Response {
// Matches: /files/image.jpg
// Matches: /files/document.pdf
// Does not match: /files/folder/image.jpg (multiple segments)
}
Multi-Segment Wildcards
Use ** or {name:**} to match multiple URL segments (zero or more):
/**
* @Route("/api/**")
*/
public function apiCatchAll(Request $request): Response {
// Matches: /api/v1/users/123
// Matches: /api/admin/settings/cache
}
/**
* @Route("/files/{path:**}")
*/
public function nestedFiles(string $path): Response {
// Matches: /files/docs/reports/2024/summary.pdf
// $path = "docs/reports/2024/summary.pdf"
// Matches: /files/image.jpg
// $path = "image.jpg"
}
Route Prefixes
Apply a prefix to all routes in a controller using @RoutePrefix:
<?php
namespace App\Controllers;
use Quellabs\Canvas\Annotations\Route;
use Quellabs\Canvas\Annotations\RoutePrefix;
use Quellabs\Canvas\Controllers\BaseController;
/**
* @RoutePrefix("/admin")
*/
class AdminController {
/**
* @Route("/users")
*/
public function users(): Response {
// Full path: /admin/users
}
/**
* @Route("/settings")
*/
public function settings(): Response {
// Full path: /admin/settings
}
}
Inherited Route Prefixes
Route prefixes stack through inheritance (parent to child order):
/**
* @RoutePrefix("/api")
*/
class ApiController {}
/**
* @RoutePrefix("/v1")
*/
class ApiV1Controller {
/**
* @Route("/users")
*/
public function users(): Response {
// Full path: /api/v1/users
// Prefixes combine: /api + /v1 + /users
}
}
Route Matching Priority
When multiple routes could match a URL, Canvas automatically prioritizes more specific patterns over generic ones. If multiple routes match, the highest priority route wins.
Priority order (highest to lowest):
- Static routes: Fully static paths like
/users/active - Constrained parameters: Routes with type constraints like
/users/{id:int} - Unconstrained parameters: Routes with simple parameters like
/users/{name} - Wildcards: Single-segment
*or multi-segment**patterns
/**
* @Route("/users/active")
*/
public function activeUsers(): Response {
// Static route - highest priority
}
/**
* @Route("/users/{id:int}")
*/
public function showUserById(int $id): Response {
// Constrained parameter - won't match /users/active (fails int constraint)
}
/**
* @Route("/users/{slug:slug}")
*/
public function showUserBySlug(string $slug): Response {
// Could match /users/active depending on route order
}
/**
* @Route("/users/{name}")
*/
public function showUserByName(string $name): Response {
// Unconstrained parameter - matches anything
}
Accessing Request Data
Inject Symfony's Request object to access HTTP request data:
use Symfony\Component\HttpFoundation\Request;
/**
* @Route("/search", methods={"GET", "POST"})
*/
public function search(Request $request): Response {
// Get query parameters
$query = $request->query->get('q');
// Get POST data
$name = $request->request->get('name');
// Get cookies
$sessionId = $request->cookies->get('session_id');
// Get headers
$userAgent = $request->headers->get('User-Agent');
// Check HTTP method
if ($request->isMethod('POST')) {
// Handle POST
}
// Get full URL
$url = $request->getUri();
// Get path
$path = $request->getPathInfo();
}