Difference between #2 and #3 of AssetManager: clearing browser's cache on site update

unchanged
Title
AssetManager: clearing browser's cache on site update
unchanged
Category
How-tos
unchanged
Tags
assets, cache, AssetManager, CAssetManager
changed
Content
If you like the things to be short and clear, then you can skip right to a short
summary of instructions at the end of this article.

Intro
-----

You probably know, that
[AssetManager](http://www.yiiframework.com/doc/api/1.1/CAssetManager "Link
to CAssetManager class description") might be useful to avoid overlapping
of static files like css, js, images in serveral different modules. That is
awesome and so on and bla-bla-bla - you can read about it in the [Understanding
"Assets"](http://www.yiiframework.com/wiki/148/understanding-assets
"Open the article") article.

**But wait! Do you really create modules so often?  **
I can tell what you really do very often when you develop web applications.  
_You write new pieces of Javascipt code, you update CSS, you add elements to
your CSS-sprite images!_

And if you are in constant cycle of improvements, then you probably need to
deliver
all of those tasty new features to your end-user.

And here is the big question: **Is it really so hard to do?**

And the answer is... **nope, it's easy! You just need to know how to do it the
right way.**  
If you think, that storing your files in a folder on the server and then just
changing them will deliver it's new contents to the end user then you are deeply
mistaken. All your static files by default will be cached by user's browser and
will stay there for a long-long time. The cache will be cleared, only if you
change the name of the file.

Some may say, well I'll add timestamp to the end of each file like
`main.css?v=123556`  
Really? I find this too difficult and this may cause a lot of troubles, if you
for example refer to the same static files in several widgets or views. This may
cause conflicts.

And here is short and simple explanation of how to do it the right way (on the
author's humble opinion).

The point of using AssetManager
-------------------------------

First thing we should do is to start using AssetManager in our project.  
That is simple. Explanation below.

### Step 1. Prepare files.
Create folder named 'assets' under your protected folder.  
And place all your static files there. You might skip some files, like favicon
or maybe logo or
maybe some documents which are referenced from some other websites and have to
be located under constant URL.

But the rest of the files, will now look like:

~~~
/protected
	/assets
		/css
			/main.css
		/img
			/bg.png
			/sprite.png
			/extra_icons.png
		/js
			/main.js
			/form.js
			/etc.js

~~~

You may ask: "Hey man, but how will we refer to these files? They are under
protected now!"  
Just read further and you'll know it.

### Step 2. Prepare the tools.
You probably already have a custom base Controller class for all of your
controllers.  
We will modify it a little and will make it look like that:

~~~
[php]
class Controller extends CController
{
        private $_assetsBase;

        public function getAssetsBase()
        {
                if ($this->_assetsBase === null) {
                        $this->_assetsBase =
Yii::app()->assetManager->publish(
                                Yii::getPathOfAlias('application.assets'),
                                false,
                                -1,
                                defined('YII_DEBUG') &&
YII_DEBUGYII_DEBUG
                        );
                }
                return $this->_assetsBase;
        }

        public $menu=array();
        public $items=array();
        public $breadcrumbs=array();
}
~~~

Really simple, yeah?

What we did now is we created a getter method which publishes our
/protected/assets folder
and stores the path of published files in _assetsBase private property.

There are several interesting options for
[CAssetManager.publish()](http://www.yiiframework.com/doc/api/1.1/CAssetManager#publish-detail
"Link to CAssetManager.publish() method description") method.

~~~
[php]
publish(string $path, boolean $hashByName=false, integer $level=-1, boolean
$forceCopy=false)
~~~

`$path` - the assets folder we want to publish  
`$hashByName` - very important parameter, which will allow us to clear browser's
cache later (please, read on)  
`$level` - we'll just publish all subdirs recursivly  
`$forceCopy` - we set this to `YII_DEBUG`, which causes instant assets updates
on our local PC and causes only required updates on production server
(`YII_DEBUG` should be `false` in production)

### Step 3. Using prepared tools to operate with prepared files.
Now we can use our `assetsBase` property everywhere to refer our assets.

You can write in your layout or view files something like:

~~~
[php]
<link rel="stylesheet" type="text/css"
href="<?=$this->assetsBase?>/css/main.css" />
~~~

or

~~~
[php]
<?Yii::app()->clientScript->registerScriptFile($this->assetsBase.'/js/utils.js')?>
~~~

You can write it this way, because `$this` is the `Controller` instance here.  
You can also use it within widgets, but the code would be a little different:

~~~
[php]
<?Yii::app()->clientScript->registerScriptFile(Yii::app()->controller->assetsBase.'/js/widget.js')?>
~~~

Now, when we did all that stuff, the really explosive power of AssetManager is
about to happen...

### Step 4. Updating production version and forcing user's browser cache to
clear.
Now, if you remove all subfolders in your /assets folder under the website root,
then you'll notice
that Yii will automatically create new randomly-named folders.

But if you take a closer look, then you'll notice, that even if you changed the
contents of your static files and cleared the assets folder, the new assets
subfolder's names will be similar to they were before!

"What the hack?" - you'll ask!  
Well, this problem is described in [this topic
thread](http://www.yiiframework.com/forum/index.php/topic/23055-asset-manager
"View topic thread") and [SamDark](https://github.com/samdark
"View SamDark's github profile") actualy resolved it with his
[patch](http://code.google.com/p/yii/source/detail?spec=svn3602&r=3441
"View commit at googlecode") on Nov 8, 2011 and those second
`$hashByName` parameter of `CAssetManager.publish()` method does all the magic
now.

`$hashByName` parameter set to `false` says to Yii to build assets folder name
as the hash from dirname of the path being published and its modification time.

This means, that if you just do

~~~
touch /path/to/your/website/protected/assets
~~~

in linux console, then you'll have a brand new assets folder name on next
assets' update

And this means, that all your end-users will have to re-validate their browser's
cache and will get your new highly improved static files right away.

Short summary
-------------

So, basically all you should do is:

1. Move all your static files to /protected/assets folder
2. Improve your basic `Controller` class as described above
3. Use `controller->assetsBase` everywhere you refer to static files
4. When you do a site update, just do `touch
/path/to/your/website/protected/assets`
5. PROFIT: users will get latest versions of your static files (images, styles,
scripts, etc)

Additional links
----------------

1. [CAssetManager docs](http://www.yiiframework.com/doc/api/1.1/CAssetManager
"View CAssetManager documentation")
2. [Topic thread about this
issue](http://www.yiiframework.com/forum/index.php/topic/23055-asset-manager
"View topic thread at forum")
3. [Topic thread about this issue in upcoming
Yii2](http://www.yiiframework.com/forum/index.php?/topic/23147-asset-manager/
"View topic thread at forum") (available for active Yii forum users
only)
4. Extensions which provide cool minification features for assets:
- [escriptboost](http://www.yiiframework.com/extension/escriptboost "Visit
extension's page")
- [clientscriptpacker](http://www.yiiframework.com/extension/clientscriptpacker
"Visit extension's page")
- [minscript](http://www.yiiframework.com/extension/minscript "Visit
extension's page")
- [minifyclientscript](http://www.yiiframework.com/extension/minifyclientscript
"Visit extension's page")
- [dynamicres](http://www.yiiframework.com/extension/dynamicres/ "Visit
extension's page")  
and it's up to you to decide which one to use
5. [Another article](http://www.yiiframework.com/wiki/148/understanding-assets
"View the article") describing usage of CAssetManager
6. [Russian version of this article](http://habrahabr.ru/blogs/yii/139166/
"Visit habrahab.ru")

Wish you luck!