Tariff Street Ltd and the Yii Framework

At Tariff Street Ltd. we use Yii as our primary development framework. We chose the Yii framework for its excellent Active Record implementation, its strict PHP5 and OOP concepts, and because of our previous knowledge of Prado and ASP.NET, from which Yii borrows many ideas.

Removing a file from git versioning

There are two steps to taking a file back out of any repo that you manage while not deleting it.

1) Add the file to your .gitignore file.

If you don’t have one already, still a new file called .gitignore into the root of your repo.

Into your gitignote file add the file you want to remove:


2) Tell git to stop looking at the file which was already in the version control system.

git rm --cached path/to/file.text

And you’re there. File still exists, but git will stop looking at it for changes.

MySQL Global Unique IDs or GUIDs

99% of websites use databases which contain a primary key, auto incrementing id column.

It’s used to link any related tables together and allows very fast select of one or more specified rows.

When you are linking tables in a standard way ids work just fine and there is no need to consider global unique ids. So when should you consider them at all?

MySQL can’t cope with what are called Polymorphic Associations. That is, a second table is joined in a query based on the value of a column in the first table.

Let me give you an example. I want to link an address table to the people table, office table and factory table. My options would be to include a column for each into the address table, and only populate one for each row or include a type column and then the id of the thing that it is related to. Neither are great, especially when you have a new thing that an address could be related to. You need to keep adding columns or do a separate select for each type of thing you want the addresses for.

The alternative to all this chaos is the global unique id, or guid.

If everything in the relevant tables of your database had their own unique id, you could join person, office and factory onto your people table based on a single column – for argument’s sake let’s call it owner_id – and you’ll get everything you want back in a single query.

If you set up your site to do this from the start it’s pretty painless.

You’ll need an additional table called something like guid which will have a single column id and only be used to supply unique ids to use in other tables.

All other tables will need the auto increment on the id column removing.

When you want to insert a new office, factory or person you first insert a new line into the guid table and then use that new guid as the id for your new row.

I’ll write about how to use guids in Yii in another article soon.

Improving Yii CBreadcrumbs

Note: As of Yii 1.1.11 CBreadcrumbs now has more options which allow you to create Bootstrap style breadcrumbs without the method below…

$this->widget('zii.widgets.CBreadcrumbs', array(
	'links' => array(
		'businesses' => array('business/index'),
		'Ikea' => array('business/view', 'id' => '123'),
	'homeLink' => false,
	'separator' => '',
	'tagName' => 'ul',
	'inactiveLinkTemplate' => '<li><span>{label}</span></li>',
	'activeLinkTemplate' => '<li><a href="{url}">{label}</a> <span class="divider">/</span></li>',
	'htmlOptions' => array('class' => 'breadcrumb'),

Original post:

The default breadcrumbs in yii (CBreadcrumbs) do a job. Not very well, but they do it.

Ideally breadcrumbs should be an unordered list containing anchor tags for maximum styling options. Yii uses a div with a list of anchor tags.

I used to put up with it, but as the infinitely useful Twitter Bootstrap uses an unordered list for their own breadcrumbs I decided enough was enough.

So here is how to create your own breadcrumbs that use an unordered list, list items and anchor tags…

The first thing we need is to create our own component which extends CWidget, as that’s how we’ll include our breadcrumb into a view.

Create a file called Breadcrumb.php into protected/components with the following contents:

<?php class BreadCrumb extends CWidget {         public $crumbs = array();         public $divider = ' / ';         public $homeLink = array('Home' => false);         public $htmlOptions = array();         public function run()         {                 $this->render('breadcrumb');         } } ?>

Next we need to create a view file for it. If you don’t already have one add a view folder to the protected/components folder and then create a new file in there called breadcrumb.php (so protected/components/views/breadcrumb.php) with the following contents:

<ul<?php echo ($this->htmlOptions['class'] ? ' class="' . $this->htmlOptions['class'] . '"' : ''); ?>> <?php foreach($this->crumbs as $k => $crumb) {         echo '<li' . ($crumb[2] ? ' class="active"' : '') . '>';         if(isset($crumb[1])) {                 echo CHtml::link($crumb[0], $crumb[1]);         }         else         {                 echo $crumb[0];         }         if(sizeof($this->crumbs) > ($k+1))         {                 echo '<span class="divider">' . $this->divider . '</span>';         }         echo '</li>'; } ?> </ul>

And finally how to use it. Well select your view and inject the following code:

$this->widget('application.components.BreadCrumb', array(         'divider' => '/',         'htmlOptions' => array(                 'class' => 'breadcrumb',         ),         'crumbs'=>array(                 array('Home', array('site/index')),                 array('Organisations', array('organisation/index')),                 array('Acme Inc.', array('organisation/view', 'id' => 3)),                 array('Update Business', null),         ), ));

Which should then give you something like the following html (which twitter bootstrap will love because we’ve used the correct html and included the breadcrumb class on the ul and divider class on the divider)…

<ul class="breadcrumb>         <li><a href="/">Home</a><span class="divider">/</span></li>         <li><a href="/organisation">Organisations</a><span class="divider">/</span></li>         <li><a href="/organisation/2">Acme Inc.</a><span class="divider">/</span></li>         <li>Update Business</li> </ul>

Hopefully that’s of some use to you. You could easily extend it to allow a container element to be added or to bring back the default ‘homeLink’ option.

choose RegisterCoreScript position in page html

By default…


…will include the script tag for the jQuery core script inside the head of your page immediately before your title tag.

There are times when you will want to include it just after the opening body tag and just before the closing body tag.

CClientScript::POS_HEAD will be positioned in the head just before the title tag
CClientScript::POS_BEGIN will be positioned immediately after the opening body tag
CClientScript::POS_END will be position immediately before the closing body tag

You have two ways to specify the location you would like. You can include your choice in the config/main.php file (or your own config file)…

    'coreScriptPosition' => CClientScript::POS_END,

…inside the components array.

Or you can specify it in a controller or view (usually just before you register the core script(s) using…


Remembering to update POS_END with your chosen position.

Implementing Cron jobs with Yii and CConsoleCommand

A site I’m currently building requires a bunch of Cron jobs running daily to bring in and update various bits of data. It’s the first time I’ve needed to do this within the Yii framework, and it was a useful experience figuring out how it works. The Yii documentation located at http://www.yiiframework.com/wiki/91/implementing-cron-jobs-with-yii/ isn’t completely clear, so hopefully this post will help.

Firstly, forget about browser emulation, the best way to implement Cron jobs in Yii is by using Yii’s Console Application (CConsoleCommand) functions.

Essentially this is a separate instance of your application that can be run from the command line rather than as a publicly executable php script.

The first thing to do is to create a new entry or index script – which looks a lot like the index.php file in your public_html or root directory. However, for extra security your new index script can be (and probably should be) placed outside of the public root so you can only access it through the command line.

I called my script cron.php and dropped it on to the server below public_html. The file will look something like this:

// change the following paths if necessary $yii=dirname(__FILE__).'/framework/yii.php'; $config=dirname(__FILE__).'/public_html/protected/config/cron.php'; // 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); require_once($yii); $app = Yii::createConsoleApplication($config)->run();

Two things to notice here. Firstly the $config information comes from a new config file I’ve called cron.php. This is very much like your main.php config file, except it contains a lot less stuff:

return array(         'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',         'name'=>'Cron',         'preload'=>array('log'),          'import'=>array(                 'application.components.*',                 'application.models.*',         ),         // application components         'components'=>array(                 'db'=>array(                         'connectionString' => 'mysql:host=localhost;dbname=db_name',                         'emulatePrepare' => true,                         'username' => 'root',                         'password' => '',                         'charset' => 'utf8',                         'enableProfiling' => true,                 ),                 'log'=>array(                         'class'=>'CLogRouter',                         'routes'=>array(                                 array(                                         'class'=>'CFileLogRoute',                                         'logFile'=>'cron.log',                                         'levels'=>'error, warning',                                 ),                                 array(                                         'class'=>'CFileLogRoute',                                         'logFile'=>'cron_trace.log',                                         'levels'=>'trace',                                 ),                         ),                 ),                 'functions'=>array(                         'class'=>'application.extensions.functions.Functions',                 ),         ), );

Secondly, you’re running Yii’s createConsoleApplication method, rather than firing up the web application.

Once you’ve done that, the next thing is to understand what this createConsoleApplication()->run(); whatsit is doing.

When this function is fired, it looks in /protected/commands for commands to run. A command takes the form of a Class file of the name YourFunctionCommand.php – yes, you must suffix it with Command.php.

Within this Class file you need something like

class VisitorsCommand extends CConsoleCommand {         public function run($args)         {            // Do stuff         } }

So this file is called VisitorsCommand.php and the class name is VisitorsCommand – get it?

Now, if you were to fire up your command line interface and run something along these lines:

/usr/bin/php /Users/james/Dropbox/Sites/mySiteRoot/cron.php

You’ll get a message saying

The following commands are available:  - visitors

So now you can do

/usr/bin/php /Users/james/Dropbox/Sites/mySiteRoot/cron.php visitors

And Yii will instantiate your Visitors Class and fire the run() method.

Also… you will notice the run() method accepts a parameter – I’ve called it $args, for the sake of argument.

You can pass parameters into the run method like this

/usr/bin/php /Users/james/Dropbox/Sites/mySiteRoot/cron.php visitors param1 param2 param3

And they’ll end up in your run method as an array. You could for example write one Command class that can perform a variety of functions, or pass a key to validate the user of the Cron.

Within your new Command Class, you can access all your Models as normal to perform whatever functions you require.

Have fun!