Routing
Routes define how your application responds to HTTP requests. ZephyrPHP provides a powerful, expressive routing system with all the features you need.
Basic Routing
Routes are defined in routes/web.php. The simplest route accepts a URI and a callback:
<?php
use ZephyrPHP\Router\Route;
// Closure-based route
Route::get('/hello', function() {
return 'Hello, World!';
});
// Controller string syntax (recommended)
Route::get('/users', 'UserController@index');
// Controller array syntax
Route::get('/users', [UserController::class, 'index']);
Available HTTP Methods
ZephyrPHP supports all common HTTP methods:
Route::get('/users', 'UserController@index');
Route::post('/users', 'UserController@store');
Route::put('/users/{id}', 'UserController@update');
Route::patch('/users/{id}', 'UserController@update');
Route::delete('/users/{id}', 'UserController@destroy');
Route::options('/users', 'UserController@options');
// Match multiple methods
Route::match(['get', 'post'], '/form', 'FormController@handle');
// Match all methods
Route::any('/webhook', 'WebhookController@handle');
| Method | Description |
|---|---|
| GET | Retrieve a resource |
| POST | Create a new resource |
| PUT | Update an entire resource |
| PATCH | Partially update a resource |
| DELETE | Delete a resource |
Route Parameters
Capture segments of the URI using route parameters:
Required Parameters
Route::get('/users/{id}', function($id) {
return "User ID: $id";
});
Route::get('/posts/{post}/comments/{comment}', function($postId, $commentId) {
return "Post: $postId, Comment: $commentId";
});
Optional Parameters
Make parameters optional by adding a ? suffix:
Route::get('/users/{name?}', function($name = 'Guest') {
return "Hello, $name!";
});
Parameter Constraints
Restrict parameters using inline regex or constraint helpers:
// Inline regex constraint
Route::get('/users/{id:\d+}', 'UserController@show');
Route::get('/posts/{slug:[a-z-]+}', 'PostController@show');
// Using constraint helpers
Route::get('/users/{id}', 'UserController@show')
->whereNumber('id');
Route::get('/posts/{slug}', 'PostController@show')
->whereSlug('slug');
Route::get('/category/{type}', 'CategoryController@show')
->whereIn('type', ['tech', 'news', 'sports']);
// Multiple constraints
Route::get('/articles/{id}/{slug}', 'ArticleController@show')
->whereNumber('id')
->whereSlug('slug');
Available Constraint Helpers
| Method | Pattern | Example |
|---|---|---|
whereNumber() |
\d+ |
123, 456 |
whereAlpha() |
[a-zA-Z]+ |
john, Admin |
whereAlphaNumeric() |
[a-zA-Z0-9]+ |
user123 |
whereSlug() |
[a-z0-9-]+ |
my-post-title |
whereUuid() |
UUID format | 550e8400-e29b-41d4-a716-446655440000 |
whereIn() |
Specific values | draft, published |
where() |
Custom regex | Any pattern |
Global Patterns
Define patterns that apply to all routes:
// In your routes file or service provider
Route::pattern('id', '\d+');
Route::pattern('slug', '[a-z0-9-]+');
// Now all {id} parameters will be numeric
Route::get('/users/{id}', 'UserController@show');
Route::get('/posts/{id}', 'PostController@show');
Named Routes
Named routes allow you to generate URLs or redirects without hardcoding URIs:
Route::get('/users/{id}', 'UserController@show')
->name('users.show');
Route::get('/dashboard', 'DashboardController@index')
->name('dashboard');
Generating URLs
Use the route() helper to generate URLs for named routes:
// Simple route
$url = route('dashboard');
// Result: /dashboard
// Route with parameters
$url = route('users.show', ['id' => 1]);
// Result: /users/1
// Absolute URL
$url = Route::url('users.show', ['id' => 1], absolute: true);
// Result: https://example.com/users/1
Checking Current Route
// Get current route name
$name = Route::currentRouteName();
// Check if current route matches
if (Route::is('users.*')) {
// On a users route
}
if (Route::is('admin.dashboard', 'admin.settings')) {
// On admin dashboard or settings
}
Redirecting to Named Routes
// In a controller
return $this->redirectToRoute('users.show', ['id' => $user->id]);
Route Groups
Group routes that share common attributes like prefixes or middleware:
Simple Prefix Groups
Route::group('/admin', function() {
Route::get('/dashboard', 'AdminController@dashboard');
Route::get('/users', 'AdminController@users');
Route::get('/settings', 'AdminController@settings');
});
// Results in:
// /admin/dashboard
// /admin/users
// /admin/settings
Fluent Group Builder
Route::prefix('/api/v1')
->middleware(['api', 'throttle'])
->group(function() {
Route::get('/users', 'Api\UserController@index');
Route::get('/posts', 'Api\PostController@index');
});
// Combine with domain
Route::domain('admin.example.com')
->prefix('/dashboard')
->middleware('admin')
->group(function() {
Route::get('/', 'AdminController@index');
});
Array Syntax
Route::group([
'prefix' => '/admin',
'middleware' => ['auth', 'admin'],
'domain' => 'admin.example.com',
], function() {
Route::get('/dashboard', 'AdminController@dashboard');
});
Resource Routes
Quickly register all CRUD routes for a resource:
// Full resource (7 routes)
Route::resource('users', 'UserController');
// API resource (5 routes - no create/edit)
Route::apiResource('posts', 'PostController');
// Multiple resources
Route::resources([
'users' => 'UserController',
'posts' => 'PostController',
]);
Resource Route Options
// Only specific actions
Route::resource('photos', 'PhotoController', [
'only' => ['index', 'show']
]);
// Exclude specific actions
Route::resource('videos', 'VideoController', [
'except' => ['destroy']
]);
// With middleware
Route::resource('articles', 'ArticleController', [
'middleware' => ['auth']
]);
Resource Routes Table
| Method | URI | Action | Route Name |
|---|---|---|---|
| GET | /users | index | users.index |
| GET | /users/create | create | users.create |
| POST | /users | store | users.store |
| GET | /users/{user} | show | users.show |
| GET | /users/{user}/edit | edit | users.edit |
| PUT/PATCH | /users/{user} | update | users.update |
| DELETE | /users/{user} | destroy | users.destroy |
Route Model Binding
Automatically resolve route parameters to model instances:
// Register model binding
Route::model('user', App\Models\User::class);
Route::model('post', App\Models\Post::class);
// Now {user} will be resolved to a User model
Route::get('/users/{user}', function(User $user) {
return $user->name;
});
// Bind by different column
Route::model('post', App\Models\Post::class, 'slug');
// Custom binding logic
Route::bind('user', function($value) {
return User::where('username', $value)->firstOrFail();
});
Fallback Routes
Define a fallback route for unmatched requests:
Route::fallback('FallbackController@handle');
// Or with closure
Route::fallback(function() {
return view('errors/404');
});
Redirect Routes
Define redirect routes without a controller:
// Temporary redirect (302)
Route::redirect('/old-page', '/new-page');
// Permanent redirect (301)
Route::permanentRedirect('/legacy', '/modern');
// View route (renders template directly)
Route::view('/about', 'pages/about', ['title' => 'About Us']);
Subdomain Routing
Define routes for specific subdomains:
Route::domain('api.example.com')->group(function() {
Route::get('/users', 'Api\UserController@index');
});
// Wildcard subdomain
Route::domain('{account}.example.com')->group(function() {
Route::get('/dashboard', function($account) {
return "Dashboard for: $account";
});
});
Route Middleware
Attach middleware to individual routes:
Route::get('/admin', 'AdminController@index', ['auth', 'admin']);
// Or using method
Route::get('/profile', 'ProfileController@index')
->middleware('auth');
HTML Forms & Method Spoofing
HTML forms only support GET and POST. For PUT, PATCH, or DELETE, use method spoofing:
<form action="/users/1" method="POST">
<input type="hidden" name="_method" value="DELETE">
{{ csrf_field()|raw }}
<button type="submit">Delete User</button>
</form>
All POST, PUT, PATCH, and DELETE requests require a CSRF token. Use {{ csrf_field()|raw }} in your forms.
Route Listing
Get a list of all registered routes:
// In code
$routes = Route::list();
// Each route contains:
// - method
// - path
// - name
// - middleware
// - domain
Route Caching
In production, cache your routes for better performance:
php craftsman route:cache
Clear the route cache with:
php craftsman route:clear
Route caching only works with controller-based routes, not closures. If you use closure routes, they cannot be cached.
Complete Example
A typical routes file:
<?php
use ZephyrPHP\Router\Route;
// Public routes
Route::get('/', 'HomeController@index')->name('home');
Route::get('/about', 'PageController@about')->name('about');
Route::view('/contact', 'pages/contact')->name('contact');
// Authentication
Route::get('/login', 'AuthController@showLogin')->name('login');
Route::post('/login', 'AuthController@login');
Route::post('/logout', 'AuthController@logout')->name('logout');
// Protected routes
Route::prefix('/dashboard')
->middleware('auth')
->group(function() {
Route::get('/', 'DashboardController@index')->name('dashboard');
Route::resource('posts', 'PostController');
Route::resource('comments', 'CommentController');
});
// Admin routes
Route::prefix('/admin')
->middleware(['auth', 'admin'])
->group(function() {
Route::get('/', 'Admin\DashboardController@index')->name('admin.dashboard');
Route::resource('users', 'Admin\UserController');
});
// API routes
Route::prefix('/api/v1')->group(function() {
Route::apiResource('posts', 'Api\PostController');
Route::apiResource('users', 'Api\UserController');
});
// Fallback
Route::fallback('ErrorController@notFound');
Learn how to handle requests in Controllers, protect routes with Middleware, or work with Request and Response objects.