Yii 1.1: Organize directories for applications with front-end and back-end

33 followers

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.

Total 10 comments

#9045 report it
Farzan at 2012/07/16 09:02am
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.
    ),
)
#8716 report it
tiagoc at 2012/06/20 10:42pm
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();

#4863 report it
jwinn at 2011/08/22 06:23pm
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

Yii::app()->createWebApplication

Instead, I used the following, after looking at the existing index.php:

Yii::createWebApplication

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.*',
),
#4085 report it
gattu_marrudu at 2011/06/04 07:47pm
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)

#3576 report it
sagarsarkar at 2011/04/20 03:08am
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.

#3381 report it
yiistarter at 2011/04/08 02:11am
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

  1. root/admin/backend.php
  2. root/protected/(all front end code)
  3. roott/protected/cms (all back end code)

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.

#3051 report it
marcovtwout at 2011/03/10 07:59am
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

#498 report it
Tova at 2010/05/14 11:25am
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.

#937 report it
yiimann at 2010/01/24 07:40am
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.*',
),
#1298 report it
phpdevmd at 2009/09/25 05:28am
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

Leave a comment

Please to leave your comment.

Write new article
  • Written by: qiang
  • Updated by: intel352
  • Category: Tutorials
  • Yii Version: 1.1
  • Votes: +28
  • Viewed: 60,094 times
  • Created on: May 8, 2009
  • Last updated: Jun 26, 2012