How To Call Controller If Controller's Name And Module's Name Are The Same?

Hello,

I’m using Yii-boilerplate and Yii-user extensions, my application’s directories are as follow (simplified to this problem):




/common

  /config

  /modules

     /user

       /controllers

           

/frontend

  /config

  /controllers



So far, I can access mysite.com/user/profile for example, and it calls Profile controller in User module correctly.

Now, I want to add a controller with name ‘User’ on frontend, for example I want to access mysite.com/user/view, which I mean to call actionView in /frontend/controllers/UserController.php instead of calling User module. Currently it calls the User module and return an error, Unable to resolve the request “user/view” (Error 404).

My question: How to ask Yii to prioritize to find controller first, then if it’s not found, it should look for it in the module?

My configs:

/frontend/config/main.php:




return CMap::mergeArray(

  require(__DIR__ . '/../../common/config/main.php'),

  array(


		'basePath' => 'frontend',

  ...

		'import' => array(

			'application.components.*',

			'application.controllers.*',

			'application.models.*',

		),

...

		'components' => array(

			'urlManager' => array(

				'rules' => array(

					'<controller:\w+>/<id:\d+>' => '<controller>/view',

					'<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',

					'<controller:\w+>/<action:\w+>' => '<controller>/<action>',

				)

			),

		),



/common/config/main.php:




return array(

   'import' => array(

      'common.components.*',

      'common.models.*',

      'common.modules.user.models.*',

      'common.modules.user.components.*',

    ),


    'modules'=>array(

      'user'=>array(

          'class' => 'common.modules.user.UserModule',

          ...

          )




Bootstrap index.php file:




...

require_once('common' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Yii' . DIRECTORY_SEPARATOR . 'yii.php');

$config = require('frontend' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'main.php');

require_once('common' . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'WebApplication.php');


$app = Yii::createApplication('WebApplication', $config);


$app->run();



Any help is appreciated, thanks in advance.

You could put an extra URL rule in to use the specific actions:




    'components' => array(

        'urlManager' => array(

            'rules' => array(

                'user/<action:(view|other|actions|here)>' => 'user/<action>',

                '<controller:\w+>/<id:\d+>' => '<controller>/view',

                '<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',

                '<controller:\w+>/<action:\w+>' => '<controller>/<action>',

            )

        ),

    ),



It’s not necessarily the most elegant way to do it, but it should work.

Unfortunately, it doesn’t work. I think if we put this rule


'user/<action:(view|other|actions|here)>' => 'user/<action>'

the router will translate ‘user/view’ route to ‘user/<action>’ which is the same ‘user/view’, and Yii still look for module=‘user’ and controller=‘view’ instead of controller=‘user’ and action=‘view’. CMIIW.

How about using a different name for your user controller to disambiguate it, then using the URL rules to route to it?




    'user/<action:(view|other|actions|here)>' => 'myuser/<action>',



Hi,


'urlManager'=>array(

                        'urlFormat'=>'path',

                        //'showScriptName'=>false,

                        'rules'=>array(

                                'admin/'			=>'admin/index/index',

                                'admin/login'		=>'admin/index/login',

                                'admin/logout'		=>'admin/index/logout',

                                'admin/<controller:\w+>/<action:\w+>'=>'admin/<controller>/<action>',




                                'customer/'			=>'customer/index/index',

                                'customer/login'	=>'customer/login',

                                'customer/logout'	=>'customer/index/logout',

                                'customer/<controller:\w+>/<action:\w+>'=>'customer/<controller>/<action>',

                

                  				'vendor/'=>'vendor/index/index',

                                'vendor/login'	=>'vendor/login',

                                'vendor/logout'	=>'vendor/index/logout',

                                'vendor/<controller:\w+>/<action:\w+>'=>'vendor/<controller>/<action>',




                        /*

				'user/'				=>'user/index/index',

				'user/login'		=>'user/index/login',

				'user/logout'		=>'user/index/logout',

				'user/<controller:\w+>/<action:\w+>'=>'user/<controller>/<action>', 

				'<controller:\w+>/<id:\d+>'=>'<controller>/view',

				'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',

				'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',*/


                        ),

                ),

Yes, I think it’s the viable solution right now, although I will need to update the router everytime I add some actions or update the action’s name in the controller.

An alternative (which has worked for me) is to alter the name of the module in the config, and adjust imports and URLs accordingly. Note that this will not be appropriate in all cases, but in my particular case, it was a lot more straightforward than renaming the controller.

Start by changing the name in the config:




'modules'=> array(

    ...

    'usermanage'=>array(

        'class'=>'application.modules.user.UserModule',

    ),

    ....

),



Note that you will need to adjust any imports within the module that rely on the module name:


'user.models.*'

will need to become:


'application.modules.user.models.*'

and any createURL calls will need to use the new module name:


$this->createUrl('user/<controller>/<action>')

will need to be


$this->createUrl('usermanage/<controller>/<action>')

Once complete, the ambiguity is gone - ‘user’ refers to the controller and ‘usermanage’ refers to the module - without changing any class names or file names.

Hi,

I cant solve the problem,

Sry I am New to yii,If you dont mind plz explain me the below Coding What is the myuser/action

Change that Front end controller name,

‘user/<action:(view|other|actions|here)>’ => ‘myuser/<action>’,

I had same issue. The solution is to create a special module which will handle only application controllers and won’t check modules.

Create a module somewhere and redefine createController() method.

Remove $module->createController() call.

Change $this->createControllerByID() call to $this->module->createControllerByID().

Add module to config with ‘app’ prefix and use ‘app’ prefix in routes.

This is solution for Yii 2, but for Yii 1 it will be similar.





    'components' => [

        ...

        'urlManager' => [

            ...

            'rules' => [

                '<module:(user)>/login' => '<module>/security/login',

                '<module:(user)>/logout' => '<module>/security/logout',

                '<controller:(user)>/<action>' => 'app/<controller>/<action>',

            ],

        ],

        ...

    ],

    'modules' => [

        ...

        'app' => [

	    'class' => 'frontend\modules\AppModule',

        ],

        ...

    ],




// frontend\modules\AppModule


namespace frontend\modules;


use yii\base\Module;


class AppModule extends Module

{

    /**

     * It is used to handle routes when some route and some controller has the same name.

     */

    public function createController($route)

    {

        if ($route === '') {

            $route = $this->defaultRoute;

        }


        // double slashes or leading/ending slashes may cause substr problem

        $route = trim($route, '/');

        if (strpos($route, '//') !== false) {

            return false;

        }


        if (strpos($route, '/') !== false) {

            list ($id, $route) = explode('/', $route, 2);

        } else {

            $id = $route;

            $route = '';

        }


        // controller map take precedence

        if (isset($this->controllerMap[$id])) {

            $controller = Yii::createObject($this->controllerMap[$id], [$id, $this]);

            return [$controller, $route];

        }

        //$module = $this->getModule($id);

        //if ($module !== null) {

        //    return $module->createController($route);

        //}

        

        if ($this->module === null) {

            return null;

        }


        if (($pos = strrpos($route, '/')) !== false) {

            $id .= '/' . substr($route, 0, $pos);

            $route = substr($route, $pos + 1);

        }


        // changed here

        $controller = $this->module->createControllerByID($id);

        if ($controller === null && $route !== '') {

            $controller = $this->module->createControllerByID($id . '/' . $route);

            $route = '';

        }


        return $controller === null ? false : [$controller, $route];

    }

}