Skip to content

Commit c5b8676

Browse files
authored
Merge pull request #7416 from kenjis/feat-auto-routing-module
feat: [Auto Routing Improved] Module Routing
2 parents 5be9023 + a72ea47 commit c5b8676

File tree

7 files changed

+121
-6
lines changed

7 files changed

+121
-6
lines changed

app/Config/Routing.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,17 @@ class Routing extends BaseRouting
9797
* Default: false
9898
*/
9999
public bool $prioritize = false;
100+
101+
/**
102+
* Map of URI segments and namespaces. For Auto Routing (Improved).
103+
*
104+
* The key is the first URI segment. The value is the controller namespace.
105+
* E.g.,
106+
* [
107+
* 'blog' => 'Acme\Blog\Controllers',
108+
* ]
109+
*
110+
* @var array [ uri_segment => namespace ]
111+
*/
112+
public array $moduleRoutes = [];
100113
}

system/Commands/Utilities/Routes.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\AutoRouteCollector as AutoRouteCollectorImproved;
1919
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;
2020
use CodeIgniter\Commands\Utilities\Routes\SampleURIGenerator;
21+
use Config\Routing;
2122
use Config\Services;
2223

2324
/**
@@ -152,6 +153,22 @@ public function run(array $params)
152153
);
153154

154155
$autoRoutes = $autoRouteCollector->get();
156+
157+
// Check for Module Routes.
158+
if ($routingConfig = config(Routing::class)) {
159+
foreach ($routingConfig->moduleRoutes as $uri => $namespace) {
160+
$autoRouteCollector = new AutoRouteCollectorImproved(
161+
$namespace,
162+
$collection->getDefaultController(),
163+
$collection->getDefaultMethod(),
164+
$methods,
165+
$collection->getRegisteredControllers('*'),
166+
$uri
167+
);
168+
169+
$autoRoutes = [...$autoRoutes, ...$autoRouteCollector->get()];
170+
}
171+
}
155172
} else {
156173
$autoRouteCollector = new AutoRouteCollector(
157174
$collection->getDefaultNamespace(),

system/Commands/Utilities/Routes/AutoRouterImproved/AutoRouteCollector.php

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ final class AutoRouteCollector
3535
*/
3636
private array $protectedControllers;
3737

38+
/**
39+
* @var string URI prefix for Module Routing
40+
*/
41+
private string $prefix;
42+
3843
/**
3944
* @param string $namespace namespace to search
4045
*/
@@ -43,13 +48,15 @@ public function __construct(
4348
string $defaultController,
4449
string $defaultMethod,
4550
array $httpMethods,
46-
array $protectedControllers
51+
array $protectedControllers,
52+
string $prefix = ''
4753
) {
4854
$this->namespace = $namespace;
4955
$this->defaultController = $defaultController;
5056
$this->defaultMethod = $defaultMethod;
5157
$this->httpMethods = $httpMethods;
5258
$this->protectedControllers = $protectedControllers;
59+
$this->prefix = $prefix;
5360
}
5461

5562
/**
@@ -82,9 +89,18 @@ public function get(): array
8289
$routes = $this->addFilters($routes);
8390

8491
foreach ($routes as $item) {
92+
$route = $item['route'] . $item['route_params'];
93+
94+
// For module routing
95+
if ($this->prefix !== '' && $route === '/') {
96+
$route = $this->prefix;
97+
} elseif ($this->prefix !== '') {
98+
$route = $this->prefix . '/' . $route;
99+
}
100+
85101
$tbody[] = [
86102
strtoupper($item['method']) . '(auto)',
87-
$item['route'] . $item['route_params'],
103+
$route,
88104
'',
89105
$item['handler'],
90106
$item['before'],
@@ -101,13 +117,22 @@ private function addFilters($routes)
101117
$filterCollector = new FilterCollector(true);
102118

103119
foreach ($routes as &$route) {
120+
$routePath = $route['route'];
121+
122+
// For module routing
123+
if ($this->prefix !== '' && $route === '/') {
124+
$routePath = $this->prefix;
125+
} elseif ($this->prefix !== '') {
126+
$routePath = $this->prefix . '/' . $routePath;
127+
}
128+
104129
// Search filters for the URI with all params
105130
$sampleUri = $this->generateSampleUri($route);
106-
$filtersLongest = $filterCollector->get($route['method'], $route['route'] . $sampleUri);
131+
$filtersLongest = $filterCollector->get($route['method'], $routePath . $sampleUri);
107132

108133
// Search filters for the URI without optional params
109134
$sampleUri = $this->generateSampleUri($route, false);
110-
$filtersShortest = $filterCollector->get($route['method'], $route['route'] . $sampleUri);
135+
$filtersShortest = $filterCollector->get($route['method'], $routePath . $sampleUri);
111136

112137
// Get common array elements
113138
$filters['before'] = array_intersect($filtersLongest['before'], $filtersShortest['before']);

system/Router/AutoRouterImproved.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use CodeIgniter\Exceptions\PageNotFoundException;
1515
use CodeIgniter\Router\Exceptions\MethodNotFoundException;
16+
use Config\Routing;
1617
use ReflectionClass;
1718
use ReflectionException;
1819

@@ -112,6 +113,15 @@ public function getRoute(string $uri): array
112113
{
113114
$segments = explode('/', $uri);
114115

116+
// Check for Module Routes.
117+
if (
118+
($routingConfig = config(Routing::class))
119+
&& array_key_exists($segments[0], $routingConfig->moduleRoutes)
120+
) {
121+
$uriSegment = array_shift($segments);
122+
$this->namespace = rtrim($routingConfig->moduleRoutes[$uriSegment], '\\') . '\\';
123+
}
124+
115125
// WARNING: Directories get shifted out of the segments array.
116126
$nonDirSegments = $this->scanControllers($segments);
117127

tests/system/Router/AutoRouterImprovedTest.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace CodeIgniter\Router;
1313

14+
use CodeIgniter\Config\Factories;
1415
use CodeIgniter\Config\Services;
1516
use CodeIgniter\Exceptions\PageNotFoundException;
1617
use CodeIgniter\Router\Controllers\Dash_folder\Dash_controller;
@@ -39,11 +40,11 @@ protected function setUp(): void
3940
$this->collection = new RouteCollection(Services::locator(), $moduleConfig, new Routing());
4041
}
4142

42-
private function createNewAutoRouter(string $httpVerb = 'get'): AutoRouterImproved
43+
private function createNewAutoRouter(string $httpVerb = 'get', $namespace = 'CodeIgniter\Router\Controllers'): AutoRouterImproved
4344
{
4445
return new AutoRouterImproved(
4546
[],
46-
'CodeIgniter\Router\Controllers',
47+
$namespace,
4748
$this->collection->getDefaultController(),
4849
$this->collection->getDefaultMethod(),
4950
true,
@@ -66,6 +67,27 @@ public function testAutoRouteFindsDefaultControllerAndMethodGet()
6667
$this->assertSame([], $params);
6768
}
6869

70+
public function testAutoRouteFindsModuleDefaultControllerAndMethodGet()
71+
{
72+
$config = config(Routing::class);
73+
$config->moduleRoutes = [
74+
'test' => 'CodeIgniter\Router\Controllers',
75+
];
76+
Factories::injectMock('config', Routing::class, $config);
77+
78+
$this->collection->setDefaultController('Index');
79+
80+
$router = $this->createNewAutoRouter('get', 'App/Controllers');
81+
82+
[$directory, $controller, $method, $params]
83+
= $router->getRoute('test');
84+
85+
$this->assertNull($directory);
86+
$this->assertSame('\\' . Index::class, $controller);
87+
$this->assertSame('getIndex', $method);
88+
$this->assertSame([], $params);
89+
}
90+
6991
public function testAutoRouteFindsDefaultControllerAndMethodPost()
7092
{
7193
$this->collection->setDefaultController('Index');

user_guide_src/source/changelogs/v4.4.0.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ Others
9191
the ``Content-Disposition: inline`` header to display the file in the browser.
9292
See :ref:`open-file-in-browser` for details.
9393
- **View:** Added optional 2nd parameter ``$saveData`` on ``renderSection()`` to prevent from auto cleans the data after displaying. See :ref:`View Layouts <creating-a-layout>` for details.
94+
- **Auto Routing (Improved)**: Now you can route to Modules.
95+
See :ref:`auto-routing-improved-module-routing` for details.
9496
- **Auto Routing (Improved)**: Now you can use URI without a method name like
9597
``product/15`` where ``15`` is an arbitrary number.
9698
See :ref:`controller-default-method-fallback` for details.

user_guide_src/source/incoming/routing.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,32 @@ In this example, if the user were to visit **example.com/products**, and a ``Pro
726726
.. important:: You cannot access the controller with the URI of the default method name.
727727
In the example above, you can access **example.com/products**, but if you access **example.com/products/listall**, it will be not found.
728728

729+
.. _auto-routing-improved-module-routing:
730+
731+
Module Routing
732+
==============
733+
734+
.. versionadded:: 4.4.0
735+
736+
You can use auto routing even if you use :doc:`../general/modules` and place
737+
the controllers in a different namespace.
738+
739+
To route to a module, the ``$moduleRoutes`` property in **app/Config/Routing.php**
740+
must be set::
741+
742+
public array $moduleRoutes = [
743+
'blog' => 'Acme\Blog\Controllers',
744+
];
745+
746+
The key is the first URI segment for the module, and the value is the controller
747+
namespace. In the above configuration, **http://localhost:8080/blog/foo/bar**
748+
will be routed to ``Acme\Blog\Controllers\Foo::getBar()``.
749+
750+
.. note:: If you define ``$moduleRoutes``, the routing for the module takes
751+
precedence. In the above example, even if you have the ``App\Controllers\Blog``
752+
controller, **http://localhost:8080/blog** will be routed to the default
753+
controller ``Acme\Blog\Controllers\Home``.
754+
729755
.. _auto-routing-legacy:
730756

731757
Auto Routing (Legacy)

0 commit comments

Comments
 (0)