Como estruturar as Rotas em grandes projectos Laravel?

Imagine um projeto Laravel com mais de 100 rotas, separadas para utilizadores convidados, registados, administradores, etc… Tem a certeza que quer manter essas rotas num único ficheiro??? Como podemos agrupar, adicionar prefixos às URLs? Vamos ver quais opções temos disponíveis.
Separar as rotas WEB e API
Esta é fácil e é uma funcionalidade que o Laravel oferece por defeito. Existem dois ficheiros:
- routes/web.php
- routes/api.php
Então, se no seu projeto tem tanto views como uma API (o que nos dias de hoje é cada vez mais comum), por favor, utilize o ficheiro routes/api.php
para estruturar essas rotas.
Por exemplo, se tiver a página /users
e também um endpoint /api/users
, separar estas rotas nos respetivos ficheiros irá ajudar a não criar confusões de nomeações no mesmo ficheiro.
Estruturar o ficheiro routes/web.php em Groups
Esta funcionalidade também vem por defeito numa nova instalação de Laravel. Este exemplo é retirado da documentação oficial do Laravel:
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// Uses first & second Middleware
});
Route::get('user/profile', function () {
// Uses first & second Middleware
});
});
A forma mais basica é “esconder” diferentes groups dentro de diferentes middlewares. Por exemplo, precisa de um group que esteja restrito por defeito ao middleware auth
, e outro group para o middleware admin
.
Com isto, pode usar route group names e prefixes, como mostra o exemplo abaixo:
Route::prefix('admin')->group(function () {
Route::get('users', function () {
// Matches The "/admin/users" URL
});
});
Route::name('admin.')->group(function () {
Route::get('users', function () {
// Route assigned name "admin.users"...
})->name('users');
});
Pode ainda adicionar tudo (middleware+name+prefix) a um unico group, fica muito mais legível colocar dentro de um array:
// Instead of chaining like this:
Route::name('admin.')->prefix('admin')->middleware('admin')->group(function () {
// ...
});
// You can use an array
Route::group([
'name' => 'admin.',
'prefix' => 'admin',
'middleware' => 'auth'
], function () {
// ...
});
Vamos então colocar tudo junto num exemplo que podia perfeitamente ser real.
- Utilizadores Convidados (guest) – group com /front/xxxxx URLs sem qualquer middleware
- Utilizadores registados (user) – group com /user/xxxxx URLs com middleware auth
- Admins – group com /admin/xxxxxx URLs e com middleware admin
Deixo uma forma para agrupar rotas no ficheiro routes/web.php:
Route::group([
'name' => 'admin.',
'prefix' => 'admin',
'middleware' => 'admin'
], function () {
// URL: /admin/users
// Route name: admin.users
Route::get('users', function () {
return 'Admin: user list';
})->name('users');
});
Route::group([
'name' => 'user.',
'prefix' => 'user',
'middleware' => 'auth'
], function () {
// URL: /user/profile
// Route name: user.profile
Route::get('profile', function () {
return 'User profile';
})->name('profile');
});
Route::group([
'name' => 'front.',
'prefix' => 'front'
], function () {
// No middleware here
// URL: /front/about-us
// Route name: front.about
Route::get('about-us', function () {
return 'About us page';
})->name('about');
});
Agrupar Controllers com Namespaces
No exemplo anterior, não usámos Controllers, simplesmente retornamos texto como exemplo. Vamos adicionar Controllers com mais um “truque” – vamos estruturar os controladores em pastas com os seus próprios namespaces, como a seguir:

Depois podemos utilizar no nosso ficheiro de Routes:
Route::group([
'name' => 'front.',
'prefix' => 'front'
], function () {
Route::get('about-us', 'Front\AboutController@index')->name('about');
});
Mas, e se tivermos muito controladores neste grupo? Devemos continuar a utilizar Front\QualquerController sempre??? Claro que não!!! Pode especificar o namespace como parametro também.
Route::group([
'name' => 'front.',
'prefix' => 'front',
'namespace' => 'Front',
], function () {
Route::get('about-us', 'AboutController@index')->name('about');
Route::get('contact', 'ContactController@index')->name('contact');
});
Grupos dentro de grupos
Na situação anterior com três grupos está simplificada. Projectos reais têm pequenas diferenças na estrutura.
Imagine dois grupos: front e auth. E depois dentro de auth temos sub-grupos: user e admin. Para isso podemos criar sub-grupos e adicionar-mos diferentes middlewares e prefixos.
Route::group([
'middleware' => 'auth',
], function() {
Route::group([
'name' => 'admin.',
'prefix' => 'admin',
'middleware' => 'admin'
], function () {
// URL: /admin/users
// Route name: admin.users
Route::get('users', 'UserController@index')->name('users');
});
Route::group([
'name' => 'user.',
'prefix' => 'user',
], function () {
// URL: /user/profile
// Route name: user.profile
Route::get('profile', 'ProfileController@index')->name('profile');
});
});
Podemos fazer ainda com mais que dois niveis. Deixo um exemplo do projecto open-source Akauting:
Route::group(['middleware' => 'language'], function () {
Route::group(['middleware' => 'auth'], function () {
Route::group(['prefix' => 'uploads'], function () {
Route::get('{id}', 'Common\Uploads@get');
Route::get('{id}/show', 'Common\Uploads@show');
Route::get('{id}/download', 'Common\Uploads@download');
});
Route::group(['middleware' => 'permission:read-admin-panel'], function () {
Route::group(['prefix' => 'wizard'], function () {
Route::get('/', 'Wizard\Companies@edit')->name('wizard.index');
// ...
Definições Globais em RouteServiceProvider
O ficheiro app/Providers/RouteServiceProvider.php é onde estão todas as definições das rotas. Contém o método map() onde é feita a ligação aos dois ficheiros de rotas:
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
Reparou que o middleware, o namespace e o prefixo estão mencionados nos métodos? Aqui é onde pode configurar todas as definições globais dos ficheiros, assim não precisa repetir em cada grupo de rotas no ficheiro.
Na maior parte das vezes é usado nas rotas de API, porque as suas configurações são normalmente as mesmas.
protected function mapApiRoutes()
{
Route::group([
'middleware' => ['api'],
'namespace' => $this->namespace,
'prefix' => 'api/v1',
], function ($router) {
require base_path('routes/api.php');
});
}
O método anterior define o prefixo de todas as URLs da API em api/v1 no seu inicio.
Agrupar Rotas em ficheiros customizados
Se você tem um elevado número de rotas e deseja agrupá-las de uma forma organizada em diferentes ficheiros, então pode usar o ficheiro já mencionado, app/Providers/RouteServiceProvider.php. Vamos ver em pormenor no método map().
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
//
}
Neste método pode mencionar e ligar mais ficheiros de rotas. Para isso deverá criar um novo método como por exemplo mapAdminRoutes() dentro deste ficheiro, e depois adicionar no método map(), então o seu ficheiro de rotas será registado e carregado automaticamente.
Encontrar a rota certa com o comando Artisan route:list
Já se viu perdido entre uma enormidade de número de rotas? Dispomos de um comando artisan que nos ajuda a localizar uma determinada rota.
Provavelmente já sabe que php artisan route:list devolve todas as rotas no projecto.

Mas, sabia que tem funcionalidades de filtro para procurar exatamente o que procura? Simplestemente adicione –method, –name ou –path com parametros.
Filtrar pelo método -GET, POST, etc:

Filtrar pelo nome ou URL:

Isto é tudo o que o agrupamento de rotas lhe pode oferecer em grandes projetos. Tem outros exemplos? Por favor compartilhe nos comentários.