Skip to content

Commit

Permalink
Merge pull request #7416 from kenjis/feat-auto-routing-module
Browse files Browse the repository at this point in the history
feat: [Auto Routing Improved] Module Routing
  • Loading branch information
kenjis authored Apr 12, 2023
2 parents 5be9023 + a72ea47 commit c5b8676
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 6 deletions.
13 changes: 13 additions & 0 deletions app/Config/Routing.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,17 @@ class Routing extends BaseRouting
* Default: false
*/
public bool $prioritize = false;

/**
* Map of URI segments and namespaces. For Auto Routing (Improved).
*
* The key is the first URI segment. The value is the controller namespace.
* E.g.,
* [
* 'blog' => 'Acme\Blog\Controllers',
* ]
*
* @var array [ uri_segment => namespace ]
*/
public array $moduleRoutes = [];
}
17 changes: 17 additions & 0 deletions system/Commands/Utilities/Routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use CodeIgniter\Commands\Utilities\Routes\AutoRouterImproved\AutoRouteCollector as AutoRouteCollectorImproved;
use CodeIgniter\Commands\Utilities\Routes\FilterCollector;
use CodeIgniter\Commands\Utilities\Routes\SampleURIGenerator;
use Config\Routing;
use Config\Services;

/**
Expand Down Expand Up @@ -152,6 +153,22 @@ public function run(array $params)
);

$autoRoutes = $autoRouteCollector->get();

// Check for Module Routes.
if ($routingConfig = config(Routing::class)) {
foreach ($routingConfig->moduleRoutes as $uri => $namespace) {
$autoRouteCollector = new AutoRouteCollectorImproved(
$namespace,
$collection->getDefaultController(),
$collection->getDefaultMethod(),
$methods,
$collection->getRegisteredControllers('*'),
$uri
);

$autoRoutes = [...$autoRoutes, ...$autoRouteCollector->get()];
}
}
} else {
$autoRouteCollector = new AutoRouteCollector(
$collection->getDefaultNamespace(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ final class AutoRouteCollector
*/
private array $protectedControllers;

/**
* @var string URI prefix for Module Routing
*/
private string $prefix;

/**
* @param string $namespace namespace to search
*/
Expand All @@ -43,13 +48,15 @@ public function __construct(
string $defaultController,
string $defaultMethod,
array $httpMethods,
array $protectedControllers
array $protectedControllers,
string $prefix = ''
) {
$this->namespace = $namespace;
$this->defaultController = $defaultController;
$this->defaultMethod = $defaultMethod;
$this->httpMethods = $httpMethods;
$this->protectedControllers = $protectedControllers;
$this->prefix = $prefix;
}

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

foreach ($routes as $item) {
$route = $item['route'] . $item['route_params'];

// For module routing
if ($this->prefix !== '' && $route === '/') {
$route = $this->prefix;
} elseif ($this->prefix !== '') {
$route = $this->prefix . '/' . $route;
}

$tbody[] = [
strtoupper($item['method']) . '(auto)',
$item['route'] . $item['route_params'],
$route,
'',
$item['handler'],
$item['before'],
Expand All @@ -101,13 +117,22 @@ private function addFilters($routes)
$filterCollector = new FilterCollector(true);

foreach ($routes as &$route) {
$routePath = $route['route'];

// For module routing
if ($this->prefix !== '' && $route === '/') {
$routePath = $this->prefix;
} elseif ($this->prefix !== '') {
$routePath = $this->prefix . '/' . $routePath;
}

// Search filters for the URI with all params
$sampleUri = $this->generateSampleUri($route);
$filtersLongest = $filterCollector->get($route['method'], $route['route'] . $sampleUri);
$filtersLongest = $filterCollector->get($route['method'], $routePath . $sampleUri);

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

// Get common array elements
$filters['before'] = array_intersect($filtersLongest['before'], $filtersShortest['before']);
Expand Down
10 changes: 10 additions & 0 deletions system/Router/AutoRouterImproved.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\Router\Exceptions\MethodNotFoundException;
use Config\Routing;
use ReflectionClass;
use ReflectionException;

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

// Check for Module Routes.
if (
($routingConfig = config(Routing::class))
&& array_key_exists($segments[0], $routingConfig->moduleRoutes)
) {
$uriSegment = array_shift($segments);
$this->namespace = rtrim($routingConfig->moduleRoutes[$uriSegment], '\\') . '\\';
}

// WARNING: Directories get shifted out of the segments array.
$nonDirSegments = $this->scanControllers($segments);

Expand Down
26 changes: 24 additions & 2 deletions tests/system/Router/AutoRouterImprovedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace CodeIgniter\Router;

use CodeIgniter\Config\Factories;
use CodeIgniter\Config\Services;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\Router\Controllers\Dash_folder\Dash_controller;
Expand Down Expand Up @@ -39,11 +40,11 @@ protected function setUp(): void
$this->collection = new RouteCollection(Services::locator(), $moduleConfig, new Routing());
}

private function createNewAutoRouter(string $httpVerb = 'get'): AutoRouterImproved
private function createNewAutoRouter(string $httpVerb = 'get', $namespace = 'CodeIgniter\Router\Controllers'): AutoRouterImproved
{
return new AutoRouterImproved(
[],
'CodeIgniter\Router\Controllers',
$namespace,
$this->collection->getDefaultController(),
$this->collection->getDefaultMethod(),
true,
Expand All @@ -66,6 +67,27 @@ public function testAutoRouteFindsDefaultControllerAndMethodGet()
$this->assertSame([], $params);
}

public function testAutoRouteFindsModuleDefaultControllerAndMethodGet()
{
$config = config(Routing::class);
$config->moduleRoutes = [
'test' => 'CodeIgniter\Router\Controllers',
];
Factories::injectMock('config', Routing::class, $config);

$this->collection->setDefaultController('Index');

$router = $this->createNewAutoRouter('get', 'App/Controllers');

[$directory, $controller, $method, $params]
= $router->getRoute('test');

$this->assertNull($directory);
$this->assertSame('\\' . Index::class, $controller);
$this->assertSame('getIndex', $method);
$this->assertSame([], $params);
}

public function testAutoRouteFindsDefaultControllerAndMethodPost()
{
$this->collection->setDefaultController('Index');
Expand Down
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Others
the ``Content-Disposition: inline`` header to display the file in the browser.
See :ref:`open-file-in-browser` for details.
- **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.
- **Auto Routing (Improved)**: Now you can route to Modules.
See :ref:`auto-routing-improved-module-routing` for details.
- **Auto Routing (Improved)**: Now you can use URI without a method name like
``product/15`` where ``15`` is an arbitrary number.
See :ref:`controller-default-method-fallback` for details.
Expand Down
26 changes: 26 additions & 0 deletions user_guide_src/source/incoming/routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,32 @@ In this example, if the user were to visit **example.com/products**, and a ``Pro
.. important:: You cannot access the controller with the URI of the default method name.
In the example above, you can access **example.com/products**, but if you access **example.com/products/listall**, it will be not found.

.. _auto-routing-improved-module-routing:

Module Routing
==============

.. versionadded:: 4.4.0

You can use auto routing even if you use :doc:`../general/modules` and place
the controllers in a different namespace.

To route to a module, the ``$moduleRoutes`` property in **app/Config/Routing.php**
must be set::

public array $moduleRoutes = [
'blog' => 'Acme\Blog\Controllers',
];

The key is the first URI segment for the module, and the value is the controller
namespace. In the above configuration, **http://localhost:8080/blog/foo/bar**
will be routed to ``Acme\Blog\Controllers\Foo::getBar()``.

.. note:: If you define ``$moduleRoutes``, the routing for the module takes
precedence. In the above example, even if you have the ``App\Controllers\Blog``
controller, **http://localhost:8080/blog** will be routed to the default
controller ``Acme\Blog\Controllers\Home``.

.. _auto-routing-legacy:

Auto Routing (Legacy)
Expand Down

0 comments on commit c5b8676

Please sign in to comment.