Routing
Introduction
Doppar’s routing system, available through the Phaseolies\Support\Facades\Route
namespace, provides a clean, expressive way to define your application’s URL structure and map it to the appropriate controller actions or closures. Inspired by modern frameworks but tailored for performance and clarity, Doppar routing makes it easy to build both small APIs and large-scale web applications.
With support for route prefix grouping, named routes, throttle route, middleware assignment, and RESTful resource routing, Doppar gives developers full control over how requests are handled—without unnecessary complexity. Whether you're defining a simple GET endpoint or building a full REST API, Doppar’s routing engine keeps your code concise, maintainable, and scalable.
Supported HTTP Methods
Doppar supports the following HTTP methods for defining routes:
Method | Description |
---|---|
GET | Retrieves data from the server. Commonly used for fetching resources. |
POST | Submits new data to the server. Typically used for creating new records. |
PUT | Replaces existing data with new data. Used for full updates. |
PATCH | Partially updates existing data. Ideal for minor changes to a resource. |
DELETE | Removes data from the server. |
HEAD | Same as GET but returns only headers. Useful for checking existence or metadata. |
OPTIONS | Describes available communication options. Often used for CORS preflight checks. |
Route Caching
Doppar supports route caching for improved performance in production environments. Route caching is controlled by the APP_ROUTE_CACHE
environment variable: Set to true to enable route caching (recommended for production). Set to false to disable (default for development) Route cache files are stored in: storage/framework/cache/
.
Cache Routes
The route:cache
command is used to compile your application's routes into a single, cached file. This improves performance by significantly speeding up route registration, especially in production environments.
When the route cache is enabled, the framework loads the routes from the generated cache file instead of parsing all route definitions on each request. This reduces overhead and improves response times.
To create the route cache, run:
php pool route:cache
It's important to ensure that all of your routes are working correctly before caching them. If there are any issues or syntax errors in your route definitions, the command will fail and output the relevant error.
Clear Route Cache
The route:clear
command is used to remove the route cache file. This is useful when you have made changes to your application's routes and want to ensure the framework uses the updated definitions.
In a production environment, route caching improves performance by loading a precompiled route list. However, if the route cache becomes outdated or corrupted, it can lead to unexpected behavior. Running this command will delete the cached route file, forcing the application to load routes directly from the source files on the next request.
You should run this command after deploying any updates that modify your routes:
php pool route:clear
Use this when making route changes in production or if experiencing route-related issues.
Basic Routing
The most basic Doppar routes accept a URI and a closure, providing a very simple and expressive method of defining routes and behavior without complicated routing configuration files:
<?php
use Phaseolies\Support\Facades\Route;
Route::get('/', fn() => "Welcome to Doppar");
The Default Route Files
All Doppar routes are defined in your route files, located within the routes
directory. These files are automatically loaded by the framework. The routes/web.php
file is dedicated to defining routes for your web interface and is assigned the web middleware group, which enables essential features like session management and CSRF protection.
For most Doppar applications, you will start by defining routes inside the routes/web.php
file. Any route declared in this file can be accessed through its corresponding URL in the browser. For example, the following route can be accessed by visiting http://example.com/user
in your browser:
use App\Http\Controllers\UserController;
Route::get('user', [UserController::class, 'index']);
API Routes
Doppar provides separete api routes file localted in routes/api.php
.
You can use Doppar flarion, which provides a robust, yet simple API token authentication system which can be used to authenticate third-party API consumers, or mobile applications.
use Phaseolies\Support\Facades\Route;
use Phaseolies\Http\Request;
Route::get('user', function (Request $request) {
return $request->user();
})->middleware('auth-api');
// Endpoint
// http://example.com/api/user
Dependency Injection
You may type-hint any dependencies required by your route directly within the route’s callback signature. Doppar’s service container will automatically resolve and inject the appropriate instances for you. For example, you can type-hint the Phaseolies\Http\Request
class to have the current HTTP request automatically injected into your route callback:
use Phaseolies\Http\Request;
Route::post('payment', function (Request $request) {
//
});
CSRF Protection
Keep in mind that any HTML forms targeting routes using the POST
, PUT
, PATCH
, or DELETE
methods—defined in the web.php routes file—must include a CSRF token field. Without this token, Doppar will reject the request for security reasons. To learn more, refer to the CSRF protection documentation.
<form method="POST" action="{{ route('profile') }}">
@csrf
...
</form>
Handling Modified Request Route
In Doppar, when you need to handle PUT
, PATCH
, or DELETE
requests (typically for updating or deleting data), you must follow a few conventions to make it work properly with HTML forms.
The HTTP methods PUT, PATCH, and DELETE define the intended action on a resource in RESTful APIs or web applications. Here's what each one does when a request is made:
Method | Action | Body Required | Idempotent | Common Use Case |
---|---|---|---|---|
PUT | Full replace of resource | ✅ Yes | ✅ Yes | Update full user record |
PATCH | Partial update of resource | ✅ Yes | ✅ Usually | Change a single field |
DELETE | Remove resource | ❌ Usually no | ✅ Yes | Delete an item or record |
HTTP Verb Spoofing in Forms
Since HTML forms only support GET and POST methods directly, Doppar provides Blade directives to spoof other HTTP methods like PUT, PATCH, and DELETE.
Here’s how you do it in your form:
<form method="POST" action="{{ route('update-profile') }}">
@csrf
@method('PUT') {{-- For PUT Request --}}
{{-- @method('PATCH') For PATCH Request --}}
{{-- @method('DELETE') For DELETE Request --}}
<button type="submit">Submit</button>
</form>
WARNING
Always include @csrf
to protect against CSRF attacks. The @method
directive tells Doppar to treat the request as the specified HTTP verb.
Defining Modified Request Routes
Once your form is set up to spoof PUT
, PATCH
, or DELETE
methods, you need to define the corresponding routes in your routes/web.php
file. These routes will map the specific HTTP methods to the appropriate controller actions.
In Doppar, this is typically done using the Route::put
, Route::patch
, and Route::delete
methods provided by the routing system.
use App\Http\Controllers\ProfileController;
// PUT Route
Route::put('update-profile', [ProfileController::class, 'update']);
// PATCH Route
Route::patch('update-profile', [ProfileController::class, 'update']);
// DELETE Route
Route::delete('user/{id}', [ProfileController::class, 'delete']);
Route Parameters
Sometimes you will need to capture segments of the URI within your route. For example, you may need to capture a user's ID from the URL. You may do so by defining route parameters:
Route::get('user/{id}', function (string $id) {
return 'User '.$id;
});
You may define as many route parameters as required by your route:
Route::get('posts/{post}/comments/{comment}', function (
string $postId, string $commentId
) {
echo $postId;
echo $commentId;
});
Route parameters in Doppar are defined by wrapping the parameter name in curly braces {}
and should consist of alphabetic characters. You may also use underscores (_)
in the parameter names. These parameters are automatically passed into your route callbacks or controller methods based on their position in the route —
the actual variable names in the callback or method signature do not need to match the parameter names in the route.
Named Routes
Doppar support convenient naming route structure. Named routes allow the convenient generation of URLs or redirects for specific routes. You may specify a name for a route by chaining the name method onto the route definition:
use Phaseolies\Support\Facades\Route;
use App\Http\Controllers\UserController;
Route::get('user/{id}/{name}', [UserController::class, 'profile'])
->name('profile');
Now use this naming route any where using route()
global method.
<form action="{{ route('profile', ['id' => 2, 'name' => 'abc']) }}"
method="post">
@csrf
<button type="submit" class="btn btn-primary">Submit</button>
</form>
If there is single param in your route, just use
Route::get('user/{id}', [UserController::class, 'profile'])
->name('profile');
Now call the route
{{ route('profile', $user->id) }}
Route names should always be unique.
Generating URLs to Named Routes
Once you have assigned a name to a given route, you may use the route's name when generating URLs or redirects via Doppar's route and redirect helper functions:
// Generating URLs...
$url = route('profile');
// Generating Redirects...
return redirect()->route('profile');
Defining Bundle Routes
The bundle method allows you to register a complete set of CRUD routes for a controller. You can customize which routes are created and the names assigned to them using the only
, except
, and names
options.
Example
use Phaseolies\Support\Router\Route;
use App\Http\Controllers\ProductController;
Route::bundle('products', ProductController::class);
Generated route for this products
bundle
| Method | URI | Action | Name |
| ------ | -------------------------- | -------------------------- | ----------------- |
| GET | `/products` | `ProductController@index` | `products.index` |
| GET | `/products/create` | `ProductController@create` | `products.create` |
| POST | `/products` | `ProductController@store` | `products.store` |
| GET | `/products/{id}/show` | `ProductController@show` | `products.show` |
| GET | `/products/{id}/edit` | `ProductController@edit` | `products.edit` |
| PUT | `/products/{id}/update` | `ProductController@update` | `products.update` |
| DELETE | `/products/{id}/delete` | `ProductController@delete` | `products.delete` |
By default, bundle routes use the primaryKey
field as the route key for model binding along with fallback to id
.
If your model does not have an id
column, or you want to bind routes using a different column (for example, slug
), you can override the getRouteKeyName()
method in your Eloquent model.
/**
* Get the route key name for model binding.
*
* @return string
*/
#[\Override]
public function getRouteKeyName(): string
{
return 'slug';
}
Now, instead of resolving routes by default primaryKey
, Doppar will automatically resolve models using the slug
field.
Bundle Model & Controller Naming Convention
If you use custom route key binding
, then you must follow this model and controller naming convention. It is important to follow a consistent naming convention for models and controllers. This ensures that automatic model resolution and route binding work correctly.
Controller → Model Mapping
| Controller | Model |
|----------------------------------|---------------------|
| `ProductController` | `Product` |
| `ProductDetailsController` | `ProductDetails` |
You must follow this convention before using bundle routes
If you use custom route key binding
. This ensures that Doppar can correctly infer the corresponding controller from the model and vice versa.
Bundle Routes with Options
The bundle method registers a full set of CRUD routes for a given controller. You can customize which routes are generated and what names they use by passing the only
, except
, names
and method
options.
Example
use App\Http\Controllers\ProductController;
use Phaseolies\Support\Router\Route;
Route::bundle('products', ProductController::class, [
'only' => ['index', 'show', 'update'],
'names' => [
'index' => 'products.all',
'show' => 'products.view'
],
'methods' => [
'update' => 'POST'
]
]);
Explanation:
- The only option ensures that only the
index
,show
andupdate
routes are registered. - The names option
overrides
thedefault route names
with custom ones. - Now the
update
endpoint usesPOST
rather thanPUT
Generated Routes:
| Method | URI | Action | Name |
| ------ | --------------------- | ------------------------- | --------------- |
| GET | `/products` | `ProductController@index` | `products.all` |
| GET | `/products/{id}/show` | `ProductController@show` | `products.view` |
API Bundle Routes
The apiBundle
method works like bundle, but it excludes the create
and edit
routes. This is ideal for API endpoints, where HTML form views are not needed, and you only want routes for data operations.
Example:
use App\Http\Controllers\InviteController;
use Phaseolies\Support\Router\Route;
Route::apiBundle('posts', InviteController::class);
Example with Options:
Route::apiBundle('posts', InviteController::class, [
'only' => ['index', 'show']
]);
Generated Routes:
| Method | URI | Action | Name |
| ------ | ---------------------- | ------------------------ | ------------ |
| GET | `/api/posts` | `InviteController@index` | `posts.index`|
| GET | `/api/posts/{id}/show` | `InviteController@show` | `posts.show` |
Reminder on API Usage
When you are building APIs that accept file uploads, this note is important:
📌 Reminder on API Usage
If you are uploading files using form-data
rather than x-www-form-urlencoded
, Doppar does not support file handling for PUT
and PATCH
requests. Always use POST
request when your API endpoint accepts files. Using PUT/PATCH
with form-data
uploads may cause unexpected issues.
By default, Doppar’s Route::apiBundle
will generate a PUT
request for the update action. Since PUT
does not support file uploads for form-data
, you can override it to POST
:
Route::apiBundle('file', FileController::class, [
'methods' => [
'update' => 'POST' // Now the update endpoint uses POST
]
]);
This ensures your update endpoint can handle file uploads without issues.
Nested Bundle Routes
The nestedBundle
method registers a full CRUD route set for a child resource that is nested under a parent resource. It automatically includes the parent resource parameter in the URI, making it easy to work with relationships such as “posts → comments
” or “categories → products
”.
Example of Nested Bundle
use App\Http\Controllers\CommentController;
use Phaseolies\Support\Router\Route;
Route::nestedBundle('posts', 'comments', CommentController::class);
Example Controller
<?php
namespace App\Http\Controllers;
use Phaseolies\Http\Request;
use App\Http\Controllers\Controller;
class CommentController extends Controller
{
public function index($postId) {}
public function create($postId) {}
public function store(Request $request, $postId) {}
public function show($postId, $id) {}
public function edit($postId, $id) {}
public function update(Request $request, $postId, $id) {}
public function delete($postId, $id) {}
}
Generated Routes
| Method | URI | Action | Name |
| ------ | ----------------------------------- | -------------------------- | ----------------------- |
| GET | `/posts/{post}/comments` | `CommentController@index` | `posts.comments.index` |
| GET | `/posts/{post}/comments/create` | `CommentController@create` | `posts.comments.create` |
| POST | `/posts/{post}/comments` | `CommentController@store` | `posts.comments.store` |
| GET | `/posts/{post}/comments/{id}/show` | `CommentController@show` | `posts.comments.show` |
| GET | `/posts/{post}/comments/{id}/edit` | `CommentController@edit` | `posts.comments.edit` |
| PUT | `/posts/{post}/comments/{id}/update`| `CommentController@update` | `posts.comments.update` |
| DELETE | `/posts/{post}/comments/{id}/delete`| `CommentController@delete` | `posts.comments.delete` |
You can also pass options in nested bundle
Route::nestedBundle('posts', 'comments', CommentController::class, [
'except' => ['create', 'edit']
]);
Note:
Bundle routes do not support method chaining like->middleware('auth')
. To apply middleware, use attribute-based middleware calling directly in your bundle controller.
Route Group
Route::group
is used to group multiple routes under a shared configuration like URL prefix. This helps in organizing routes cleanly and applying common logic to them.
Route::group([
'prefix' => 'your-prefix'
], function () {
// Routes go here
});
Example
Look at the below example, we are using prefix as a group route.
Route::group(['prefix' => 'login'], function () {
Route::get('/action', function () {
// Matches The "/login/action" URL
});
});
'prefix' => 'login'
This means all routes inside this group will be prefixed with /login
.
Route Middleware
Middleware in Doppar provides a convenient mechanism for filtering or modifying HTTP requests as they enter your application. Route middleware is typically used to perform tasks such as authentication, logging, CORS handling, input sanitization, and more — before the request reaches the controller or route logic.
You can assign middleware to routes in two primary ways:
Assigning Middleware to Individual Routes
You can attach middleware directly to a specific route using the middleware method. This allows you to apply custom logic, such as authentication or request throttling, to just that route.
Route::get('dashboard', function () {
// Only accessible to authenticated users
})->middleware('auth');
In this example, the auth middleware ensures that only authenticated users can access the dashboard
route. If a user is not authenticated, they will be redirected or denied access based on your application's configuration.
You may also chain multiple middleware by passing them as an array:
Route::post('settings', function () {
// Protected by multiple middleware
})->middleware(['auth', 'verified']);
This approach gives you fine-grained control over access and behavior at the route level.
You can assign middleware directly to a specific route using the middleware method. This lets you apply route-level behavior such as authentication, authorization, or request handling as multiple string arguments like this way.
Route::post('settings', function () {
// Protected by multiple middleware
})->middleware('auth', 'verified');
Both approaches are fully supported in Doppar and function the same. Use whichever style best fits your project's conventions or coding preferences.
Route Redirection
The Route::redirect()
method allows you to define quick and clean route redirections in your application. Whether you're migrating old URLs, setting up aliases, or redirecting to external destinations, this method provides a declarative and consistent way to do so.
Syntax
Route::redirect($from, $to, $status = 302);
$from
— The original URI pattern to match.$to
— The target location: can be a URL, a named route, or an external link.$status
— Optional HTTP status code (default: 302 Found). Use 301 for permanent redirects.
Redirecting to a New URL
Route::redirect('old', 'new', 301);
Redirecting to a Named Route
Route::redirect('legacy', 'user.profile');
Redirecting to an External URL
Route::redirect('blog', 'https://doppar.com');