December 29th, 2008
Unlike in CakePHP, it is mandatory to use the shell tool (called yiic, similar to CakePHP’s “Bake” script) to start off your application as it creates the workspace architecture (The Yii package does not come with an application bare-bones, only framework code). Yiic generates .htaccess files with rules that forwards user requests to index.php where the application runs (cake does this too). When I checked my index.php file to see what was in it, I was amazed with the possibilities of what I saw.
<?php // change the following paths if necessary $yii = 'C:\wamp\www\yiiRepo\framework\yii.php'; $config = dirname(__FILE__) . '/protected/config/main.php'; // remove the following line when in production mode defined('YII_DEBUG') or define('YII_DEBUG',true); require_once($yii); Yii::createWebApplication($config)->run();
The last two lines are the important ones. In the second-last line, it includes the main Yii class file, so that it can actually run the application as seen in the last line. createWebApplication() accepts one argument: the path to the configuration file for the application. In the configuration file you can configure things such as your database, error and mysql logging, and url routing. The configurations are stored in the form of an array. It’s a much simpler approach than cake’s, which uses a heavy configuration class. Personally I am liking Yii’s array configuration syntax much better than cake’s (I will show you my Yii configuration file in a second).
Since in Yii you define the configuration file used in your index.php file, you can greatly simplify the synchronization between your development and production server – you simply need different index files and separate configuration files. So I thought up a inheritance/tree system for configuration files.
/protected /config /main.php /production.php /development.php
Here, the production and development configuration files will simply extent the main configuration file which holds the configurations that are consistent across both production and development servers such as the url routing rules. Possible examples of each file:
<?php // This is the main Web application configuration. return array( 'basePath' => dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'name' => 'My Web Application', // Tell's Yii is can auto-load model and component classes on-demand (lazy loading) 'import' => array( 'application.models.*', 'application.components.*', ), 'preload' => array('log'), //preload the log component right away (eager loading) // application components 'components' => array( 'user' => array( // user component settings here (similar to CakePHP's auth component) ), //log class can output errors, mysql quarry summaries, etc. The specific //settings are extended in the production and development config files 'log' => array( 'class' => 'CLogRouter', ), 'urlManager' => array( 'urlFormat'=>'path', 'showScriptName' => false, 'rules' => array( // some generic url routes I often use 'user/register/*' => 'user/create', 'user/settings/*' => 'user/update', ), ), ), );
<?php //import main configurations $main = include "main.php"; // Development configurations $development = array( 'components' => array( 'db' => array( 'class' => 'CDbConnection', 'connectionString' => 'mysql:host=localhost;dbname=yiitestdrive', 'username' => 'root', 'password' => '', ), 'log' => array( 'routes' => array( //set the log component to show logs on the bottom of the page - similar to CakePHP array( 'class' => 'CWebLogRoute', 'levels' => 'trace, info, error, warning', 'categories' => 'system.db.*', ), ), ), ), ); //merge both configurations and return them return CMap::mergeArray($main, $development);
<?php //import main configurations $main = include "main.php"; // Production configurations $production = array( 'components' => array( 'db' => array( 'class' => 'CDbConnection', 'connectionString' => 'mysql:firstname.lastname@example.org;dbname=productionDatabase', 'username' => 'admin', 'password '=> 'password123', ), 'log' => array( 'routes' => array( // Configures Yii to email all errors and warnings to an email address array( 'class' => 'CEmailLogRoute', 'levels' => 'error, warning', 'emails' => 'email@example.com', ), ), ), ); //merge both configurations return CMap::mergeArray($main, $production);
As you can see, in the main config file we have the consistent settings such as URL routing, and in the production and development files we have inconsistent things such as database settings. Note also in the production config I have errors and warnings sent to an email while in the development config I have all logs outputted at the bottom of the page. Nifty, huh? Now we can set up the index files for the production and development servers like so:
Development index.php file:
<?php $yii = 'C:\wamp\www\yiiRepo\framework\yii.php'; $config = dirname(__FILE__) . '/protected/config/'; // turns debug on defined('YII_DEBUG') or define('YII_DEBUG',true); require_once($yii); Yii::createWebApplication($config . 'development.php')->run();
Production index.php file:
<?php $yii = 'framework/yii.php'; $config = dirname(__FILE__) . '/protected/config/'; require_once($yii); Yii::createWebApplication($config . 'production.php')->run();
Now you can easily set different settings for production and development set-ups without redundancy. Enjoy.
UPDATE: Qiang wrote his own version of this method in the Yii Cookbook here. He recommends you merge the configurations with CMap::mergeArray() instead of array_merge_recursive(). I have changed this article to use CMap also. CMap::mergeArray() is a helper method that comes from the Yii core.