Separation of admin controllers from public controllers

We’ve started separating our admin controllers from our public controllers, for a couple of reasons:

  • Separation of concerns

  • Distinction: have our admin URLs display as "http://site.com/admin/…" by convention.

  • Security: separating admin controllers from public controllers simplifies access control.

  • Performance: admin controllers tend to be heavier than public controllers - by separating them, we avoid loading up a bunch of admin-actions when running a public action.

By following a naming convention for admin and public controllers, we end up with controller names like these:

  • UserController

  • UserAdminController

  • PageController

  • PageAdminController

  • etc.

Now for the clever bit. Rather than adding a route for each of the admin controllers, we simply do this:




'urlManager'=>array(

  'urlFormat'=>'path',     // use "path" format, e.g. "post/show?id=4" rather than "r=post/show&id=4"

  'showScriptName'=>false, // do not show "index.php" in URLs

  'appendParams' => false, // do not append parameters as name/value pairs (DO NOT CHANGE THIS)

  'rules'=>array(

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

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

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

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

    

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

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

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

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

  ),

),




I’m sure some of you had already figured this out, but this idea was new to me - thought I’d share it :slight_smile:

There are other possible approaches.

Personally, I’m grouping admin controllers with the same names as non-admin ones into admin module of the same application.

Also you can separate application into frontend and backend applications.

What would be the other methods of seperating the controllers for public and backend?

What I would like to have would be all the public controllers in the controllers folder and maybe a sub folder for the admin controllers.

Here is what I would like to accomplish:

Right now by default when I go to the index.php page it loads the SiteController.php and renders the index view.

What I would like is when I type index.php/admin/ it would load views/admin/index.php

I have tried to add in the rules:




'admin'=>'admin/index',	



And I had to create an admin/IndexController.php

This is the path that I am returned:




echo $this->getViewPath().'<br />'.$this->getRoute(); echo '<br />'.dirname(__FILE__).DIRECTORY_SEPARATOR.'..';


\protected\views/admin/index

admin/index/index

\protected\controllers\admin\..




Is the route ok? I don’t understand…

Is there any more documentation on how we can achieve this somewhere?

Oh I just understood your approach of creating an application for frontend and backend, meaning I could have say:

Root being the frontend and in a subfolder say admin/ would be the backend application?

Would this be the best way to go or can we achieve similar structure but having only 1 application with subfolders for controllers, themes, etc…

I was thinking of 1 application with the public controllers in the controllers root dir and having a sub dir for admin/.

Then having frontend/ backend/ with their own theme folders in the themes folder.

Example:

themes/frontend/classic themes/backend/classic

And being able to tell the group of controllers say admin/* all controllers of admin use admin.php config instead of main.php.

Can this be achieved?

Any help is appreciated.

Ahh, because controllers don’t autoload, this is actually possible - never thought of that. So you have, say, a ProductController class in your main app, but then you have an admin module with a different ProductController as well?

The only downside to that, is it could get kind of confusing if you have both ProductController classes open in two tabs (in your editor/IDE) at the same time…

I do have paths in my IDE ;)

On the tab headers? What IDE?

On hovering tabs. IDE is PhpStorm.

my approach is to create a submodule for every module that needs an admin area

For a client I am doing something similar to this: http://www.yiiframework.com/doc/cookbook/33/

Only my folder structure is more like this:


	index.php

	assets/

	protected/

		config/

			main.php

		components/

		controllers/

		models/

		views/

	backend/

		index.php

		assets/

		protected/

			config/

				main.php

			components/

			controllers/

			models/

			views/



I have it so my backend can "see" (load classes from) the front-end, while the front-end can NOT see the backend. The top of my backend config file looks like this:


//points to front end.

$frontend = dirname(dirname(dirname(dirname(__FILE__)))).DIRECTORY_SEPARATOR.'protected';

Yii::setPathOfAlias('frontend',$frontend);


define('IS_FRONTEND',false);

And the import config option looks like this:


	// autoloading model and component classes

	'import'=>array(

		'application.models.*',

		'frontend.models.',

		'application.components.*',

		'frontend.components.*',

	),

Jonah, I like that approach - gives a nice insulation between the back-end and front-end. Good thinking.

I use modules only. I use the 1st level of the protected folder for all the shared data/components.




- components

   - Controller.php

- modules

   - backend

      - components

         - BackendController.php

   - frontend

      - components

         - FrontendController.php



What is the advantage of this approach? and over an above what jonah’s approach.

How would be the ‘outside’ module Controller.php configured to communicate with backend and frontend?

I guess it’s just a personal preference rather than real advantage. When you go with that approach you can call $this->getModule() within the controller of the main module (e.g. frontend module). This is not possible when you go with the traditional approach (because CWebApplication is the parent of a controller, not a CWebModule). Also you can override beforeControllerAction() within the frontend module. CWebApplication offers this method as well, but you would’ve to extend CWebApplication.

The Controller.php is considered shared and will be imported within the main config file. So the idea behind this approach is:

  • The traditional main application does only exist now to share components, views, etc. across the modules. It won’t have any actual Controller which users can access.

  • frontend & backend are seperated - each module can set it’s own special config within CWebModule::init() if needed.

But this approach also leads to some problems. For example if you’re within the frontend and you want to create a backend url, you may do


Yii::app()->getModule('backend')->urlManager->createUrl();

But as soon as you access another module, the CWebModule::init() method of that module will run. Means if you set a global app component just for the backend module within init(), it may now overwrite a global app component previously set by the frontend module. Though it’s possible to set application components for each module directly, but this is not fully supported.

I hope we see enhanced module system in Yii 2.

Thanks I will try the approach.

awesome IDE, i guess no more eclipse for me.

I’m a little confused. Did you mean to write “a submodule for every model that needs an admin area”?

rAWTAZ, a module can have a submodule.

Hi guys!

What about real modules?

For example module news, or articles or whatever.

Module must have all its contents in one folder, including admin part.

And to implement features of that module a man must only copy that folder to any project.

thanks to any reply

I have been looking out for the approaches to create and encapsulate admin section separately. This is probably the nicest way to do it. But how would I be able to use the same modules in the frontend here? Lets say if I want to use the same gii module, is that possible using the url like:

www.site.com/backend/gii/controller

I tried this and it shows 404.

I have merged the config file for the backend like follows:




CMap::mergeArray(

    require( $frontend .'/config/main.php'),

    array(

        

        'name' => 'Administration',

        'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',

        'theme' => 'newtheme',

        'import'=>array(

                'frontend.models.',

                'frontend.components.*',

        ),

    )

);



Thanks!

EDIT::

I figured out why gii was not working. It was because there was no htaccess file placed inside the backend folder. So all the requests were going to the frontend for anything that said /backend/<controller>

I am trying out further to see what else can or cannot be used. Thank you all for the tips in this post!!

Some time back, Qiang posted a nice tutorial on the wiki to show how he created the directory structure for larger applications as well as the Yii site. As I recall, something like this.




/<application name>/

     /admin/

     /common/

     /console/

     /public/



The tutorial describes how to move files from a standard install into the console and common directories. It also provides tips of configuring your main.php files to handle the directory structure, assuming you already read the Yii docs.

I use a variant of this:




/<applicationname>/

      /admin/

      /common/

      /console/

      /public/

      /www/

          /admin/

              /assets/

              /css/

              /images/

              /js/

              /themes/

                  /default/

                  /classic/

              index.php

              .htaccess

          /public/

              (same structure as admin)



What the tutorial does not mention is creating .htaccess files to convert those four directories to the equivalent of the old /protected/ directory (just move the /protected/ .htaccess to /admin/, /console/, /common/, and /public/. /www/admin/ and /www/public/ above need their own web accessible .htaccess.

I found this to be the best solution of all of my own projects. Many of my projects tend to start off small or intermediate in size and gradually grow into monsters over time as customer wants to add more and more bells and whistles.

A lot of my customers want their admin installed either on separate domains with access to the same database server, so the above works really well.

The other advantage of the above structure allows the creation of a development site that can be removed when putting your site online. I use this for handling some profiling requirements, setting public site constants, temporary file related constants, etc. It’s basically an Admin interface with some additional modules.

Still another advantage if you need to support Apache’s mod_rewrite module, additional directory structures can be created for individual subdomains.

I found Qiang’s solution to be very extensible for my own needs as well as the needs of customers because it leaves room to grow. You lose nothing by setting your directory structure up to handle needs you or your customer might not know about immediately.