Controllers in subfolders

I have following structure in the controllers directory :

- controllers
    - TestController
    - folder
        - TestController
        - folder
            - TestController (ERROR)
            - folder
                - TestController
                - folder
                    - TestController

Well, calling a custom action of the TestController in the second folder, the default action is called instead, and if none is defined, the “#404 not found error” is raised. But this happens only on the controllers in the second folder, while it works fine on all other controllers. So, if in all controllers an helloAction is defined, calling this action in the different controllers, causes different behaviours:

test/hello.html > OK
folder/test/hello.html > OK
folder/folder/test/hello.html > **ERROR**
folder/folder/folder/test/hello.html > OK
folder/folder/folder/folder/test/hello.html > OK
folder/folder/folder/folder/folder/test/hello.html > OK
folder/folder/folder/folder/folder/folder/test/hello.html > OK

I tried up to the sixth level and it works always except on the second level. I think it can depend only on the urlmanager rules, that I still don’t understand very well. These are my UrlManager settings:

'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'enableStrictParsing' => false,
    'suffix' => '.html',
    'rules' => require(__DIR__ . '/routes.php'), 
],

and these are my rules:

'<controller:[\w-]+>/<id:\d+>' => '<controller>/view',
'<controller:[\w-]+>/<folder:[\w-]+>/<action:[\w-]+>/<id:\d+>' => '<controller>/<folder>/<action>',
'<controller:[\w-]+>/<action:[\w-]+>/<id:\d+>' => '<controller>/<action>',
'<controller:[\w-]+>/<folder:[\w-]+>/<action:[\w-]+>/<text:\w+>' => '<controller>/<folder>/<action>',
'<controller:[\w-]+>/<action:[\w-]+>/<text:\d+>' => '<controller>/<action>',
'<controller:[\w-]+>/<action:[\w-]+>' => '<controller>/<action>',

Thank you

So I found out that the problem depends from this rule, but I don’t know why. Can someone explain it to me?

'<controller:[\w-]+>/<folder:[\w-]+>/<action:[\w-]+>/<text:\w+>' =>
    '<controller>/<folder>/<action>',

In the rule above, you have defined that:

  • the URL pattern is '<controller:[\w-]+>/<folder:[\w-]+>/<action:[\w-]+>/<text:\w+>' and
  • the corresponding route is '<controller>/<folder>/<action>'.

But you should note that the placeholders’ names (controller, folder, and action) have no meaning in the definition of the route. If the route pattern is aaa/bbb/ccc, it always means aaa module’s bbb controller’s ccc action.

For example, abc/def/xyz/0123 will match the URL pattern, and it will be interpreted as a route of ‘abc/def/xyz’ … this means actionXyx of DefController of AbcModule. It’s not actionXyz of AbcController in the folder of def.

In other words, only the number and the order of the placeholders matter in the definition of the route. So your rule above has the same meaning as the following:

'<module:[\w-]+>/<controller:[\w-]+>/<action:[\w-]+>/<text:\w+>' =>
    '<module>/<controller>/<action>',

Also note that the controller ID can have / when it is located in the sub folder.

So, folder/folder/folder/folder/folder/test/hello.html can match the last rule of yours, where the controller ID is folder/folder/folder/folder/folder/test and the action ID is hello. All other URLs that you have tested will match the last rule, with the only one exception of folder/folder/test/hello.html that matches the 4th rule before it reaches the last one.

Hello for you detaild answer, but I still don’t understand how rules work, but I’ll find some time to better learn them. Anyway, I tried to substitute the fourth rule by the one you suggested and it still didn’t work, because I think thant the names in the rule are not meaningful and are only placeholders. So, if we name the first parameter “controller” or “module”, it should be the same thing.

I tried to substitute the fourth rule by the one you suggested

No, I didn’t suggest you to change the 4th rule. I meant to say that it won’t work as you expect. I would rather remove it completely.

I think that the names in the rule are not meaningful and are only placeholders. So, if we name the first parameter “controller” or “module”, it should be the same thing.

Yes, you are right. That’s exactly what I wanted to say. When there are 3 placeholders in the route definition, the 1st one will be considered as the module ID no matter what name you would use.

I think I was wrong in this, because 'folder/folder/folder/folder/folder/test/hello' doesn’t match the pattern of the last rule '<controller:[\w-]+>/<action:[\w-]+>'. In fact, all URLs in subfolders other than 'folder/folder/test/hello' fail to match any of the rules. But since enableStrictParsing is set to false, they are parsed into valid routes using default URL parsing logic.

UrlManager::parseRequest()

Would you please try your test with enableStrictParsing set to true? I guess all URLs other than test/hello will fail.

Yes, you’re right, with enableStrictParsing set to true, all other urls fail.

This is the url rules for a project of mine.

'rules' => [
    '<c:\w+>/<id:\d+>' => '<c>/view',
    '<c:\w+>/<id:\d+>/<a:\w+>' => '<c>/<a>',
    '<c:\w+>/<a:\w+>' => '<c>/<a>',
],

And this is what I have for another project that has modules.

'rules' => [
    '<m:\w+>/<c:[\w-]+>/<id:\d+>' => '<m>/<c>/view',
    '<m:\w+>/<c:[\w-]+>/<id:\d+>/<a:\w+>' => '<m>/<c>/<a>',
    '<m:\w+>/<c:[\w-]+>/<a:\w+>' => '<m>/<c>/<a>',
    '<c:\w+>/<id:\d+>' => '<c>/view',
    '<c:\w+>/<id:\d+>/<a:\w+>' => '<c>/<a>',
    '<c:\w+>/<a:\w+>' => '<c>/<a>',
],
  1. I use '\w+' for controllers, actions, and modules, because their ID have no '-' in them in my projects. I would have used '[\w-]+' if they had '-' as you are doing.
  2. The rules have to be in the order of from specific to generic, because they will be tested from top to bottom. Once a matching rule get found, the rest of the rules will be ignored. Hence the rules for modules come first.

I don’t have experience with the controllers in subfolders. But I think the pattern for the controller ID must have '/' in order to catch the controllers in subfolders.

'rules' => [
    '<c:[\w-/]+>/<id:\d+>' => '<c>/view',
    '<c:[\w-/]+>/<id:\d+>/<a:\w+>' => '<c>/<a>',
    '<c:[\w-/]+>/<a:\w+>' => '<c>/<a>',
],

So, I tried to remove all rules and all the pages of my project are loaded anyway, so it seems I don’t need them, but I saw that the url of the create forms don’t have a pretty url. So for example the php command:

Url::To(['folder/folder/folder/controller/view', 'id' => 1]);

creates this url:

folder/folder/folder/controller/view.html?id=1

while I would like to have:

folder/folder/folder/controller/view/1.html

but also if apply my rules, it does’t work. So I defined following rule:

'folder/folder/folder/<controller:[\w-]+>/<action:[\w-]+>/<id:\d+>' => 
    'folder/folder/folder/<controller>/<action>',

and it works, but so I need a rule for every folder, while I think there it should also be possible to have only one rule. So I found a rule that seems finally to work for any number of folder: :grinning:

'<controller:(([\w-]+/)?)+[\w-]+>/<action:[\w-]+>/<id:\d+>' => 
    '<controller>/<action>'

So I’m finally beginning to understand how rules work, but there is at least one thing that I still don’t understand, that is why the “id” token is not present in the second string, I mean the following rule:

'<controller:[\w-]+>/<action:[\w-]+>/<id:\d+>' => '<controller>/<action>'

transforms the controller/view.html?id=1 url into controller/view/1.html, but where is said in the rule that it has to do so with the “id” token? And what part of the rule coincide with which one of the two urls?

See the Named Parameters section of the guide

https://www.yiiframework.com/doc/guide/2.0/en/runtime-routing#named-parameters

Thank you, some time ago I read this article a lot of times without really understanding it, but now I thik that I finally understood what it means. Could you please tell me now how to set the text color of a post to red, as I saw in some posts here upon this one? Thank you.

GitHub flavored markdown is supported in this forum. Use an enclosing pair of triple backticks like the following:

Input:
```
$value = ‘some text’;
$value2 = 2;
```

Output:

$value = 'some text';
$value2 = 2;

You can specify the programming language in addition in the beginning line:

```php

```

GitHub help > Creating and highlighting code blocks
https://help.github.com/articles/creating-and-highlighting-code-blocks/

1 Like

Thank you very much