Skip to content

Commit 4f530d8

Browse files
teohhanhuijaviereguiluz
authored andcommitted
Cookbook entry: Asset - Custom Version Strategy
1 parent ed3ae3c commit 4f530d8

File tree

4 files changed

+339
-0
lines changed

4 files changed

+339
-0
lines changed
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
.. index::
2+
single: Asset; Custom Version Strategy
3+
4+
How to Use a Custom Version Strategy for Assets
5+
===============================================
6+
7+
.. versionadded:: 2.7
8+
The Asset component was introduced in Symfony 2.7.
9+
10+
Symfony by default does not perform asset versioning. You can specify the
11+
:ref:`version <reference-framework-assets-version>` and
12+
:ref:`version_format <reference-framework-assets-version-format>` configuration
13+
options to add a simple version to all assets (or a specific set of assets
14+
grouped as a :ref:`package <reference-framework-assets-packages>`):
15+
16+
.. configuration-block::
17+
18+
.. code-block:: yaml
19+
20+
# app/config/config.yml
21+
framework:
22+
assets:
23+
version: "20150530"
24+
version_format: "%%s?version=%%s"
25+
26+
.. code-block:: xml
27+
28+
<!-- app/config/config.xml -->
29+
<?xml version="1.0" encoding="UTF-8" ?>
30+
<container xmlns="http://symfony.com/schema/dic/services"
31+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
32+
xmlns:framework="http://symfony.com/schema/dic/symfony"
33+
xsi:schemaLocation="http://symfony.com/schema/dic/services
34+
http://symfony.com/schema/dic/services/services-1.0.xsd
35+
http://symfony.com/schema/dic/symfony
36+
http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"
37+
>
38+
<framework:config>
39+
<framework:assets
40+
version="20150530"
41+
version-format="%%s?version=%%s"
42+
/>
43+
</framework:config>
44+
</container>
45+
46+
.. code-block:: php
47+
48+
// app/config/config.php
49+
$container->loadFromExtension('framework', array(
50+
'assets' => array(
51+
'version' => '20150530',
52+
'version_format' => '%%s?version=%%s',
53+
),
54+
));
55+
56+
However, if you require more control, you need to create a custom version
57+
strategy.
58+
59+
Default Package
60+
---------------
61+
62+
The default package is used when you do not specify a package name in the
63+
:ref:`asset <reference-twig-function-asset>` Twig function. In order to
64+
override the version strategy used by the default package, it is necessary
65+
to add a compiler pass.
66+
67+
This example shows how to integrate with `gulp-buster`_.
68+
69+
.. note::
70+
71+
busters.json as referenced below is the output from gulp-buster which
72+
maps each asset file to its hash. A small snippet of the file's format
73+
(JSON object):
74+
75+
.. code-block:: json
76+
77+
{
78+
"js/script.js": "f9c7afd05729f10f55b689f36bb20172",
79+
"css/style.css": "91cd067f79a5839536b46c494c4272d8"
80+
}
81+
82+
Create Compiler Pass
83+
~~~~~~~~~~~~~~~~~~~~
84+
85+
.. code-block:: php
86+
87+
// src/AppBundle/DependencyInjection/Compiler/OverrideAssetsDefaultPackagePass.php
88+
namespace AppBundle\DependencyInjection\Compiler;
89+
90+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
91+
use Symfony\Component\DependencyInjection\ContainerBuilder;
92+
use Symfony\Component\DependencyInjection\Reference;
93+
94+
class OverrideAssetsDefaultPackagePass implements CompilerPassInterface
95+
{
96+
public function process(ContainerBuilder $container)
97+
{
98+
$definition = $container->getDefinition('assets._default_package');
99+
$definition->replaceArgument(1, new Reference('app.assets.buster_version_strategy'));
100+
}
101+
}
102+
103+
The code above fetches the service definition of the default package, and replaces
104+
its second argument (the version strategy).
105+
106+
Register Compiler Pass
107+
~~~~~~~~~~~~~~~~~~~~~~
108+
109+
.. code-block:: php
110+
111+
// src/AppBundle/AppBundle.php
112+
namespace AppBundle;
113+
114+
use AppBundle\DependencyInjection\Compiler\OverrideAssetsDefaultPackagePass;
115+
use Symfony\Component\DependencyInjection\ContainerBuilder;
116+
use Symfony\Component\HttpKernel\Bundle\Bundle;
117+
118+
class AppBundle extends Bundle
119+
{
120+
public function build(ContainerBuilder $container)
121+
{
122+
parent::build($container);
123+
124+
// only register in prod environment
125+
if ('prod' === $container->getParameter('kernel.environment')) {
126+
$container->addCompilerPass(new OverrideAssetsDefaultPackagePass());
127+
}
128+
}
129+
}
130+
131+
See :doc:`/cookbook/service_container/compiler_passes` for more information
132+
on how to use compiler passes.
133+
134+
Register Services
135+
~~~~~~~~~~~~~~~~~
136+
137+
.. configuration-block::
138+
139+
.. code-block:: yaml
140+
141+
# app/config/services.yml
142+
services:
143+
app.assets.buster_version_strategy:
144+
class: AppBundle\Asset\VersionStrategy\BusterVersionStrategy
145+
arguments:
146+
- "%kernel.root_dir%/../busters.json"
147+
- "%%s?version=%%s"
148+
public: false
149+
150+
.. code-block:: xml
151+
152+
<!-- app/config/services.xml -->
153+
<?xml version="1.0" encoding="UTF-8" ?>
154+
<container xmlns="http://symfony.com/schema/dic/services"
155+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
156+
xsi:schemaLocation="http://symfony.com/schema/dic/services
157+
http://symfony.com/schema/dic/services/services-1.0.xsd"
158+
>
159+
<services>
160+
<service id="app.assets.buster_version_strategy" class="AppBundle\Asset\VersionStrategy\BusterVersionStrategy" public="false">
161+
<argument>%kernel.root_dir%/../busters.json</argument>
162+
<argument>%%s?version=%%s</argument>
163+
</service>
164+
</services>
165+
</container>
166+
167+
.. code-block:: php
168+
169+
// app/config/services.php
170+
use Symfony\Component\DependencyInjection\Definition;
171+
172+
$definition = new Definition(
173+
'AppBundle\Asset\VersionStrategy\BusterVersionStrategy',
174+
array(
175+
'%kernel.root_dir%/../busters.json',
176+
'%%s?version=%%s',
177+
)
178+
);
179+
$definition->setPublic(false);
180+
181+
$container->setDefinition('app.assets.buster_version_strategy', $definition);
182+
183+
Implement VersionStrategyInterface
184+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185+
186+
.. code-block:: php
187+
188+
// src/AppBundle/Asset/VersionStrategy/BusterVersionStrategy.php
189+
namespace AppBundle\Asset\VersionStrategy;
190+
191+
use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
192+
193+
class BusterVersionStrategy implements VersionStrategyInterface
194+
{
195+
/**
196+
* @var string
197+
*/
198+
private $manifestPath;
199+
200+
/**
201+
* @var string
202+
*/
203+
private $format;
204+
205+
/**
206+
* @var string[]
207+
*/
208+
private $hashes;
209+
210+
/**
211+
* @param string $manifestPath
212+
* @param string|null $format
213+
*/
214+
public function __construct($manifestPath, $format = null)
215+
{
216+
$this->manifestPath = $manifestPath;
217+
$this->format = $format ?: '%s?%s';
218+
}
219+
220+
public function getVersion($path)
221+
{
222+
if (!is_array($this->hashes)) {
223+
$this->hashes = $this->loadManifest();
224+
}
225+
226+
return isset($this->hashes[$path]) ? $this->hashes[$path] : '';
227+
}
228+
229+
public function applyVersion($path)
230+
{
231+
$version = $this->getVersion($path);
232+
233+
if ('' === $version) {
234+
return $path;
235+
}
236+
237+
$versionized = sprintf($this->format, ltrim($path, '/'), $version);
238+
239+
if ($path && '/' === $path[0]) {
240+
return '/'.$versionized;
241+
}
242+
243+
return $versionized;
244+
}
245+
246+
private function loadManifest(array $options)
247+
{
248+
$hashes = json_decode(file_get_contents($this->manifestPath), true);
249+
250+
return $hashes;
251+
}
252+
}
253+
254+
.. _`gulp-buster`: https://www.npmjs.com/package/gulp-buster

cookbook/asset/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Asset
2+
=====
3+
4+
.. toctree::
5+
:maxdepth: 2
6+
7+
custom_version_strategy

reference/configuration/framework.rst

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,7 @@ option.
10901090
``version``. This makes it easier to increment the cache on each
10911091
deployment.
10921092

1093+
.. _reference-framework-assets-version-format:
10931094
.. _reference-templating-version-format:
10941095
.. _reference-assets-version-format:
10951096

@@ -1255,6 +1256,81 @@ templating loaders. Templating loaders are used to find and load templates
12551256
from a resource (e.g. a filesystem or database). Templating loaders must
12561257
implement :class:`Symfony\\Component\\Templating\\Loader\\LoaderInterface`.
12571258

1259+
.. _reference-framework-assets-packages:
1260+
1261+
packages
1262+
........
1263+
1264+
You can group assets into packages, to specify different base URLs for them:
1265+
1266+
.. configuration-block::
1267+
1268+
.. code-block:: yaml
1269+
1270+
# app/config/config.yml
1271+
framework:
1272+
# ...
1273+
templating:
1274+
packages:
1275+
avatars:
1276+
base_urls: 'http://static_cdn.example.com/avatars'
1277+
1278+
.. code-block:: xml
1279+
1280+
<!-- app/config/config.xml -->
1281+
<?xml version="1.0" encoding="UTF-8" ?>
1282+
<container xmlns="http://symfony.com/schema/dic/services"
1283+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1284+
xmlns:framework="http://symfony.com/schema/dic/symfony"
1285+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
1286+
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
1287+
1288+
<framework:config>
1289+
1290+
<framework:templating>
1291+
1292+
<framework:package
1293+
name="avatars"
1294+
base-url="http://static_cdn.example.com/avatars">
1295+
1296+
</framework:templating>
1297+
1298+
</framework:config>
1299+
</container>
1300+
1301+
.. code-block:: php
1302+
1303+
// app/config/config.php
1304+
$container->loadFromExtension('framework', array(
1305+
// ...
1306+
'templating' => array(
1307+
'packages' => array(
1308+
'avatars' => array(
1309+
'base_urls' => 'http://static_cdn.example.com/avatars',
1310+
),
1311+
),
1312+
),
1313+
));
1314+
1315+
Now you can use the ``avatars`` package in your templates:
1316+
1317+
.. configuration-block:: php
1318+
1319+
.. code-block:: html+jinja
1320+
1321+
<img src="{{ asset('...', 'avatars') }}">
1322+
1323+
.. code-block:: html+php
1324+
1325+
<img src="<?php echo $view['assets']->getUrl('...', 'avatars') ?>">
1326+
1327+
Each package can configure the following options:
1328+
1329+
* :ref:`base_urls <reference-templating-base-urls>`
1330+
* :ref:`version <reference-framework-assets-version>`
1331+
* :ref:`version_format <reference-templating-version-format>`
1332+
1333+
>>>>>>> Cookbook entry: Asset - Custom Version Strategy
12581334
translator
12591335
~~~~~~~~~~
12601336

reference/twig_reference.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ Returns an instance of ``ControllerReference`` to be used with functions
9393
like :ref:`render() <reference-twig-function-render>` and
9494
:ref:`render_esi() <reference-twig-function-render-esi>`.
9595

96+
.. _reference-twig-function-asset:
97+
9698
asset
9799
~~~~~
98100

0 commit comments

Comments
 (0)