Previously there was described a way to build an application with front-end and back-end. I would like to continue this theme and suggest another way to organize directories using WebApplicationEnd behavior.
If you are not familiar with events & behaviors yet, then please, read this tutorial to get basic knowledge about using them.
First, create a new web application with yiic tool. I used an application generated by Yii 1.1.1, but everything described below should work with other versions of Yii Framework (probably with minor changes).
Now let's modify some directories' structure to separate front-end and back-end related files.
Since application's ends usually use the same models, but different controllers and views, we will separate them by creating two subdirectories under protected/controllers and protected/views directories:
webroot/
protected/
components/
Controller.php
UserIdentity.php
controllers/
/front
SiteController.php
/back
SiteController.php
views/
/front
/layouts
column1.php
column2.php
main.php
/site
/pages
about.php
contact.php
error.php
index.php
login.php
/back
/layouts
main.php
/site
error.php
index.php
login.php
Front-end SiteController and all front-end views are files generated by yiic tool. You have to create back-end SiteController and back-end views by yourself (or just copy and modify front-end ones).
Note: Since a new full path to front-end layouts is "application.views.front.layouts", you have to edit "column1.php" and "column2.php" files to render "application.views.front.layouts.main" as a parent layout. Also set Controller's (protected/components/Controller.php) layout property to 'column1'.
Now let's create different config files for both ends. Since these files usually have much in common, we will "inherit" them from the main.php config:
webroot/protected/config/front.php:
return CMap::mergeArray(
require(dirname(__FILE__).'/main.php'),
array(
// Put front-end settings there
// (for example, url rules).
)
);
webroot/protected/config/back.php:
return CMap::mergeArray(
require(dirname(__FILE__).'/main.php'),
array(
// Put back-end settings there.
)
);
By default, Yii will try to find controllers and views in protected/controllers and protected/views directories respectively. We have to change this behavior and force Yii to search controllers and views in the "back" or "front" subdirectories depending on the currently running end.
Actually we can do it in the -end's config file by setting "viewPath" and "controllerPath" properties, but what if we are going to have some modules like News, Articles, etc.? We'll need to set these properties for them too. We can also have some back-end modules which don't need such separation.
Here comes the Yii magic. In protected/components directory create a file "WebApplicationEndBehavior.php" with the following contents:
class WebApplicationEndBehavior extends CBehavior
{
// Web application end's name.
private $_endName;
// Getter.
// Allows to get the current -end's name
// this way: Yii::app()->endName;
public function getEndName()
{
return $this->_endName;
}
// Run application's end.
public function runEnd($name)
{
$this->_endName = $name;
// Attach the changeModulePaths event handler
// and raise it.
$this->onModuleCreate = array($this, 'changeModulePaths');
$this->onModuleCreate(new CEvent($this->owner));
$this->owner->run(); // Run application.
}
// This event should be raised when CWebApplication
// or CWebModule instances are being initialized.
public function onModuleCreate($event)
{
$this->raiseEvent('onModuleCreate', $event);
}
// onModuleCreate event handler.
// A sender must have controllerPath and viewPath properties.
protected function changeModulePaths($event)
{
$event->sender->controllerPath .= DIRECTORY_SEPARATOR.$this->_endName;
$event->sender->viewPath .= DIRECTORY_SEPARATOR.$this->_endName;
}
}
Now add some lines to the main config file:
'behaviors'=>array(
'runEnd'=>array(
'class'=>'application.components.WebApplicationEndBehavior',
),
),
Now our application has a new method runEnd (to run one of the application's ends) and a new event onModuleCreate. By raising this event from a web module we can change modules' properties. Controllers and views paths are changed in the attached handler "changeModulePaths".
If you have a module, which should use different controllers and views for different ends, then just modify it's init() method:
protected function init()
{
// ...
// We can configure our module depending on the value
// of Yii::app()->endName.
$this->foo = (Yii::app()->endName == 'front') ? 'bar1' : 'bar2';
// Raise onModuleCreate event.
Yii::app()->onModuleCreate(new CEvent($this));
}
Note that in this case the module's controllers and views paths must be organized as shown before.
If a module doesn't need a separation to back-end and front-end controllers and views, then just omit the onModuleCreate event's raising.
Finally, let's protect back-end by creating a parent controller for all back-end controllers:
webroot/protected/components/BackEndController.php:
class BackEndController extends CController
{
public $layout='layout_name';
public $menu=array();
public $breadcrumbs=array();
public function filters()
{
return array(
'accessControl',
);
}
public function accessRules()
{
return array(
array('allow',
'users'=>array('*'),
'actions'=>array('login'),
),
array('allow',
'users'=>array('@'),
),
array('deny',
'users'=>array('*'),
),
);
}
}
webroot/protected/controllers/back/SiteController.php must extend this controller to perform access checking.
Everything's done. New index.php and backend.php files are:
webroot/index.php:
$yii = dirname(__FILE__).'/../yii/framework/yii.php';
$config = dirname(__FILE__).'/protected/config/front.php';
// Remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3);
require_once($yii);
Yii::createWebApplication($config)->runEnd('front');
webroot/backend.php:
$yii = dirname(__FILE__).'/../yii/framework/yii.php';
$config = dirname(__FILE__).'/protected/config/back.php';
// Remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3);
require_once($yii);
Yii::createWebApplication($config)->runEnd('back');
CRUD management as frontend and backend
For backend crud generation run url as,
/backend.php?r=gii
controller generate as : controllers\back\XyzController.php
view files generate as : views\back\xyz\files.php
For frontend crud generation run url as,
/index.php?r=gii/crud
controller generate as : controllers\front\XyzController.php
view files generate as : views\front\xyz\files.php
Summary ¶
The created behavior delivers us from specifying controllers and views paths for the application and all it's modules by using runEnd() method and invoking the onModuleCreate event in necessary places.
Also modules became more self-sufficient and can be easily integrated with the existing front-end and back-end layouts.
Appendix A. How to use yiic tool. ¶
You can easily generate models and views for the front-end the old way without any changes. Just go to the application's webroot directory and execute the next command:
protected/yiic shell
or
path/to/php.exe protected/yiic shell
All generated models will be saved under protected/models directory, and controllers & views will be saved under protected/(controllers|views)/front directory.
Generating controllers and views for the back-end is a little different. You have to run yiic the next way:
protected/yiic shell backend.php
or
path/to/php.exe protected/yiic shell backend.php
Now all generated controllers and views will go to protected/(controllers|views)/back directory.
Appendix B. Back-end url rules. ¶
Usually you don't need to setup url rules for the back-end, but if you want to do this, then you'll have to modify .htaccess as follows:
AddDefaultCharset utf-8
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# Make the backend accessible via url: http://site/backend.
RewriteRule ^backend backend.php
# If a directory or a file exists, use it directly.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Otherwise forward it to index.php.
RewriteRule . index.php
Then, in the back.php config file setup url manager component:
'urlManager'=>array(
'urlFormat'=>'path',
'showScriptName'=>false,
'rules'=>array(
'backend'=>'site/index',
'backend/<_c>'=>'<_c>',
'backend/<_c>/<_a>'=>'<_c>/<_a>',
),
),
Now you can generate beautiful urls using CHtml::link() method.
If you have a module (e.g. news), then you'll need to add 3 more rules before existing ones:
'backend/news'=>'news',
'backend/news/<_c>'=>'news/<_c>',
'backend/news/<_c>/<_a>'=>'news/<_c>/<_a>',
Also, you still can add own rules:
'backend/news/<id:\d+>'=>'news/newsReport/update',
But make sure that you insert these rules before news rules containing <_c> and <_a>. Otherwise the latter rules will be used instead.
No need for another script in your root!
Hi there,
There is no need for another script in your root.
If you have privileges to setup subdomains try this!
Put the following code in the index.php bootstrap
define('YII_DEBUG', true); require_once '../../lib/yii/yii.php'; if(($sd = explode('.',$_SERVER['HTTP_HOST'])) && $sd[0] == 'backend') Yii::createWebApplication('./path/to/config/backend.php')->runEnd('back'); else Yii::createWebApplication('./path/to/config/frontend.php')->runEnd('front');
to isreal
If you have a module with a front-end and back-end views, then we should use front-end views if the front-end is running, and back-end views otherwise.
Yii::app()->onModuleCreate(new CEvent($this));
Raises onModuleCreate event and says to the application to change this module's views path to moduleId/views/front and controllers path to moduleId/controllers/front.
This line:
$this->newsPerPage = (Yii::app()->endName == 'front') ? 5 : 15;
is just to show that you can use endName property to change some logic of the application depending on what "end" (front or back) is currently running now.
htaccess & url manager
.htaccess file may look like:
Options +FollowSymLinks IndexIgnore */* RewriteEngine on # Make backend accessible via url: http://site/backend. RewriteRule ^backend backend.php # If a directory or a file exists, use it directly. RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # Otherwise forward it to index.php. RewriteRule . index.php
Usually you don't need to rewrite urls in the backend. But if you want to, then add urlManager component to the backend config file (see frontend config). It can look like:
'urlManager'=>array( 'urlFormat'=>'path', 'showScriptName'=>false, 'urlSuffix'=>'', 'rules'=>array( 'backend'=>'site/index', // News. 'backend/news'=>'News/newsReport/list', 'backend/news/<id:\d+>'=>'News/newsReport/update', ), ),
To generate appropriate links use CHtml::link method. For example:
CHtml::link('News', array('News/newsReport/list'));
great job
nice tutorial, i've used it in more then 10 projects.
but now i'm trying make some themes for one of this project and meet problem with setting up right config for theme.
Could you give any advice how to handle themes in this architecture ?
great job
If you want to keep everything in one theme
Create directories:
And edit webroot/protected/config/back.php
'theme'=>'classic/backend'
Or create two different themes and indicate webroot/protected/config/back.php:
'theme'=>'backend'
Theming
Be sure to set up a theme name in your config file
theme => 'your-theme-name';
Make the directory structure like in the protected's view folder
Modify WebApplicationEndBehavior.php onModuleChange function
protected function changeModulePaths($event) { $event->sender->controllerPath .= DIRECTORY_SEPARATOR.$this->_endName; $event->sender->viewPath .= DIRECTORY_SEPARATOR.$this->_endName; if ($event->sender->theme !== null) $event->sender->viewPath = $event->sender->theme->basePath.DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.$this->_endName; }
Links from backend to frontend
What is the best approach to create Urls inside the backend web application that should link to frontend?
One abvious way is to hard code the frontend base url and building absolute urls. But this is not very elegant and ignores all the url rules defined in the frontend.
Any better ideas?
Very good article
Very good article.going to try this now.
Got Exception after setting up url manager component
if i paste ulrManager rules in the back file i got the following exception error
"Property "CWebApplication.urlManager" is read only. "
Two separate themes for front and back
If you are using two separate themes for front and back in your 'Themes' folder.
now add following into protected/config/back.php otherwise it will
array( 'theme'=>'back', #... 'components'=>array( 'urlManager'=>array( #... ............. 'rules'=>array( 'backend'=>'site/index', 'backend/<controller:\w+>/<id:\d+>'=>'<controller>/view', 'backend/<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', 'backend/<controller:\w+>/<action:\w+>'=>'<controller>/<action>', ), ), ), ),
you can also add a theme name in front.php in same directory for front section.
Thanks for the help
Thanks sir my problem has been resolved, thanks very much.
Front and admin login issue
I was working with http://www.yiiframework.com/wiki/356/how-to-create-front-and-admin-side-login-form and i found that is we don't add different name in config file font.php & back.php i.e.
'name'=>'Website Admin',
it overrides front's login with admin vice-versa. If anybody is having session issue try to add 'name' in both config.
webapp from scratch
Hi,
thanks for the tutorial.
I would like to ask a question: what are the steps to create a new webapp from scratch, separating backend and frontend?
Thanks,
Regards.
theming...
I could'nt understand theming part...
@fouss
this will use to seperate system in two part as admin side as "back" and user side as "front",
For more understanding you can refer This wiki article written by yii user vibhaJadwani
theming... @kiran sharma
I undersatand that and I tested it (working) but theming is causing problem to me... there should be a forum post where we could discuss deeply about all the aspect of this way of Organizing directories.
I'll be happy if the author (andy_s) could update his wiki including theming (for admin and front users)
Thx!
@fouss
any Yii user had questions/doubts about this wiki article, can move further with This Forum Post..
My url in backend is not correct
url in backend is not display url correct
my config/main.php
'urlManager'=>array( 'urlFormat'=>'path', 'showScriptName'=>false, 'rules'=>array( '<controller:\w+>/<id:\d+>'=>'<controller>/view', '<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>', '<controller:\w+>/<action:\w+>'=>'<controller>/<action>', ), ),
and my config/back.php
'urlManager'=>array( 'urlFormat'=>'path', 'showScriptName'=>false, 'rules'=>array( 'backend'=>'admin/login', 'backend/<_c>'=>'<_c>', 'backend/<_c>/<_a>'=>'<_c>/<_a>', ), ),
url in backend not display before url "backend"
SiteController cannot find the requested view "index".
Hi, great tutorial and I think I am really close to getting it working, except I get this one error on the frontend. It is probably something really simple I am missing but I am still trying to learn the yii framework so if anyone can point me in the right direction, I would be extremely grateful!!!
CException
SiteController cannot find the requested view "index".
/var/www/[mysite]/protected/controllers/front/SiteController.php(33): CController->render("index")
28 public function actionIndex()
29 {
30 // renders the view file 'protected/views/site/index.php'
31 // using the default layout 'protected/views/layouts/main.php'
32
33 $this->render('index');
34 }
35
36 /*
37 This is the action to handle external exceptions.
38 */
how to create URLs between ends?
Thanks for the great article.
Just wondering if there is a way to create URLs between two ends? For example, creating a URL pointing to a page in the frontend from backend?
I noticed some others raised the same question in the forum post but no answer. Many thanks!
make this structure in module
hi ,everybody
i need to make this structure in module like this
module
test
controllers back front
Needs some clarification on using modules
I don't understand exactly what the value of foo should be in the explained example.
I guess the bar1 is for frontend and bar2 for backend.
protected function init() { // ... // We can configure our module depending on the value // of Yii::app()->endName. $this->foo = (Yii::app()->endName == 'front') ? 'bar1' : 'bar2'; // Raise onModuleCreate event. Yii::app()->onModuleCreate(new CEvent($this)); }
module access prevent
Hello,
how can we prevent module access from frontend ?
i created module for backend but i can access that module via frontend too
eg. http://www.srtest.local/backend/users/users
i dont want to access it via http://www.srtest.local/users/users.
Please advice
About separating the two ends for module
@banago
protected function init()
{
// ... // We can configure our module depending on the value // of Yii::app()->endName. $this->foo = (Yii::app()->endName == 'front') ? 'bar1' : 'bar2'; // Raise onModuleCreate event. Yii::app()->onModuleCreate(new CEvent($this));
}
$this->foo means any module parameter you want to set according to backend or frontend.
If you want to separate the backend controller and frontend controller, the line
$this->foo = (Yii::app()->endName == 'front') ? 'bar1' : 'bar2';
is not need. and the line
Yii::app()->onModuleCreate(new CEvent($this));
is enough for you.
Please test in your own module as,
class xxxModule extends CWebModule {
public function init() { parent::init(); // Raise onModuleCreate event. Yii::app()->onModuleCreate(new CEvent($this)); // your code here }
}
@Befree Nishant (module access prevent)
The module have two ends (frontend and backend).
Please move backend contollers to application.modules.youmodule.controllers.backend.
Please move front contollers to application.modules.youmodule.controllers.frontend.
and add the code
parent::init();
Yii::app()->onModuleCreate(new CEvent($this));
to your init function of xxxModule.php files
If the module just belongs to frontend or backend, please only add the module config to config/frontend.php or config/backend.php.
How to create Backend Url?
When i am in admin and creating url
echo Yii::app()->createUrl("site/contact");
Output :
But I want,
Solution Worked for me is : In protected/config/main.php remove rules from urlmanager. is it right way to do?
Download working admin panel
Here you can download working admin panel, I created two versions.
Link: http://scriptbaker.com/how-to-separate-front-and-admin-panel-in-yii-framework#download
Good article, but...
I work with project builded this way. But I've noticed that
`
parseUrlmethod of
UrlManager`
class is calling twice.Are there way avoid it?
Routing Problem
I have successfully separate frontend and backend. But I am unable to set this in virtual host. Please check:
Backend virtual host:
<VirtualHost *:80> DocumentRoot "C:\xampp\htdocs\myproject" ServerName admin.chinmay.com <Directory "C:\xampp\htdocs\myproject"> AllowOverride All Order allow,deny Allow from all RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . backend.php DirectoryIndex backend.php Require all granted </Directory> </VirtualHost>
Frontend virtual host:
<VirtualHost *:80> DocumentRoot "C:\xampp\htdocs\myproject" ServerName demo.chinmay.com <Directory "C:\xampp\htdocs\myproject"> AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost>
See above code my
backend.php
file which is not working for inner pages.admin.chinmay.com this is working fine but when I call other controller action I got
index.php
instead ofbackend.php
I want all the action and sub-action which comes under
admin.chinmay.com
should loadbackend.php
file.How to manage it?
Please help.
How to separate components and model directory like controller backend and fronetend
I have successfully separated backend and frontend both controller and view. But I am unable to separate components and model. Could you please help me
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.