Large applications are often divided into front-end and back-end (or even more ends) depending on the target user groups. The front-end should be used by common users, while the back-end mainly the administrators or staff members. The two ends usually have dramatically different appearance, even though they may share a lot of code underneath. In this tutorial, we describe a way of organizing directories of the code for both ends.
Note: The directory organization described in this tutorial is meant to serve as a referential implementation. It is not a standard. Yii offers complete freedom for you to organize your directories, according to your needs.
To start with, we give out the directory organization as follows,
wwwroot/
index.php
backend.php
assets/
images/
js/
protected/
config/
main.php
components/
controllers/
models/
views/
runtime/
backend/
config/
main.php
components/
controllers/
models/
views/
runtime/
We have two entry scripts here: index.php
and backend.php
. The former is used by front-end, while the latter by back-end. All the application code are placed under the base application directory protected
which should be configured to prevent from being accessed directly by end users.
Under protected
, we have the normal set of sub-directories needed by a typical Yii application: config
, components
, controllers
, models
, views
and runtime
.
The extra backend
directory is used to store code that are specifically written for the back-end. Similar to the front-end, we organize these back-end code in terms of config
, components
, controllers
, models
, views
and runtime
.
The entry script code for the front-end and the back-end look like the following. Their main difference is that different application configurations are used.
// index.php:
require('path/to/yii.php');
Yii::createWebApplication('protected/config/main.php')->run();
// backend.php:
require('path/to/yii.php');
Yii::createWebApplication('protected/backend/config/main.php')->run();
The front-end application configuration is very normal, just like we usually have for single-end applications. The back-end application configuration is a bit special. Its content is given as follows,
$backend=dirname(dirname(__FILE__));
$frontend=dirname($backend);
Yii::setPathOfAlias('backend', $backend);
return array(
'basePath' => $frontend,
'controllerPath' => $backend.'/controllers',
'viewPath' => $backend.'/views',
'runtimePath' => $backend.'/runtime',
'import' => array(
'backend.models.*',
'backend.components.*',
'application.models.*',
'application.components.*',
),
// ... other configurations ...
);
In the above, we first define $backend
and $frontend
to be the directory protected/backend
and protected/
, respectively. We then define a root alias named backend
to be the directory protected/backend
. In the configuration array, we specify that the base application directory of the back-end to be the same as that of the front-end, namely, protected/
(the reason of doing so is to explained shortly). The rest of the crucial paths (controllerPath
, viewPath
and runtimePath
) are defined to be located under protected/backend
. And finally, we import several directories, starting with the back-end components
and components
directories, followed by the normal application components
and components
directories.
So why are we using protected
as the base application directory for both the front-end and the back-end? This is because the back-end often needs to reuse the code designed for the front-end, but not vice versa. Having the same base application directory means that the two ends have the same path for the application
root path alias. Therefore, code referring to the application
alias can be reused without any problem in both ends.
The back-end, in addition to reusing the front-end code, usually has its own special code to deal with, for example, content administration. We store these code under the protected/backend/
directory and sub-directories. In its application configuration, we also import these additional sub-directories together with those meant for both of the ends.
Warning about layout
Be carefull if you are using components/Controller.php generated by yiic tool by default. Don't forget to change this line according to your needs:
public $layout='application.views.layouts.column1';
And change default layout file, if it is used, to.
axe
I rather like to work with a backend config like this:
//...//
return array(
'basePath' => $backend, 'import' => array( 'frontend.models.*', 'frontend.components.*', 'application.models.*', 'application.components.*', ),
Optimization
In backend/config/main.php
<?php $backend=dirname(dirname(__FILE__)); $frontend=dirname($backend); Yii::setPathOfAlias('backend', $backend); $frontendArray=require($frontend.'/config/main.php'); // This is the main Web application backend configuration. Any writable // CWebApplication properties can be configured here. $backendArray=array( 'basePath' => $frontend, 'controllerPath' => $backend.'/controllers', 'viewPath' => $backend.'/views', 'runtimePath' => $backend.'/runtime', // autoloading model and component classes 'import'=>array( 'backend.models.*', 'backend.components.*', 'application.models.*', 'application.components.*', 'application.extensions.*', ), // main is the default layout 'layout'=>'main', // alternate layoutPath 'layoutPath'=>dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.'_layouts'.DIRECTORY_SEPARATOR, // application-level parameters that can be accessed // using Yii::app()->params['paramName'] and MParams class 'params'=>require(dirname(__FILE__).'/params.php'), // application components 'components'=>array( 'urlManager'=>array( 'rules'=>require(dirname(__FILE__).'/routes.php'), ), ), ); if(!function_exists('w3_array_union_recursive')) { /** * This function does similar work to $array1+$array2, * except that this union is applied recursively. * @param array $array1 - more important array * @param array $array2 - values of this array get overwritten * @return array */ function w3_array_union_recursive($array1,$array2) { $retval=$array1+$array2; foreach($array1 as $key=>$value) { if(is_array($array1[$key]) && is_array($array2[$key])) $retval[$key]=w3_array_union_recursive($array1[$key],$array2[$key]); } return $retval; } } return w3_array_union_recursive($backendArray,$frontendArray); ?>
Now you don't have to repeat config twice in frontend and backend. Backend inherits config array (including params.php) from the frontend, and you only need to redefine backend specific configuration.
Original idea by Maxximus
Re: Optimisation
Merging of array can also be done by native array_replace_recursive() in PHP 5.3
For using this function before PHP 5.3, see user comment: http://www.php.net/manual/en/function.array-replace-recursive.php#92574
about the backed.php in the root
I have configured my application to work in the folder structure described in the above article. Thanks for that.
But I would like to create a folder in the root called admin and password protect that and put the backend.php inside that folder for more security. I was having trouble with this although I configured the backed.php correctly (path to yii and backend/config/main.php).
In simple terms I want to work like this
below is the code I used for my admin/backend.php
$yii=dirname(__FILE__).'/../yii-1.1.6.r2877/framework/yii.php'; $config=dirname(__FILE__).'/../protected/admincms/config/main.php'; require_once($yii); Yii::createWebApplication($config)->run();
any help is much appreciated.
Instead of backend.php i need the better url.
Hello, this is a great stuff to setup an admin end. but is there any way that the url looks
like http://www.sitename/backend/ ?
Thanks in advance.
more customization
I had to add some more code in order to make it work right.
First off, the .htaccess:
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^admin/?(.*?)$ admin.php?url=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(?!admin\/)(.*?)$ index.php?url=$1 [QSA,L] </IfModule>
(the first regexp could be improved - typing e.g. /administrator would redirect to /admin.php?url=istrator. Not too bad though, because it still gives a 404, which is expected anyway)
Then a unified routes file on the front-end part (of course this can be customized to your needs):
return array( 'admin<opt:(\/)?>' => 'article/index', '<user:(admin)?><opt:(\/)?>category/<id:\d+>' => 'article/index', '<user:(admin)?><opt:(\/)?>article/<id:\d+>/<slug:.+>' => 'article/view', '<user:(admin)?><opt:(\/)?><controller:\w+>/<id:\d+>' => '<controller>/view', '<user:(admin)?><opt:(\/)?><controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>', '<user:(admin)?><opt:(\/)?><controller:\w+>/<action:\w+>' => '<controller>/<action>', );
(not sure i this is the best way to do this but it seems to work. The way the Yii routing system handles regexp's is kind of obscure to me)
Then, in order to keep login consistent between the two applications, I had to add a parameter to the user component in the main configuration:
... 'user'=>array( // enable cookie-based authentication 'allowAutoLogin' => true, 'stateKeyPrefix' => 'MySession', ), ...
(thanks phtamas for enligtening me on this point)
Error in index.php or backend.php, and order of import for components/models
Using the generated Yii example, I was getting an error using
Instead, I used the following, after looking at the existing index.php:
Note that the order of the model and component autoloading in the configuration file is important. I've just realized that the backend was using the frontend's components. I have both the /components/UserIdentity.php and /backend/components/UserIdentity. For the backend to take precedence over (overwrite?) the frontend file of the same name, I simply changed the order of the import.
'import'=>array( 'application.models.*', 'application.components.*', 'backend.models.*', 'backend.components.*', ),
don´t work for me
Fatal error: Call to a member function createWebApplication() on a non-object in E:\wamp\www\site\admin.php on line 14
Call Stack
Time Memory Function Location
1 0.0008 365864 {main}( ) ..\admin.php:0
I try the steps but doesn´t work , the index.php is fine , but admin.php in root directory give me this error.
My path rather than backend is:
protected/admin/config/main.php
code:
// change the following paths if necessary
$yii='C:\wamp\www\framework\yii.php';
require_once($yii);
// remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG',true);
// specify how many levels of call stack should be shown in each log message
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);
$configfile='protected/admin/config/main.php';
Yii::app()->createWebApplication($config)->run();
How to use modules in backend
This is how you can use modules in back end: Add backend modules to main.php like this:
'modules' => array( 'foo', // Will be looked up in `frontend.modules`. 'bar' => array( 'class' => 'backend.modules.bar.BarModule' // Path to module in backend. ), )
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.