Routing
PivotPHP provides an Express.js-like routing system that’s intuitive and powerful. Routes are defined using HTTP verb methods on the application instance.
Basic Routing
The most basic route accepts a URI and a closure:
$app->get('/', function($request, $response) {
return $response->send('Hello World!');
});
Available Router Methods
PivotPHP supports all standard HTTP verbs:
$app->get($uri, $callback);
$app->post($uri, $callback);
$app->put($uri, $callback);
$app->patch($uri, $callback);
$app->delete($uri, $callback);
$app->options($uri, $callback);
$app->head($uri, $callback);
You can also match multiple verbs using the match
method:
$app->match(['get', 'post'], '/contact', function($req, $res) {
// Handle GET or POST
});
Or respond to all HTTP verbs using the any
method:
$app->any('/api/*', function($req, $res) {
// Handle any HTTP verb
});
Route Parameters
Required Parameters
Capture segments of the URI using route parameters:
$app->get('/user/{id}', function($req, $res) {
$userId = $req->param('id');
return $res->json(['user_id' => $userId]);
});
You can have multiple parameters:
$app->get('/posts/{year}/{month}/{slug}', function($req, $res) {
$year = $req->param('year');
$month = $req->param('month');
$slug = $req->param('slug');
// Find post by year, month, and slug
});
Optional Parameters
Make a parameter optional by adding a ?
:
$app->get('/posts/{id?}', function($req, $res) {
$id = $req->param('id', 'latest'); // Default to 'latest'
if ($id === 'latest') {
// Return latest posts
} else {
// Return specific post
}
});
Regular Expression Constraints
You can constrain route parameters using regular expressions:
// Only match numeric IDs
$app->get('/user/{id:[0-9]+}', function($req, $res) {
// $id is guaranteed to be numeric
});
// Match specific pattern
$app->get('/posts/{slug:[a-z0-9-]+}', function($req, $res) {
// $slug matches lowercase letters, numbers, and hyphens
});
// UUID pattern
$app->get('/api/v1/resources/{uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}', function($req, $res) {
// Matches UUID format
});
Route Groups
Group related routes to share common attributes like middleware or URL prefixes:
$app->group('/api', function($group) {
// All routes in this group will be prefixed with /api
$group->get('/users', function($req, $res) {
// Matches: GET /api/users
});
$group->post('/users', function($req, $res) {
// Matches: POST /api/users
});
// Nested groups
$group->group('/v1', function($v1) {
$v1->get('/status', function($req, $res) {
// Matches: GET /api/v1/status
});
});
});
Groups with Middleware
Apply middleware to all routes in a group:
$app->group('/admin', function($group) {
// All admin routes
})->middleware(['auth', 'admin']);
// Or apply middleware inside the group
$app->group('/api', function($group) {
$group->middleware('throttle:60,1');
$group->get('/users', function($req, $res) {
// Rate limited
});
});
Controller Routes
Instead of closures, you can use controller classes:
// Single action
$app->get('/users', [UserController::class, 'index']);
$app->post('/users', [UserController::class, 'store']);
$app->get('/users/{id}', [UserController::class, 'show']);
$app->put('/users/{id}', [UserController::class, 'update']);
$app->delete('/users/{id}', [UserController::class, 'destroy']);
// RESTful resource controller
$app->resource('/posts', PostController::class);
The resource
method creates the following routes:
Verb | URI | Action | Route Name |
---|---|---|---|
GET | /posts | index | posts.index |
GET | /posts/create | create | posts.create |
POST | /posts | store | posts.store |
GET | /posts/{id} | show | posts.show |
GET | /posts/{id}/edit | edit | posts.edit |
PUT/PATCH | /posts/{id} | update | posts.update |
DELETE | /posts/{id} | destroy | posts.destroy |
Named Routes
Assign names to routes for easy URL generation:
$app->get('/profile', function($req, $res) {
// Show profile
})->name('profile');
$app->get('/posts/{id}', function($req, $res) {
// Show post
})->name('posts.show');
// Generate URLs
$url = route('profile'); // /profile
$url = route('posts.show', ['id' => 123]); // /posts/123
Route Model Binding
Automatically inject model instances into your routes:
// Implicit binding
$app->get('/users/{user}', function($req, $res, User $user) {
return $res->json($user);
});
// Custom binding
$app->bind('user', function($value) {
return User::where('slug', $value)->firstOrFail();
});
$app->get('/users/{user}', function($req, $res, User $user) {
// $user is resolved by slug instead of ID
});
Fallback Routes
Define a fallback route that executes when no other routes match:
$app->fallback(function($req, $res) {
return $res->status(404)->json([
'error' => 'Not Found'
]);
});
Route Caching
For production, cache your routes for better performance:
php helix route:cache
Clear the route cache when making changes:
php helix route:clear
Best Practices
- Order matters: Define more specific routes before generic ones
- Use route groups: Organize related routes together
- Name your routes: Makes URL generation easier and more maintainable
- Use controllers: For complex logic, move code to controller classes
- Validate parameters: Use regex constraints to ensure parameter validity
- Cache in production: Always cache routes in production for optimal performance