Yii upgrade service

Yii 1.1 is currently in maintenance mode following Yii 2 being released way back in May 2013.

Support and bug fixes for Yii 1 were only provided until December 31, 2016 and security fixes and PHP 7 compatibility ends December 31, 2020.

That means that right now the only reason updates to Yii 1 are being created and made available are for security issues found in the existing codebase, and from 2021 it’s mothballed completely.

If you’re maintaining a Yii 1 codebase it’s time to start thinking about how to migrate away from Yii completely, or upgrade your codebase to Yii 2.

There is an official guide aimed at supporting the upgrade process, but it is unlikely to get you from Yii 1 to Yii 2 without significant pain and head scratching.

Converting from Yii 1 to Yii 2 is completely unlike developing code for either framework, and you’d want to source a Yii upgrade professional rather than getting an inexperienced developer or team involved with a large or mission critical codebase.

Personally I’ve now upgraded four codebases from Yii 1 to Yii 2. All the upgrades have been different. All follow a similar path, and the repeatable process I’ve created, but each has its own edge cases, bespoke widgets and complexities.

The work takes many days per codebase before testing and perfection can happen, and where an existing development team are involved with ongoing development or support of the Yii 1 system, it’s best handled as a team effort. Acceptance that the process isn’t anywhere close to just flicking a switch or downloading a patch is required from day one.

At this point I’m considering two next steps.

Firstly, if your company has significant budget and is in need of support moving from Yii 1 to Yii 2, I’d be interested in hearing from you. You may want a supportive voice helping your existing developers to make the move. You may also be looking to hand over a Yii 1 codebase and get it back ready for some testing and iteration.

Secondly, I’m considering sharing some or all of the process I’ve created for manually moving a codebase over from Yii 1 to Yii 2, the gotchas, the process and how to update specific things within Yii 1, where it isn’t as simple as a like

Feel free to send me a direct message on Twitter @harrybailey or leave a comment below if you’d like to contact me.

Yii (and Yii2) wildcard / catch all url rules

Both Yii and Yii2 have url management and routing built in. They will compare the url of the request to a list of rules you’ve defined.

Sometimes you might want to do various checks of the url in yii config main and then send every other request to a particular controller.

The rule you need to add last to the urlManager is…

'(.*)' => 'controller/action',

… and now any rule that isn’t matched previously will end up being sent to your defined controller and action.

In Yii2 you can also do the same:

'<url:(.*)>' => 'controller/action'

Yii 2 (and Yii 1.1) UpdateAll Examples

A few Yii 2 updateAll examples:

Updating a single column:

$rows = Comment::updateAll(['status' => 1], 'type_id = 1 AND status = 0');

Updating multiple columns:

$rows = Comment::updateAll(['status' => 1, 'updated' => '2011-08-25 09:33:23'], 'type_id = 1 AND status = 0' );

Some simple Yii 1.1 examples of how to use updateAll():

In this example we want to set status = 1 WHERE type_id = 1 AND status = 0

Note this is a simple example where we are only updating one column and using a string for our condition. There are more complex options available.

$rows = Comment::model()->updateAll(array( 'status' => 1 ), 'type_id = 1 AND status = 0' );

So as you can see first comes an array of column => new_value pairs, then a condition string which is pretty much used as the WHERE clause.

An example of updating more than one column:

$rows = Comment::model()->updateAll(array( 'status' => 1, 'updated' => '2011-08-25 09:33:23' ), 'type_id = 1 AND status = 0 ' );

Yii – “Model.id” not defined for CArrayDataProvider and CSqlDataProvider etc

There is a Yii error I come across every now and again, and each time it stumps me.

If you ever get SomeModel.id not defined error when trying to put your dataprovider results into a cgridview or similar then it’s likely because

1) Your model doesn’t have an id column, it might use file_id or user_id etc instead and

2) You haven’t defined which other column should be used.

We do this using what’s known as keyField:


new CArrayDataProvider($users, array('keyField' => 'user_id'));
new CSqlDataProvider($users, array('keyField' => 'user_id'));

[carousel keywords=”yii” tag=”fetchit-21″]

Yii – Rewrite file or image urls for download or display

I recently had a need to track files and images which were uploaded and for their urls to be secret. This meant that they couldn’t sit in a folder waiting to be found and had to be saved below the site root.

To get access to them I wanted to the check the user permissions followed by forcing download of all files (including images). Here are the steps I took to accomplish this…

For the sake of this page, my (fake) file urls are something like:

example.com/user/123/file/456.pdf

Update the urlmanager to rewrite files to my filemanager/view:


	'user//file/.[a-z0-9]+' => 'filemanager/view',

Update the filemanager class’ actionView function:


	public function actionView($id)
	{
		$file = $this->loadModel($id);

		header("Pragma: public"); // required
		header("Expires: 0");
		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
		header("Cache-Control: private",false); // required for certain browsers 
		//header("Content-Type: " . $file->mime_type);
		header("Content-Disposition: attachment; filename=\"" . str_replace(" ", "-", preg_replace("@[^a-z0-9 ]@", "", strtolower($file->file_name))) . '.' . $file->file_extension . "\";" );
		header("Content-Transfer-Encoding: binary");
		header("Content-Length: ".filesize(Yii::app()->basePath . '/../../uploads/user_files/' . $file->file_id . '.' . $file->file_extension));
		readfile(Yii::app()->basePath . '/../../uploads/user_files/' . $file->file_id . '.' . $file->file_extension);
		exit();

	}

I also of course required filemanager access to be strict:


	public function accessRules()
	{
		return array(
			array('allow', 
				'actions'=>array('view', 'create','delete'),
				'users'=>array(),
				'expression' => 'User::model()->hasRightsToFile()',
			),
			array('deny',  // deny all users
				'users'=>array('*'),
			),
		);
	}

And that’s about it. Controlled and forced file download with rewritten file urls.

[carousel keywords=”yii” tag=”fetchit-21″]