diff --git a/app/Config/Feature.php b/app/Config/Feature.php index efd4a0b20ae8..35024d357919 100644 --- a/app/Config/Feature.php +++ b/app/Config/Feature.php @@ -10,9 +10,9 @@ class Feature extends BaseConfig { /** - * Use improved new auto routing instead of the default legacy version. + * Use improved new auto routing instead of the legacy version. */ - public bool $autoRoutesImproved = false; + public bool $autoRoutesImproved = true; /** * Use filter execution order in 4.4 or before. diff --git a/app/Config/Routing.php b/app/Config/Routing.php index 7abadc7b76f0..3005543a9e79 100644 --- a/app/Config/Routing.php +++ b/app/Config/Routing.php @@ -136,5 +136,5 @@ class Routing extends BaseRouting * * Default: false */ - public bool $translateUriToCamelCase = false; + public bool $translateUriToCamelCase = true; } diff --git a/tests/system/Commands/RoutesTest.php b/tests/system/Commands/RoutesTest.php index 2ee9f44f1bac..60b326dee35e 100644 --- a/tests/system/Commands/RoutesTest.php +++ b/tests/system/Commands/RoutesTest.php @@ -16,6 +16,7 @@ use CodeIgniter\Router\RouteCollection; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\StreamFilterTrait; +use Config\Feature; use Config\Services; use PHPUnit\Framework\Attributes\Group; @@ -213,6 +214,8 @@ public function testRoutesCommandRouteLegacy(): void $routes = $this->getCleanRoutes(); $routes->loadRoutes(); + $featureConfig = config(Feature::class); + $featureConfig->autoRoutesImproved = false; $routes->setAutoRoute(true); command('routes'); diff --git a/tests/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReaderTest.php b/tests/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReaderTest.php index 936b2b8fe184..2eaabe3cd833 100644 --- a/tests/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReaderTest.php +++ b/tests/system/Commands/Utilities/Routes/AutoRouterImproved/ControllerMethodReaderTest.php @@ -74,8 +74,9 @@ public function testRead(): void public function testReadTranslateURIDashes(): void { - $config = config(Routing::class); - $config->translateURIDashes = true; + $config = config(Routing::class); + $config->translateURIDashes = true; + $config->translateUriToCamelCase = false; Factories::injectMock('config', Routing::class, $config); $reader = $this->createControllerMethodReader( diff --git a/tests/system/Router/AutoRouterImprovedTest.php b/tests/system/Router/AutoRouterImprovedTest.php index 5975280bcdfd..7e6f639f7ddc 100644 --- a/tests/system/Router/AutoRouterImprovedTest.php +++ b/tests/system/Router/AutoRouterImprovedTest.php @@ -204,8 +204,16 @@ public function testAutoRouteFindsControllerWithSubSubfolder(): void $this->assertSame([], $params); } + private function disableTranslateUriToCamelCase(): void + { + $routingConfig = config(Routing::class); + $routingConfig->translateUriToCamelCase = false; + } + public function testAutoRouteFindsDashedSubfolder(): void { + $this->disableTranslateUriToCamelCase(); + $router = $this->createNewAutoRouter(); [$directory, $controller, $method, $params] @@ -222,6 +230,8 @@ public function testAutoRouteFindsDashedSubfolder(): void public function testAutoRouteFindsDashedController(): void { + $this->disableTranslateUriToCamelCase(); + $router = $this->createNewAutoRouter(); [$directory, $controller, $method, $params] @@ -235,6 +245,8 @@ public function testAutoRouteFindsDashedController(): void public function testAutoRouteFindsDashedMethod(): void { + $this->disableTranslateUriToCamelCase(); + $router = $this->createNewAutoRouter(); [$directory, $controller, $method, $params] @@ -248,6 +260,8 @@ public function testAutoRouteFindsDashedMethod(): void public function testAutoRouteFindsDefaultDashFolder(): void { + $this->disableTranslateUriToCamelCase(); + $router = $this->createNewAutoRouter(); [$directory, $controller, $method, $params] @@ -438,6 +452,8 @@ public function testRejectsURIWithUnderscoreController(): void 'AutoRouterImproved prohibits access to the URI containing underscores ("dash_controller")' ); + $this->disableTranslateUriToCamelCase(); + $router = $this->createNewAutoRouter(); $router->getRoute('dash-folder/dash_controller/dash-method', Method::GET); @@ -450,6 +466,8 @@ public function testRejectsURIWithUnderscoreMethod(): void 'AutoRouterImproved prohibits access to the URI containing underscores ("dash_method")' ); + $this->disableTranslateUriToCamelCase(); + $router = $this->createNewAutoRouter(); $router->getRoute('dash-folder/dash-controller/dash_method', Method::GET); diff --git a/tests/system/Router/RouteCollectionTest.php b/tests/system/Router/RouteCollectionTest.php index 89959cd5f33e..6f623196db69 100644 --- a/tests/system/Router/RouteCollectionTest.php +++ b/tests/system/Router/RouteCollectionTest.php @@ -19,6 +19,7 @@ use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\HTTP\Method; use CodeIgniter\Test\CIUnitTestCase; +use Config\Feature; use Config\Modules; use Config\Routing; use PHPUnit\Framework\Attributes\DataProvider; @@ -1768,12 +1769,12 @@ public static function provideRouteDefaultNamespace(): iterable ]; } - /** - * @param mixed $namespace - */ #[DataProvider('provideRouteDefaultNamespace')] - public function testAutoRoutesControllerNameReturnsFQCN($namespace): void + public function testAutoRoutesControllerNameReturnsFQCN(string $namespace): void { + $featureConfig = config(Feature::class); + $featureConfig->autoRoutesImproved = false; + $routes = $this->getCollector(); $routes->setAutoRoute(true); $routes->setDefaultNamespace($namespace); @@ -1788,11 +1789,8 @@ public function testAutoRoutesControllerNameReturnsFQCN($namespace): void $this->assertSame('\\' . Product::class, $router->controllerName()); } - /** - * @param mixed $namespace - */ #[DataProvider('provideRouteDefaultNamespace')] - public function testRoutesControllerNameReturnsFQCN($namespace): void + public function testRoutesControllerNameReturnsFQCN(string $namespace): void { Services::request()->setMethod(Method::GET); $routes = $this->getCollector(); diff --git a/tests/system/Router/RouterTest.php b/tests/system/Router/RouterTest.php index bb5302e721dc..e21763b9dc8a 100644 --- a/tests/system/Router/RouterTest.php +++ b/tests/system/Router/RouterTest.php @@ -23,6 +23,7 @@ use CodeIgniter\Router\Exceptions\RouterException; use CodeIgniter\Test\CIUnitTestCase; use Config\App; +use Config\Feature; use Config\Modules; use Config\Routing; use PHPUnit\Framework\Attributes\DataProvider; @@ -42,12 +43,19 @@ protected function setUp(): void { parent::setUp(); + $this->disableAutoRoutesImproved(); $this->createRouteCollection(); $this->request = Services::request(); $this->request->setMethod(Method::GET); } + private function disableAutoRoutesImproved(): void + { + $featureConfig = config(Feature::class); + $featureConfig->autoRoutesImproved = false; + } + private function createRouteCollection(?Routing $routingConfig = null): void { $moduleConfig = new Modules(); diff --git a/tests/system/Test/FeatureTestTraitTest.php b/tests/system/Test/FeatureTestTraitTest.php index 4fd921d6e9a6..b3ee3eadb4cc 100644 --- a/tests/system/Test/FeatureTestTraitTest.php +++ b/tests/system/Test/FeatureTestTraitTest.php @@ -20,6 +20,7 @@ use CodeIgniter\HTTP\Response; use CodeIgniter\Test\Mock\MockCodeIgniter; use Config\App; +use Config\Feature; use Config\Routing; use Config\Services; use PHPUnit\Framework\Attributes\DataProvider; @@ -359,17 +360,19 @@ public static function provideOpenCliRoutesFromHttpGot404(): iterable ]; } - /** - * @param mixed $from - * @param mixed $to - * @param mixed $httpGet - */ + private function disableAutoRoutesImproved(): void + { + $featureConfig = config(Feature::class); + $featureConfig->autoRoutesImproved = false; + } + #[DataProvider('provideOpenCliRoutesFromHttpGot404')] - public function testOpenCliRoutesFromHttpGot404($from, $to, $httpGet): void + public function testOpenCliRoutesFromHttpGot404(string $from, string $to, string $httpGet): void { $this->expectException(PageNotFoundException::class); $this->expectExceptionMessage('Cannot access CLI Route: '); + $this->disableAutoRoutesImproved(); $collection = Services::routes(); $collection->setAutoRoute(true); $collection->setDefaultNamespace('Tests\Support\Controllers'); @@ -641,6 +644,7 @@ public function testSetupRequestBodyWithBody(): void public function testAutoRoutingLegacy(): void { + $this->disableAutoRoutesImproved(); $config = config(Routing::class); $config->autoRoute = true; Factories::injectMock('config', Routing::class, $config); diff --git a/user_guide_src/source/changelogs/v4.4.0.rst b/user_guide_src/source/changelogs/v4.4.0.rst index 59092ccd9860..df0b712f3f35 100644 --- a/user_guide_src/source/changelogs/v4.4.0.rst +++ b/user_guide_src/source/changelogs/v4.4.0.rst @@ -261,7 +261,7 @@ Others - If a controller is found that corresponds to a URI segment and that controller does not have a method defined for the URI segment, the default method will now be executed. This addition allows for more flexible handling of URIs in - auto routing. See :ref:`controller-default-method-fallback` for details. + auto routing. See :ref:`auto-routing-improved-default-method-fallback` for details. - **Filters:** Now you can use Filter Arguments with :ref:`$filters property `. - **Request:** Added ``IncomingRequest::setValidLocales()`` method to set valid locales. - **Table:** Added ``Table::setSyncRowsWithHeading()`` method to synchronize row columns with headings. See :ref:`table-sync-rows-with-headings` for details. diff --git a/user_guide_src/source/changelogs/v4.5.0.rst b/user_guide_src/source/changelogs/v4.5.0.rst index 1f978a4dcfc6..97cfe9ec10b0 100644 --- a/user_guide_src/source/changelogs/v4.5.0.rst +++ b/user_guide_src/source/changelogs/v4.5.0.rst @@ -62,7 +62,7 @@ Routing - **AutoRouting Improved:** The ``$translateUriToCamelCase`` option has been added that allows using CamelCase controller and method names. See - :ref:`controller-translate-uri-to-camelcase`. + :ref:`translate-uri-to-camelcase`. - **Others:** - Added option ``$multipleSegmentsOneParam``. When this option is enabled, a placeholder that matches multiple segments, such as ``(:any)``, will diff --git a/user_guide_src/source/incoming/auto_routing_improved.rst b/user_guide_src/source/incoming/auto_routing_improved.rst new file mode 100644 index 000000000000..1ed1cb304c6b --- /dev/null +++ b/user_guide_src/source/incoming/auto_routing_improved.rst @@ -0,0 +1,500 @@ +.. _auto-routing-improved: + +####################### +Auto Routing (Improved) +####################### + +.. versionadded:: 4.2.0 + +.. contents:: + :local: + :depth: 3 + +********************************* +What is Auto Routing (Improved) ? +********************************* + +By default, all routes must be :ref:`defined ` in the +configuration file. + +However, with **Auto Routing (Improved)**, you can define the controller name and +its method name according to the convention and it will be automatically routed. +In other words, there is no need to define routes manually. + +If you enable Auto Routing (Improved), when no defined route is found that matches +the URI, the system will attempt to match that URI against the controllers and +methods. + +.. important:: For security reasons, if a controller is used in the defined routes, + Auto Routing (Improved) does not route to the controller. + +.. note:: Auto Routing (Improved) is disabled by default. To use it, see + :ref:`enabled-auto-routing-improved`. + +************************************** +Differences from Auto Routing (Legacy) +************************************** + +:ref:`auto-routing-legacy` is a routing system from CodeIgniter 3. If you are not +familiar with it, go to the next section. + +If you know it well, these are some changes in **Auto Routing (Improved)**: + +- A controller method needs HTTP verb prefix like ``getIndex()``, ``postCreate()``. + - Since developers always know the HTTP method, a request with an unexpected + HTTP method will never execute the controller. +- The Default Controller (``Home`` by default) and the Default Method (``index`` by default) must be omitted in the URI. + - It restricts one-to-one correspondence between controller methods and URIs. + - E.g. by default, you can access ``/``, but ``/home`` and ``/home/index`` + will be 404 Not Found. +- It checks method parameter count. + - If there are more parameters in the URI than the method parameters, it results + in 404 Not Found. +- It does not support ``_remap()`` method. + - It restricts one-to-one correspondence between controller methods and URIs. + - But it has the :ref:`auto-routing-improved-default-method-fallback` feature + instead. +- Can't access controllers in Defined Routes. + - It completely separates controllers accessible via **Auto Routing** from + those accessible via **Defined Routes**. + +.. _enabled-auto-routing-improved: + +****************************** +Enable Auto Routing (Improved) +****************************** + +To use it, you need to change the setting ``$autoRoute`` option to ``true`` in +**app/Config/Routing.php**:: + + public bool $autoRoute = true; + +And you need to change the property ``$autoRoutesImproved`` to ``true`` in +**app/Config/Feature.php**:: + + public bool $autoRoutesImproved = true; + +.. important:: When you use Auto Routing (Improved), you must remove the line + ``$routes->get('/', 'Home::index');`` in **app/Config/Routes.php**. Because + defined routes take precedence over Auto Routing, and controllers defined in + the defined routes are denied access by Auto Routing (Improved) for security + reasons. + +************ +URI Segments +************ + +The segments in the URL, in following with the Model-View-Controller approach, +usually represent:: + + http://example.com/{class}/{method}/{param1} + +1. The first segment represents the controller **class** that should be invoked. +2. The second segment represents the class **method** that should be called. +3. The third, and any additional segments, represent any **parameters** that will be passed to the controller method. + +Consider this URI:: + + http://example.com/hello-world/hello/1 + +In the above example, when you send an HTTP request with **GET** method, +Auto Routing (Improved) would attempt to find a controller named +``App\Controllers\HelloWorld`` and executes ``getHello()`` method with passing +``'1'`` as the first parameter. + +.. note:: A controller method that will be executed by Auto Routing (Improved) + needs HTTP verb (``get``, ``post``, ``put``, etc.) prefix like ``getIndex()``, + ``postCreate()``. + +.. note:: When a controller's short name matches the first segment of a URI, it + will be loaded. + +************************** +Let's try it: Hello World! +************************** + +Let's create a simple controller so you can see it in action. + +Create a Controller +=================== + +Using your text editor, create a file called **HelloWorld.php** in your +**app/Controllers** directory, and put the following code in it. + +.. literalinclude:: auto_routing_improved/020.php + +.. important:: The file must be called **HelloWorld.php**. When you use Auto + Routing (Improved), controller class names MUST be CamelCase. + +You will notice that the ``HelloWorld`` Controller is extending the ``BaseController``. +You can also extend the ``CodeIgniter\Controller`` if you do not need the functionality +of the BaseController. + +The BaseController provides a convenient place for loading components and performing +functions that are needed by all your controllers. You can extend this class in +any new controller. + +.. important:: A controller method that will be executed by Auto Routing (Improved) + needs HTTP verb (``get``, ``post``, ``put``, etc.) prefix like ``getIndex()``, + ``postCreate()``. + +Check the Routes +================ + +You can check your routes with the ``spark routes`` command. + +.. code-block:: console + + php spark routes + +If you did it right, you should see: + +.. code-block:: none + + +-----------+-------------+------+---------------------------------------+----------------+---------------+ + | Method | Route | Name | Handler | Before Filters | After Filters | + +-----------+-------------+------+---------------------------------------+----------------+---------------+ + | GET(auto) | hello-world | | \App\Controllers\HelloWorld::getIndex | | | + +-----------+-------------+------+---------------------------------------+----------------+---------------+ + +See :ref:`routing-spark-routes` for the output details. + +Visit Your Site +=============== + +Now visit your site using a URL similar to this:: + + http://example.com/hello-world + +The system automatically translates URI with dashes (``-``) to CamelCase in the +controller and method URI segments. + +For example, the URI ``sub-dir/hello-controller/some-method`` will execute the +``SubDir\HelloController::getSomeMethod()`` method. + +If you did it right, you should see:: + + Hello World! + +**************************** +Examples of Controller Names +**************************** + +The following is an valid controller name. Because ``App\Controllers\HelloWorld`` +is CamelCase. + +.. literalinclude:: auto_routing_improved/009.php + +The following is **not** valid. Because the first letter (``h``) is not capital. + +.. literalinclude:: auto_routing_improved/010.php + +The following is also **not** valid. Because the first letter (``h``) is not capital. + +.. literalinclude:: auto_routing_improved/011.php + +****************** +Controller Methods +****************** + +Method Visibility +================= + +When you define a method that is executable via HTTP request, the method must be +declared as ``public``. + +.. warning:: For security reasons be sure to declare any new utility methods as + ``protected`` or ``private``. + +Default Method +============== + +In the above example, the method name is ``getIndex()``. The method +(HTTP verb + ``Index()``) is called the **Default Method**, and is loaded if the +**second segment** of the URI is empty. + +Normal Methods +============== + +The second segment of the URI determines which method in the controller gets called. + +Let's try it. Add a new method to your controller: + +.. literalinclude:: auto_routing_improved/021.php + +Now load the following URL to see the ``getComment()`` method:: + + http://example.com/hello-world/comment/ + +You should see your new message. + +************************************ +Passing URI Segments to Your Methods +************************************ + +If your URI contains more than two segments they will be passed to your +method as parameters. + +For example, let's say you have a URI like this:: + + http://example.com/products/shoes/sandals/123 + +Your method will be passed URI segments 3 and 4 (``'sandals'`` and ``'123'``): + +.. literalinclude:: auto_routing_improved/022.php + +.. note:: If there are more parameters in the URI than the method parameters, + Auto Routing (Improved) does not execute the method, and it results in 404 + Not Found. + +****************** +Default Controller +****************** + +The **Default Controller** is a special controller that is used when a URI ends +with a directory name or when a URI is not present, as will be the case when only +your site root URL is requested. + +By default, the Default Controller is ``Home``. + +.. note:: Define only the default method (``getIndex()`` for GET requests) + in the default controller. If you define any other public method, that method + will not be executed. + +For more information, please refer to the +:ref:`routing-auto-routing-improved-configuration-options`. + +.. _auto-routing-improved-default-method-fallback: + +*********************** +Default Method Fallback +*********************** + +.. versionadded:: 4.4.0 + +If the controller method corresponding to the URI segment of the method name +does not exist, and if the default method is defined, the remaining URI segments +are passed to the default method for execution. + +.. literalinclude:: controllers/024.php + +Load the following URL:: + + http://example.com/product/15/edit + +The method will be passed URI segments 2 and 3 (``'15'`` and ``'edit'``): + +.. important:: If there are more parameters in the URI than the method parameters, + Auto Routing (Improved) does not execute the method, and it results in 404 + Not Found. + +Fallback to Default Controller +============================== + +If the controller corresponding to the URI segment of the controller name +does not exist, and if the default controller (``Home`` by default) exists in +the directory, the remaining URI segments are passed to the default controller's +default method. + +For example, when you have the following default controller ``Home`` in the +**app/Controllers/News** directory: + +.. literalinclude:: controllers/025.php + +Load the following URL:: + + http://example.com/news/101 + +The ``News\Home`` controller and the default ``getIndex()`` method will be found. +So the default method will get the second URI segment (``'101'``): + +.. note:: If there is ``App\Controllers\News`` controller, it takes precedence. + The URI segments are searched sequentially and the first controller found + is used. + +.. note:: If there are more parameters in the URI than the method parameters, + Auto Routing (Improved) does not execute the method, and it results in 404 + Not Found. + +************************************************ +Organizing Your Controllers into Sub-directories +************************************************ + +If you are building a large application you might want to hierarchically +organize or structure your controllers into sub-directories. CodeIgniter +permits you to do this. + +Simply create sub-directories under the main **app/Controllers**, +and place your controller classes within them. + +.. important:: Directory names MUST start with an uppercase letter and be CamelCase. + +When using this feature the first segment of your URI must +specify the directory. For example, let's say you have a controller located here:: + + app/Controllers/Products/Shoes.php + +To call the above controller your URI will look something like this:: + + http://example.com/products/shoes/show/123 + +.. note:: You cannot have directories with the same name in **app/Controllers** + and **public**. + This is because if there is a directory, the web server will search for it and + it will not be routed to CodeIgniter. + +Each of your sub-directories may contain a default controller which will be +called if the URL contains *only* the sub-directory. Simply put a controller +in there that matches the name of your default controller as specified in +your **app/Config/Routing.php** file. + +*************************************** +Examples of Controller/Methods and URIs +*************************************** + +In the case of a **GET** request with the default configuration, the mapping +between controller/methods and URIs is as follows: + +============================ ============================ ============================================= +Controller/Method URI Description +============================ ============================ ============================================= +``Home::getIndex()`` / The default controller and the default method. +``Blog::getIndex()`` /blog The default method. +``UserProfile::getIndex()`` /user-profile The default method. +``Blog::getTags()`` /blog/tags +``Blog::getNews($id)`` /blog/news/123 +``Blog\Home::getIndex()`` /blog Sub-directory ``Blog`` and the default + controller and the default method. If there + is ``Blog`` controller, it takes precedence. +``Blog\Tags::getIndex()`` /blog/tags Sub-directory ``Blog`` and the default + method. If there is ``Blog`` controller, it + takes precedence. +``Blog\News::getIndex($id)`` /blog/news/123 Sub-directory ``Blog`` and the default method + fallback. If there is ``Blog`` controller, it + takes precedence. +============================ ============================ ============================================= + +**************** +Applying Filters +**************** + +Applying controller filters allows you to add processing before and after the +controller method execution. This is especially handy during authentication or +api logging. + +If you use Auto Routing, set the filters to be applied in **app/Config/Filters.php**. +See :doc:`Controller Filters ` for more information on setting up filters. + +.. _routing-auto-routing-improved-configuration-options: + +********************* +Configuration Options +********************* + +These options are available in the **app/Config/Routing.php** file. + +Default Controller +================== + +For Site Root URI +----------------- + +When a user visits the root of your site (i.e., **http://example.com**) the controller +to use is determined by the value set to the ``$defaultController`` property, +unless a route exists for it explicitly. + +The default value for this is ``Home`` which matches the controller at +**app/Controllers/Home.php**:: + + public string $defaultController = 'Home'; + +For Directory URI +----------------- + +The default controller is also used when no matching route has been found, and +the URI would point to a directory in the controllers directory. For example, if +the user visits **http://example.com/admin**, if a controller was found at +**app/Controllers/Admin/Home.php**, it would be used. + +.. important:: You cannot access the default controller with the URI of the + controller name. When the default controller is ``Home``, you can access + **http://example.com/**, but if you access **http://example.com/home**, it + will be not found. + +.. _routing-auto-routing-improved-default-method: + +Default Method +============== + +This works similar to the default controller setting, but is used to determine +the default method that is used when a controller is found that matches the URI, +but no segment exists for the method. The default value is ``index``. + +In this example, if the user were to visit **example.com/products**, and a ``Products`` +controller existed, the ``Products::getListAll()`` method would be executed:: + + public string $defaultMethod = 'listAll'; + +.. 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. + +.. _translate-uri-to-camelcase: + +Translate URI To CamelCase +========================== + +.. versionadded:: 4.5.0 + +.. note:: Since v4.6.0, the ``$translateUriToCamelCase`` option is enabled by + default. + +Since v4.5.0, the ``$translateUriToCamelCase`` option has been implemented, +which works well with the current CodeIgniter's coding standards. + +This option enables you to automatically translate URI with dashes (``-``) to +CamelCase in the controller and method URI segments. + +For example, the URI ``sub-dir/hello-controller/some-method`` will execute the +``SubDir\HelloController::getSomeMethod()`` method. + +.. note:: When this option is enabled, the ``$translateURIDashes`` option is + ignored. + +Disable Translate URI To CamelCase +---------------------------------- + +.. note:: The option to disable "Translate URI To CamelCase" exists only for + backward compatibility. We don't recommend to disable it. + +To disable it, you need to change the setting ``$translateUriToCamelCase`` option +to ``false`` in **app/Config/Routing.php**:: + + public bool $translateUriToCamelCase = false; + +.. _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``. diff --git a/user_guide_src/source/incoming/auto_routing_improved/009.php b/user_guide_src/source/incoming/auto_routing_improved/009.php new file mode 100644 index 000000000000..7615eecaeba0 --- /dev/null +++ b/user_guide_src/source/incoming/auto_routing_improved/009.php @@ -0,0 +1,8 @@ +`. - -This section describes the functionality of the new auto-routing. -It automatically routes an HTTP request, and executes the corresponding controller method -without route definitions. - -Since v4.2.0, the auto-routing is disabled by default. To use it, see :ref:`enabled-auto-routing-improved`. - -Consider this URI:: - - example.com/index.php/helloworld/ - -In the above example, CodeIgniter would attempt to find a controller named ``App\Controllers\Helloworld`` and load it, when auto-routing is enabled. - -.. note:: When a controller's short name matches the first segment of a URI, it will be loaded. - -Let's try it: Hello World! -========================== - -Let's create a simple controller so you can see it in action. Using your text editor, create a file called **Helloworld.php**, -and put the following code in it. You will notice that the ``Helloworld`` Controller is extending the ``BaseController``. you can -also extend the ``CodeIgniter\Controller`` if you do not need the functionality of the BaseController. - -The BaseController provides a convenient place for loading components and performing functions that are needed by all your -controllers. You can extend this class in any new controller. - -.. literalinclude:: controllers/020.php - -Then save the file to your **app/Controllers** directory. - -.. important:: The file must be called **Helloworld.php**, with a capital ``H``. When you use Auto Routing, Controller class names MUST start with an uppercase letter and ONLY the first character can be uppercase. - - Since v4.5.0, if you enable the ``$translateUriToCamelCase`` option, you can - use CamelCase classnames. See :ref:`controller-translate-uri-to-camelcase` - for details. - -.. important:: A controller method that will be executed by Auto Routing (Improved) needs HTTP verb (``get``, ``post``, ``put``, etc.) prefix like ``getIndex()``, ``postCreate()``. - -Now visit your site using a URL similar to this:: - - example.com/index.php/helloworld - -If you did it right you should see:: - - Hello World! - -This is valid: - -.. literalinclude:: controllers/009.php - -This is **not** valid: - -.. literalinclude:: controllers/010.php - -This is **not** valid: - -.. literalinclude:: controllers/011.php - -.. note:: Since v4.5.0, if you enable the ``$translateUriToCamelCase`` option, - you can use CamelCase classnames like above. See - :ref:`controller-translate-uri-to-camelcase` for details. - -Also, always make sure your controller extends the parent controller -class so that it can inherit all its methods. - -.. note:: - The system will attempt to match the URI against Controllers by matching each segment against - directories/files in **app/Controllers**, when a match wasn't found against defined routes. - That's why your directories/files MUST start with a capital letter and the rest MUST be lowercase. - - If you want another naming convention you need to manually define it using the - :ref:`Defined Route Routing `. - Here is an example based on PSR-4 Autoloader: - - .. literalinclude:: controllers/012.php - -Methods -======= - -Method Visibility ------------------ - -When you define a method that is executable via HTTP request, the method must be -declared as ``public``. - -.. warning:: For security reasons be sure to declare any new utility methods as ``protected`` or ``private``. - -Default Method --------------- - -In the above example, the method name is ``getIndex()``. -The method (HTTP verb + ``Index()``) is called the **default method**, and is loaded if the **second segment** of the URI is empty. - -Normal Methods --------------- - -The second segment of the URI determines which method in the -controller gets called. - -Let's try it. Add a new method to your controller: - -.. literalinclude:: controllers/021.php - -Now load the following URL to see the ``getComment()`` method:: - - example.com/index.php/helloworld/comment/ - -You should see your new message. - -Passing URI Segments to Your Methods -==================================== - -If your URI contains more than two segments they will be passed to your -method as parameters. - -For example, let's say you have a URI like this:: - - example.com/index.php/products/shoes/sandals/123 - -Your method will be passed URI segments 3 and 4 (``'sandals'`` and ``'123'``): - -.. literalinclude:: controllers/022.php - -Default Controller -================== - -The Default Controller is a special controller that is used when a URI ends with -a directory name or when a URI is not present, as will be the case when only your -site root URL is requested. - -Defining a Default Controller ------------------------------ - -Let's try it with the ``Helloworld`` controller. - -To specify a default controller open your **app/Config/Routing.php** -file and set this property:: - - public string $defaultController = 'Helloworld'; - -Where ``Helloworld`` is the name of the controller class you want to be used. - -And comment out the line in **app/Config/Routes.php**: - -.. literalinclude:: controllers/016.php - :lines: 2- - -If you now browse to your site without specifying any URI segments you'll -see the "Hello World" message. - -.. important:: When you use Auto Routing (Improved), you must remove the line - ``$routes->get('/', 'Home::index');``. Because defined routes take - precedence over Auto Routing, and controllers defined in the defined routes - are denied access by Auto Routing (Improved) for security reasons. - -For more information, please refer to the -:ref:`routing-auto-routing-improved-configuration-options` documentation. - -.. _controller-default-method-fallback: - -Default Method Fallback -======================= - -.. versionadded:: 4.4.0 - -If the controller method corresponding to the URI segment of the method name -does not exist, and if the default method is defined, the remaining URI segments -are passed to the default method for execution. - -.. literalinclude:: controllers/024.php - -Load the following URL:: - - example.com/index.php/product/15/edit - -The method will be passed URI segments 2 and 3 (``'15'`` and ``'edit'``): - -.. important:: If there are more parameters in the URI than the method parameters, - Auto Routing (Improved) does not execute the method, and it results in 404 - Not Found. - -Fallback to Default Controller ------------------------------- - -If the controller corresponding to the URI segment of the controller name -does not exist, and if the default controller (``Home`` by default) exists in -the directory, the remaining URI segments are passed to the default controller's -default method. - -For example, when you have the following default controller ``Home`` in the -**app/Controllers/News** directory: - -.. literalinclude:: controllers/025.php - -Load the following URL:: - - example.com/index.php/news/101 - -The ``News\Home`` controller and the default ``getIndex()`` method will be found. -So the default method will be passed URI segments 2 (``'101'``): - -.. note:: If there is ``App\Controllers\News`` controller, it takes precedence. - The URI segments are searched sequentially and the first controller found - is used. - -.. note:: If there are more parameters in the URI than the method parameters, - Auto Routing (Improved) does not execute the method, and it results in 404 - Not Found. - -Organizing Your Controllers into Sub-directories -================================================ - -If you are building a large application you might want to hierarchically -organize or structure your controllers into sub-directories. CodeIgniter -permits you to do this. - -Simply create sub-directories under the main **app/Controllers**, -and place your controller classes within them. - -.. important:: Directory names MUST start with an uppercase letter and ONLY the first character can be uppercase. - - Since v4.5.0, if you enable the ``$translateUriToCamelCase`` option, you can - use CamelCase directory names. See :ref:`controller-translate-uri-to-camelcase` - for details. - -When using this feature the first segment of your URI must -specify the directory. For example, let's say you have a controller located here:: - - app/Controllers/Products/Shoes.php - -To call the above controller your URI will look something like this:: - - example.com/index.php/products/shoes/show/123 - -.. note:: You cannot have directories with the same name in **app/Controllers** - and **public**. - This is because if there is a directory, the web server will search for it and - it will not be routed to CodeIgniter. - -Each of your sub-directories may contain a default controller which will be -called if the URL contains *only* the sub-directory. Simply put a controller -in there that matches the name of your default controller as specified in -your **app/Config/Routing.php** file. - -CodeIgniter also permits you to map your URIs using its :ref:`Defined Route Routing `.. - -.. _controller-translate-uri-to-camelcase: - -Translate URI To CamelCase -========================== - -.. versionadded:: 4.5.0 - -Since v4.5.0, the ``$translateUriToCamelCase`` option has been implemented, -which works well with the current CodeIgniter's coding standards. - -This option enables you to automatically translate URI with dashes (``-``) to -CamelCase in the controller and method URI segments. - -For example, the URI ``sub-dir/hello-controller/some-method`` will execute the -``SubDir\HelloController::getSomeMethod()`` method. - -.. note:: When this option is enabled, the ``$translateURIDashes`` option is - ignored. - -Enable Translate URI To CamelCase ---------------------------------- - -To enable it, you need to change the setting ``$translateUriToCamelCase`` option -to ``true`` in **app/Config/Routing.php**:: - - public bool $translateUriToCamelCase = true; +Auto Routing (Improved) is a new, more secure automatic routing system. +See :doc:`auto_routing_improved` for details. .. _controller-auto-routing-legacy: diff --git a/user_guide_src/source/incoming/index.rst b/user_guide_src/source/incoming/index.rst index b4399e18d1ba..d8faf0a869b5 100644 --- a/user_guide_src/source/incoming/index.rst +++ b/user_guide_src/source/incoming/index.rst @@ -10,6 +10,7 @@ Controllers handle incoming requests. routing controllers filters + auto_routing_improved message request incomingrequest diff --git a/user_guide_src/source/incoming/routing.rst b/user_guide_src/source/incoming/routing.rst index 3ab7a5848bc9..0662dde964d1 100644 --- a/user_guide_src/source/incoming/routing.rst +++ b/user_guide_src/source/incoming/routing.rst @@ -767,140 +767,14 @@ will match **product/123**, **product/123/456**, **product/123/456/789** and so And if the URI is **product/123/456**, ``123/456`` will be passed to the first parameter of the ``Catalog::productLookup()`` method. -.. _auto-routing-improved: - Auto Routing (Improved) *********************** .. versionadded:: 4.2.0 -Since v4.2.0, the new more secure Auto Routing has been introduced. - -.. note:: If you are familiar with Auto Routing, which was enabled by default - from CodeIgniter 3.x through 4.1.x, you can see the differences in - :ref:`ChangeLog v4.2.0 `. - -When no defined route is found that matches the URI, the system will attempt to match that URI against the controllers and methods when Auto Routing is enabled. - -.. important:: For security reasons, if a controller is used in the defined routes, Auto Routing (Improved) does not route to the controller. - -Auto Routing can automatically route HTTP requests based on conventions -and execute the corresponding controller methods. - -.. note:: Auto Routing (Improved) is disabled by default. To use it, see below. - -.. _enabled-auto-routing-improved: - -Enable Auto Routing -=================== - -To use it, you need to change the setting ``$autoRoute`` option to ``true`` in **app/Config/Routing.php**:: - - public bool $autoRoute = true; - -And you need to change the property ``$autoRoutesImproved`` to ``true`` in **app/Config/Feature.php**:: - - public bool $autoRoutesImproved = true; - -URI Segments -============ - -The segments in the URL, in following with the Model-View-Controller approach, usually represent:: - - example.com/class/method/ID - -1. The first segment represents the controller **class** that should be invoked. -2. The second segment represents the class **method** that should be called. -3. The third, and any additional segments, represent the ID and any variables that will be passed to the controller. - -Consider this URI:: - - example.com/index.php/helloworld/hello/1 - -In the above example, when you send an HTTP request with **GET** method, -Auto Routing would attempt to find a controller named ``App\Controllers\Helloworld`` -and executes ``getHello()`` method with passing ``'1'`` as the first argument. - -.. note:: A controller method that will be executed by Auto Routing (Improved) needs HTTP verb (``get``, ``post``, ``put``, etc.) prefix like ``getIndex()``, ``postCreate()``. - -See :ref:`Auto Routing in Controllers ` for more info. - -.. _routing-auto-routing-improved-configuration-options: - -Configuration Options -===================== - -These options are available in the **app/Config/Routing.php** file. - -Default Controller ------------------- - -For Site Root URI -^^^^^^^^^^^^^^^^^ - -When a user visits the root of your site (i.e., **example.com**) the controller -to use is determined by the value set to the ``$defaultController`` property, -unless a route exists for it explicitly. - -The default value for this is ``Home`` which matches the controller at -**app/Controllers/Home.php**:: - - public string $defaultController = 'Home'; - -For Directory URI -^^^^^^^^^^^^^^^^^ - -The default controller is also used when no matching route has been found, and the URI would point to a directory -in the controllers directory. For example, if the user visits **example.com/admin**, if a controller was found at -**app/Controllers/Admin/Home.php**, it would be used. - -.. important:: You cannot access the default controller with the URI of the controller name. - When the default controller is ``Home``, you can access **example.com/**, but if you access **example.com/home**, it will be not found. - -See :ref:`Auto Routing in Controllers ` for more info. - -.. _routing-auto-routing-improved-default-method: - -Default Method --------------- - -This works similar to the default controller setting, but is used to determine the default method that is used -when a controller is found that matches the URI, but no segment exists for the method. The default value is -``index``. - -In this example, if the user were to visit **example.com/products**, and a ``Products`` -controller existed, the ``Products::getListAll()`` method would be executed:: - - public string $defaultMethod = 'listAll'; - -.. 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()``. +Auto Routing (Improved) is a new, more secure automatic routing system. -.. 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``. +See :doc:`auto_routing_improved` for details. .. _auto-routing-legacy: diff --git a/user_guide_src/source/installation/upgrade_460.rst b/user_guide_src/source/installation/upgrade_460.rst index 04388acf7d08..8546df1903f2 100644 --- a/user_guide_src/source/installation/upgrade_460.rst +++ b/user_guide_src/source/installation/upgrade_460.rst @@ -91,7 +91,10 @@ and it is recommended that you merge the updated versions with your application: Config ------ -- @TODO +- app/Config/Feature.php + - ``Config\Feature::$autoRoutesImproved`` has been changed to ``true``. +- app/Config/Routing.php + - ``Config\Routing::$translateUriToCamelCase`` has been changed to ``true``. All Changes ===========