Skip to content

Commit 2d67f51

Browse files
authored
Merge pull request #391 from dotkernel/issue-383
Issue #383: Implemented route grouping
2 parents 0353184 + a8c67b4 commit 2d67f51

File tree

10 files changed

+165
-127
lines changed

10 files changed

+165
-127
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"dotkernel/dot-errorhandler": "^4.0.0",
5959
"dotkernel/dot-mail": "^5.3.0",
6060
"dotkernel/dot-response-header": "^3.4.1",
61+
"dotkernel/dot-router": "^1.0.4",
6162
"laminas/laminas-component-installer": "^3.5.0",
6263
"laminas/laminas-config-aggregator": "^1.18.0",
6364
"laminas/laminas-hydrator": "^4.16.0",

config/config.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class_exists(Mezzio\Tooling\ConfigProvider::class)
4646
Dot\Mail\ConfigProvider::class,
4747
Dot\DataFixtures\ConfigProvider::class,
4848
Dot\Cache\ConfigProvider::class,
49+
Dot\Router\ConfigProvider::class,
4950

5051
// Default App module config
5152
Core\Admin\ConfigProvider::class,

documentation/command/route-list.md

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,49 @@ php ./bin/cli.php route:list
1111
The command runs through all routes and extracts endpoint information in realtime.
1212
The output should be similar to the following:
1313

14-
| Request method | Route name | Route path |
15-
|-----------------|---------------------------------|--------------------------------|
16-
| DELETE | admin.delete | /admin/{uuid} |
17-
| DELETE | user.my-account.delete | /user/my-account |
18-
| DELETE | user.my-avatar.delete | /user/my-avatar |
19-
| DELETE | user.delete | /user/{uuid} |
20-
| DELETE | user.avatar.delete | /user/{uuid}/avatar |
21-
| GET | home | / |
22-
| GET | account.reset-password.validate | /account/reset-password/{hash} |
23-
| GET | admin.list | /admin |
24-
| GET | admin.my-account.view | /admin/my-account |
25-
| GET | admin.role.list | /admin/role |
26-
| GET | admin.role.view | /admin/role/{uuid} |
27-
| GET | admin.view | /admin/{uuid} |
28-
| GET | user.list | /user |
29-
| GET | user.my-account.view | /user/my-account |
30-
| GET | user.my-avatar.view | /user/my-avatar |
31-
| GET | user.role.list | /user/role |
32-
| GET | user.role.view | /user/role/{uuid} |
33-
| GET | user.view | /user/{uuid} |
34-
| GET | user.avatar.view | /user/{uuid}/avatar |
35-
| PATCH | account.activate | /account/activate/{hash} |
36-
| PATCH | account.modify-password | /account/reset-password/{hash} |
37-
| PATCH | admin.my-account.update | /admin/my-account |
38-
| PATCH | admin.update | /admin/{uuid} |
39-
| PATCH | user.my-account.update | /user/my-account |
40-
| PATCH | user.update | /user/{uuid} |
41-
| POST | account.activate.request | /account/activate |
42-
| POST | account.recover-identity | /account/recover-identity |
43-
| POST | account.register | /account/register |
44-
| POST | account.reset-password.request | /account/reset-password |
45-
| POST | admin.create | /admin |
46-
| POST | error.report | /error-report |
47-
| POST | security.generate-token | /security/generate-token |
48-
| POST | security.refresh-token | /security/refresh-token |
49-
| POST | user.create | /user |
50-
| POST | user.my-avatar.create | /user/my-avatar |
51-
| POST | user.activate | /user/{uuid}/activate |
52-
| POST | user.avatar.create | /user/{uuid}/avatar |
14+
```text
15+
+------+----------------+-------------------- 37 Routes ------+-------------------------------------+
16+
| # | Request method | Route name | Route path |
17+
+------+----------------+-------------------------------------+-------------------------------------+
18+
| 1 | GET | app::view-index | / |
19+
| 2 | GET | admin::list-admin | /admin |
20+
| 3 | POST | admin::create-admin | /admin |
21+
| 4 | GET | admin::view-account | /admin/account |
22+
| 5 | PATCH | admin::update-account | /admin/account |
23+
| 6 | GET | admin::list-role | /admin/role |
24+
| 7 | GET | admin::view-role | /admin/role/{uuid} |
25+
| 8 | DELETE | admin::delete-admin | /admin/{uuid} |
26+
| 9 | GET | admin::view-admin | /admin/{uuid} |
27+
| 10 | PATCH | admin::update-admin | /admin/{uuid} |
28+
| 11 | POST | app::create-error-report | /error-report |
29+
| 12 | POST | security::token | /security/token |
30+
| 13 | GET | user::list-user | /user |
31+
| 14 | POST | user::create-user | /user |
32+
| 15 | DELETE | user::delete-account | /user/account |
33+
| 16 | GET | user::view-account | /user/account |
34+
| 17 | PATCH | user::update-account | /user/account |
35+
| 18 | POST | user::create-account | /user/account |
36+
| 19 | POST | user::request-activate-account | /user/account/activate |
37+
| 20 | PATCH | user::activate-account | /user/account/activate/{hash} |
38+
| 21 | DELETE | user::delete-account-avatar | /user/account/avatar |
39+
| 22 | GET | user::view-account-avatar | /user/account/avatar |
40+
| 23 | POST | user::create-account-avatar | /user/account/avatar |
41+
| 24 | POST | user::recover-account | /user/account/recover |
42+
| 25 | POST | user::create-account-reset-password | /user/account/reset-password |
43+
| 26 | GET | user::check-account-reset-password | /user/account/reset-password/{hash} |
44+
| 27 | PATCH | user::update-account-reset-password | /user/account/reset-password/{hash} |
45+
| 28 | GET | user::list-role | /user/role |
46+
| 29 | GET | user::view-role | /user/role/{uuid} |
47+
| 30 | DELETE | user::delete-user | /user/{uuid} |
48+
| 31 | GET | user::view-user | /user/{uuid} |
49+
| 32 | PATCH | user::update-user | /user/{uuid} |
50+
| 33 | PATCH | user::activate-user | /user/{uuid}/activate |
51+
| 34 | DELETE | user::delete-user-avatar | /user/{uuid}/avatar |
52+
| 35 | GET | user::view-user-avatar | /user/{uuid}/avatar |
53+
| 36 | POST | user::create-user-avatar | /user/{uuid}/avatar |
54+
| 37 | PATCH | user::deactivate-user | /user/{uuid}/deactivate |
55+
+------+----------------+-------------------------------------+-------------------------------------+
56+
```
5357

5458
## Filtering results
5559

src/Admin/src/RoutesDelegator.php

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,42 @@
1313
use Api\Admin\Handler\Admin\PostAdminResourceHandler;
1414
use Api\Admin\Handler\Admin\Role\GetAdminRoleCollectionHandler;
1515
use Api\Admin\Handler\Admin\Role\GetAdminRoleResourceHandler;
16+
use Dot\Router\RouteCollectorInterface;
1617
use Mezzio\Application;
18+
use Psr\Container\ContainerExceptionInterface;
1719
use Psr\Container\ContainerInterface;
18-
19-
use function assert;
20+
use Psr\Container\NotFoundExceptionInterface;
2021

2122
class RoutesDelegator
2223
{
24+
/**
25+
* @throws ContainerExceptionInterface
26+
* @throws NotFoundExceptionInterface
27+
*/
2328
public function __invoke(ContainerInterface $container, string $serviceName, callable $callback): Application
2429
{
25-
$app = $callback();
26-
assert($app instanceof Application);
27-
2830
$uuid = \Api\App\RoutesDelegator::REGEXP_UUID;
2931

30-
$app->get('/admin', GetAdminCollectionHandler::class, 'admin::list-admin');
31-
$app->post('/admin', PostAdminResourceHandler::class, 'admin::create-admin');
32+
/** @var RouteCollectorInterface $routeCollector */
33+
$routeCollector = $container->get(RouteCollectorInterface::class);
34+
35+
$routeCollector->group('/admin')
36+
->get('', GetAdminCollectionHandler::class, 'admin::list-admin')
37+
->post('', PostAdminResourceHandler::class, 'admin::create-admin');
3238

33-
$app->delete('/admin/' . $uuid, DeleteAdminResourceHandler::class, 'admin::delete-admin');
34-
$app->get('/admin/' . $uuid, GetAdminResourceHandler::class, 'admin::view-admin');
35-
$app->patch('/admin/' . $uuid, PatchAdminResourceHandler::class, 'admin::update-admin');
39+
$routeCollector->group('/admin/' . $uuid)
40+
->delete('', DeleteAdminResourceHandler::class, 'admin::delete-admin')
41+
->get('', GetAdminResourceHandler::class, 'admin::view-admin')
42+
->patch('', PatchAdminResourceHandler::class, 'admin::update-admin');
3643

37-
$app->get('/admin/role', GetAdminRoleCollectionHandler::class, 'admin::list-role');
38-
$app->get('/admin/role/' . $uuid, GetAdminRoleResourceHandler::class, 'admin::view-role');
44+
$routeCollector->group('/admin/role')
45+
->get('', GetAdminRoleCollectionHandler::class, 'admin::list-role')
46+
->get('/' . $uuid, GetAdminRoleResourceHandler::class, 'admin::view-role');
3947

40-
$app->get('/admin/account', GetAdminAccountResourceHandler::class, 'admin::view-account');
41-
$app->patch('/admin/account', PatchAdminAccountResourceHandler::class, 'admin::update-account');
48+
$routeCollector->group('/admin/account')
49+
->get('', GetAdminAccountResourceHandler::class, 'admin::view-account')
50+
->patch('', PatchAdminAccountResourceHandler::class, 'admin::update-account');
4251

43-
return $app;
52+
return $callback();
4453
}
4554
}

src/App/src/Command/RouteListCommand.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Api\App\Command;
66

77
use Api\App\RoutesDelegator;
8+
use Fig\Http\Message\RequestMethodInterface;
89
use Mezzio\Application;
910
use Symfony\Component\Console\Attribute\AsCommand;
1011
use Symfony\Component\Console\Command\Command;
@@ -56,7 +57,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int
5657

5758
$routes = [];
5859
foreach ($this->application->getRoutes() as $route) {
59-
foreach ($route->getAllowedMethods() as $method) {
60+
$methods = $route->getAllowedMethods();
61+
if (empty($methods)) {
62+
$methods = [
63+
RequestMethodInterface::METHOD_DELETE,
64+
RequestMethodInterface::METHOD_GET,
65+
RequestMethodInterface::METHOD_PATCH,
66+
RequestMethodInterface::METHOD_POST,
67+
RequestMethodInterface::METHOD_PUT,
68+
];
69+
}
70+
71+
foreach ($methods as $method) {
6072
if (! str_contains($route->getName(), $nameFilter)) {
6173
continue;
6274
}

src/App/src/ConfigProvider.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Api\App\Factory\HandlerDelegatorFactory;
1111
use Api\App\Factory\RouteListCommandFactory;
1212
use Api\App\Factory\TokenGenerateCommandFactory;
13+
use Api\App\Handler\GetIndexResourceHandler;
1314
use Api\App\Handler\PostErrorReportResourceHandler;
1415
use Api\App\Middleware\AuthenticationMiddleware;
1516
use Api\App\Middleware\AuthorizationMiddleware;
@@ -52,6 +53,7 @@ public function getDependencies(): array
5253
return [
5354
'delegators' => [
5455
Application::class => [RoutesDelegator::class],
56+
GetIndexResourceHandler::class => [HandlerDelegatorFactory::class],
5557
PostErrorReportResourceHandler::class => [HandlerDelegatorFactory::class],
5658
],
5759
'factories' => [
@@ -63,6 +65,7 @@ public function getDependencies(): array
6365
DeprecationMiddleware::class => AttributedServiceFactory::class,
6466
Environment::class => TwigEnvironmentFactory::class,
6567
ErrorReportPermissionMiddleware::class => AttributedServiceFactory::class,
68+
GetIndexResourceHandler::class => AttributedServiceFactory::class,
6669
PostErrorReportResourceHandler::class => AttributedServiceFactory::class,
6770
ErrorReportService::class => AttributedServiceFactory::class,
6871
ResponseMiddleware::class => AttributedServiceFactory::class,

src/App/src/Middleware/DeprecationMiddleware.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
use Api\App\Exception\DeprecationConflictException;
1010
use Core\App\Message;
1111
use Dot\DependencyInjection\Attribute\Inject;
12+
use Dot\Router\Middleware\LazyLoadingMiddleware;
1213
use Laminas\Stratigility\MiddlewarePipe;
13-
use Mezzio\Middleware\LazyLoadingMiddleware;
14+
use Mezzio\Middleware\LazyLoadingMiddleware as MezzioLazyLoadingMiddleware;
1415
use Mezzio\Router\RouteResult;
1516
use Psr\Http\Message\ResponseInterface;
1617
use Psr\Http\Message\ServerRequestInterface;
@@ -141,7 +142,10 @@ private function getReflectionAttributes(ReflectionClass $reflectionObject): arr
141142
private function getHandler(MiddlewareInterface $routeMiddleware): ?ReflectionClass
142143
{
143144
$reflectionHandler = null;
144-
if ($routeMiddleware instanceof LazyLoadingMiddleware) {
145+
if (
146+
$routeMiddleware instanceof MezzioLazyLoadingMiddleware
147+
|| $routeMiddleware instanceof LazyLoadingMiddleware
148+
) {
145149
/** @var class-string $routeMiddlewareName */
146150
$routeMiddlewareName = $routeMiddleware->middlewareName;
147151
$reflectionMiddlewareClass = new ReflectionClass($routeMiddlewareName);

src/App/src/RoutesDelegator.php

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,33 @@
77
use Api\App\Handler\GetIndexResourceHandler;
88
use Api\App\Handler\PostErrorReportResourceHandler;
99
use Api\App\Middleware\ErrorReportPermissionMiddleware;
10+
use Dot\Router\RouteCollectorInterface;
1011
use Mezzio\Application;
12+
use Psr\Container\ContainerExceptionInterface;
1113
use Psr\Container\ContainerInterface;
12-
13-
use function assert;
14+
use Psr\Container\NotFoundExceptionInterface;
1415

1516
class RoutesDelegator
1617
{
1718
public const REGEXP_UUID = '{uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}}';
1819

20+
/**
21+
* @throws ContainerExceptionInterface
22+
* @throws NotFoundExceptionInterface
23+
*/
1924
public function __invoke(ContainerInterface $container, string $serviceName, callable $callback): Application
2025
{
21-
$app = $callback();
22-
assert($app instanceof Application);
23-
24-
// Home page
25-
$app->get('/', GetIndexResourceHandler::class, 'app::view-index');
26-
27-
// Other application reports an error
28-
$app->post(
29-
'/error-report',
30-
[ErrorReportPermissionMiddleware::class, PostErrorReportResourceHandler::class],
31-
'app::create-error-report'
32-
);
33-
34-
return $app;
26+
/** @var RouteCollectorInterface $routeCollector */
27+
$routeCollector = $container->get(RouteCollectorInterface::class);
28+
29+
$routeCollector
30+
->get('/', GetIndexResourceHandler::class, 'app::view-index')
31+
->post(
32+
'/error-report',
33+
[ErrorReportPermissionMiddleware::class, PostErrorReportResourceHandler::class],
34+
'app::create-error-report'
35+
);
36+
37+
return $callback();
3538
}
3639
}

src/Security/src/RoutesDelegator.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,30 @@
55
namespace Api\Security;
66

77
use Api\Security\Middleware\ErrorResponseMiddleware;
8+
use Dot\Router\RouteCollectorInterface;
89
use Mezzio\Application;
910
use Mezzio\Authentication\OAuth2\TokenEndpointHandler;
11+
use Psr\Container\ContainerExceptionInterface;
1012
use Psr\Container\ContainerInterface;
11-
12-
use function assert;
13+
use Psr\Container\NotFoundExceptionInterface;
1314

1415
class RoutesDelegator
1516
{
17+
/**
18+
* @throws ContainerExceptionInterface
19+
* @throws NotFoundExceptionInterface
20+
*/
1621
public function __invoke(ContainerInterface $container, string $serviceName, callable $callback): Application
1722
{
18-
$app = $callback();
19-
assert($app instanceof Application);
23+
/** @var RouteCollectorInterface $routeCollector */
24+
$routeCollector = $container->get(RouteCollectorInterface::class);
2025

21-
$app->post(
26+
$routeCollector->post(
2227
'/security/token',
2328
[ErrorResponseMiddleware::class, TokenEndpointHandler::class],
2429
'security::token'
2530
);
2631

27-
return $app;
32+
return $callback();
2833
}
2934
}

0 commit comments

Comments
 (0)