10000 Cookbook entry: Asset - Custom Version Strategy · symfony/symfony-docs@2f8f74b · GitHub
[go: up one dir, main page]

Skip to content

Commit 2f8f74b

Browse files
committed
Cookbook entry: Asset - Custom Version Strategy
1 parent 4500f1c commit 2f8f74b

File tree

6 files changed

+280
-0
lines changed

6 files changed

+280
-0
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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+
- manifest_path: "%kernel.root_dir%/../busters.json"
147+
format: "%%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 type="collection">
162+
<argument key="manifest_path">%kernel.root_dir%/../busters.json</argument>
163+
<argument key="format">%%s?version=%%s</argument>
164+
</argument>
165+
</service>
166+
</services>
167+
</container>
168+
169+
.. code-block:: php
170+
171+
// app/config/services.php
172+
use Symfony\Component\DependencyInjection\Definition;
173+
174+
$definition = new Definition(
175+
'AppBundle\Asset\VersionStrategy\BusterVersionStrategy',
176+
array(
177+
array(
178+
'manifest_path' => '%kernel.root_dir%/../busters.json',
179+
'format' => '%%s?version=%%s',
180+
),
181+
)
182+
);
183+
$definition->setPublic(false);
184+
185+
$container->setDefinition('app.assets.buster_version_strategy', $definition);
186+
187+
Implement VersionStrategyInterface
188+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
189+
190+
.. code-block:: php
191+
192+
// src/AppBundle/Asset/VersionStrategy/BusterVersionStrategy.php
193+
namespace AppBundle\Asset\VersionStrategy;
194+
195+
use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;
196+
197+
class BusterVersionStrategy implements VersionStrategyInterface
198+
{
199+
/**
200+
* @var string
201+
*/
202+
private $manifestPath;
203+
204+
/**
205+
* @var string
206+
*/
207+
private $format;
208+
209+
/**
210+
* @var string[]
211+
*/
212+
private $hashes;
213+
214+
/**
215+
* @param array $options {
216+
* @var string $manifest_path
217+
* @var string|null $format
218+
* }
219+
*/
220+
public function __construct(array $options)
221+
{
222+
$this->manifestPath = $options['manifest_path'];
223+
$this->format = isset($options['format']) ? $options['format'] : '%s?%s';
224+
}
225+
226+
public function getVersion($path)
227+
{
228+
if (!is_array($this->hashes)) {
229+
$this->hashes = $this->loadManifest();
230+
}
231+
232+
return isset($this->hashes[$path]) ? $this->hashes[$path] : '';
233+
}
234+
235+
public function applyVersion($path)
236+
{
237+
$version = $this->getVersion($path);
238+
239+
if ('' === $version) {
240+
return $path;
241+
}
242+
243+
$versionized = sprintf($this->format, ltrim($path, '/'), $version);
244+
245+
if ($path && '/' === $path[0]) {
246+
return '/'.$versionized;
247+
}
248+
249+
return $versionized;
250+
}
251+
252+
private function loadManifest(array $options)
253+
{
254+
$hashes = json_decode(
255+
file_get_contents(realpath($this->manifestPath)),
256+
true
257+
);
258+
259+
return $hashes;
260+
}
261+
}
262+
263+
.. _`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

cookbook/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ The Cookbook
44
.. toctree::
55
:hidden:
66

7+
asset/index
78
assetic/index
89
bundles/index
910
cache/index

cookbook/map.rst.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* :doc:`/cookbook/asset/index`
2+
3+
* :doc:`/cookbook/asset/custom_version_strategy`
4+
15
* :doc:`/cookbook/assetic/index`
26

37
* :doc:`/cookbook/assetic/asset_management`

reference/configuration/framework.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,7 @@ option.
936936
``assets_version``. This makes it easier to increment the cache on each
937937
deployment.
938938

939+
.. _reference-framework-assets-version-format:
939940
.. _reference-templating-version-format:
940941

941942
assets_version_format
@@ -1209,6 +1210,8 @@ templating loaders. Templating loaders are used to find and load templates
12091210
from a resource (e.g. a filesystem or database). Templating loaders must
12101211
implement :class:`Symfony\\Component\\Templating\\Loader\\LoaderInterface`.
12111212

1213+
.. _reference-framework-assets-packages:
1214+
12121215
packages
12131216
........
12141217

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)
0