When developing web applications, there is one well-known problem. We, programmers, write new javascript code, styles in css, change statics ... And this static is usually cached by the user's browser and can remain in the cache for quite a long time (and this is actually correct, because it can speed up page loading at times) .
But what to do if we changed the statics? How to force the user to reset the cache and update these files? There are some common ways, for example, adding a versioned label to a file name, or adding a timestamp in a GET parameter when a file is connected.
In case you use the Yii framework, you can also specify versions or time stamps of the script files and styles when connecting, however, you need to constantly monitor this, and in the case of Yii also monitor the absence of conflicts (for example, when the widget and the use the same script, but with different timestamps).
In fact, in Yii, you can organize a more civilized approach to this matter.
')
Step 1. Preparing files.
Create a subfolder of
assets in the
protected folder of your web application.
We place all the statics in this folder. Some files may have to be left in the root of the web application, if they need permanent external links (for example, favicon, logo, some documents).
The file structure might look something like this:
/ protected
/ assets
/ css
/main.css
/ img
/bg.png
/sprite.png
/extra_icons.png
/ js
/main.js
/form.js
/etc.js
Step 2. Preparation of tools.
Most likely, you already have an overridden
Controller class, which you use as the base class for all other controllers.
We will change it a bit to ensure work with assets:
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, YII_DEBUG ); } return $this->_assetsBase; } public $menu=array(); public $items=array(); public $breadcrumbs=array(); }
It's simple.
Here we added a getter, which for the first time publishes the assets from the
/ protected / assets folder and saves the path to the
_assetsBase private property, and in subsequent times simply returns the value of this private property.
The
publish () method of the
CAssetManager class has several interesting parameters.
Here is the method signature:
publish(string $path, boolean $hashByName=false, integer $level=-1, boolean $forceCopy=false)
And a brief description of the parameters:
$ path - actually the path to the folder of assets, which we will publish;
$ hashByName - determines whether to publish the folder name as it is, or to hash it. Thanks to this parameter, all the magic happens, which will be discussed later;
$ level - determines the degree of nesting of folders for publication (
-1 - all subfolders are recursive);
$ forceCopy - determines whether to force files to be copied, even if they already exist. We set this parameter to
YII_DEBUG , due to which our scripts will be constantly updated on the local machine, and on production - only if necessary (on production
YII_DEBUG must be set to
false ).
Step 3. We use the prepared tools above the prepared files.
That property
assetsBase , for which we wrote a getter a little higher, can now be used everywhere.
Now in the views and layouts we can write something as like:
<link rel="stylesheet" type="text/css" href="<?=$this->assetsBase?>/css/main.css" />
or
<?Yii::app()->clientScript->registerScriptFile($this->assetsBase.'/js/utils.js')?>
And we can write this way because
$ this in this case is an instance of the
Controller class from which all our controllers are inherited and which is passed to the views and layouts.
With widgets, the situation is a little different; you need your own approach, because widget views are executed not in the context of the controller, but in the context of the widget, therefore, to connect the Javascript file in the widget's view, we will write:
<?Yii::app()->clientScript->registerScriptFile(Yii::app()->controller->assetsBase.'/js/widget.js')?>
Step 4. Update the production version and force the user's browser to download the updated files.
This is the most important point and it is precisely that many developers overlooked it, including me once.
Reading many articles on Yii, it may seem that in order for a user to get updated statics files, it’s enough to upload them to
/ protected / assets to clean the
/ assets folder in the site root (if
AssetManager is used of
course ) and voila - the user will
update everything by itself.
But this is not the case at all!Auto-generated subfolders names in the
/ assets folder in the root of the web application are based on the hash of the folder name
/ protected / assets and
do not change from time to time, even if you clean the
/ assets folder in the root of the web application. They are restored in its original form. This means that the user's browser does not "skip" that something has changed until the caching time expires.
At least it was until recently. And this problem was discussed in
this topic , after which Alexander Makarov found a workaround and with his
commit on November 8, 2011, he corrected the situation.
Now, the second parameter of the
CAssetManager class's
publish () method — the
$ hashByName parameter set to
false — forces Yii to build the names of the subfolders in the
/ assets folder in the root of the web application based on the hash of the name of the folder with statics,
as well as the modification date of this folder .
Due to this, we can make a very simple trick with the deployment of application static files:
touch /path/to/your/website/protected/assets
we execute this command in the console of your linux server (we assume that the site is still deployed under linux) and the next time you use the static Yii will generate new asset names!
This means that the user who opened the page will download absolutely new, fresh, improved scripts, styles, images, etc.
To put it in a nutshell
You just need to:
- All static should be placed in the / protected / assets folder
- Expand the base controller class as described above.
- Use assetsBase everywhere as a basis for static paths.
- When updating statics on the site, execute the command
touch /path/to/your/website/protected/assets
which will lead to a change in the modification date of the / protected / assets folder, which means the generation of a new hash in the / assets folder in the site root - Benefit: you can be sure that users get the latest version of your scripts, styles, etc.
A little about the other
By the way, as far as I know, for example, in Ruby on Rails this problem has already been solved.
In Yii version 2, which is under development, this problem is also likely to be resolved.
In discussing the solution to this problem for Yii 2, you can participate in the
forum (access is open only to active participants).
Also, another interesting feature is the automatic minification of scripts and css. At the moment it is not implemented at the framework level, but implemented in some extensions, but this is a topic for another article ...
Useful links on the topic
- Documentation for the CAssetManager class.
- Discussion of this topic on the forum Yii
- Discussion of this problem for the new version of Yii 2 (available for active users only)
- Extensions for minification:
- English version of this article
- Another article describing how to work with CAssetManager
PS: I note that this article is neither a translation nor a crosspost, because I also wrote the English version of the article for the Yii knowledge base, but the Russian version is more qualitative and understandable (due to different levels of knowledge of languages).