You are viewing revision #3 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.
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 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" 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:
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_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() method.
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:
<link rel="stylesheet" type="text/css" href="<?=$this->assetsBase?>/css/main.css" />
or
<?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:
<?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 and SamDark actualy resolved it with his patch 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:
- Move all your static files to /protected/assets folder
- Improve your basic
Controller
class as described above - Use
controller->assetsBase
everywhere you refer to static files - When you do a site update, just do
touch /path/to/your/website/protected/assets
- PROFIT: users will get latest versions of your static files (images, styles, scripts, etc)
Additional links ¶
- CAssetManager docs
- Topic thread about this issue
- Topic thread about this issue in upcoming Yii2 (available for active Yii forum users only)
- Extensions which provide cool minification features for assets:
- escriptboost
- clientscriptpacker
- minscript
- minifyclientscript
- dynamicres
and it's up to you to decide which one to use
- Another article describing usage of CAssetManager
- Russian version of this article
Wish you luck!
Thanx
This is an excellent solution for a small project.
I used to be extended CAssetManager, but for a small project once it is garbage.
Excellent, thanx.
Thank you!
+1, great idea, clear explanation. Excellent!
Great, Thanks for sharing.
For Step 4, instead of go to in linux console, we could do the following.
if (YII_DEBUG) {
$command = "touch /path/to/your/website/protected/assets"; exec($command);
}
Permission 777 and what about just touch()?
I don't have access to the shell so using exec() is great. Anyhow it only works when I set the permisssion for assets folder to 777. Otherwise I get an access denied error message.
And there is actually a PHP function doing exactly what we want: touch(). Then it get's as easy as:
touch('/path/to/your/website/protected/assets');
Happy touching :)
Thank you
thanks for describing the assets clearly and if you dont mind ,would you please post brief info about the clientscript and conflict of jquery in yii
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.