Symfony2 assets versioning by file

2019-02-06 16:58发布

问题:

Question

Is it possible on Symfony2 use assets_version by file?

Background

We are using assets_version and assets_version_format to manage the files version and force the cache update on CDN's and browser cache.

This is working like charm!, but, we found that there is only one assets_version parameters for all the static resources used.

That is a problem since our webapp, has a long amount of static resources and we are deploying changes to prod environment daily. This situation kills the cache. :(

This is our current config:

config.yml

framework:

    templating:
        engines: ['twig']
        assets_version: %assets_version%
        assets_version_format:  "stv%%2$s/%%1$s"

# Assetic Configuration
assetic:
    debug:          %kernel.debug%
    use_controller: false
    # java: /usr/bin/java
    filters:
         cssrewrite: ~
         closure:
             jar: %kernel.root_dir%/java/compiler.jar
         yui_css:
             jar: %kernel.root_dir%/java/yuicompressor-2.4.6.jar

sometemplate.html.twig

    {% stylesheets 'bundles/webapp/css/funCommon.css'
                       'bundles/webapp/css/funMobile.css'
                       filter='?yui_css'
        %}
        <link rel=stylesheet href='{{ asset_url }}'>
        {% endstylesheets %}

    {% javascripts 'bundles/webapp/js/app.js'
                   'bundles/webapp/js/utils.js'
                    filter='?closure'  %}
        <script src="{{ asset_url }}"></script>
        {% endjavascripts %}

{% javascripts 'bundles/webapp/js/moduleX.js'
                   'bundles/webapp/js/utilsX.js'
                    filter='?closure'  %}
        <script src="{{ asset_url }}"></script>
        {% endjavascripts %}

When I change any css file or a module JS or any other file, all paths are changed.

I would like to manage the version parameter of assets_version_format by parameter of javascript/stylesheet twig tag.

This is what I'm looking for:

{% javascripts 'bundles/webapp/js/app.js'
               'bundles/webapp/js/utils.js'
                filter='?closure' **version='XX'** %}
    <script src="{{ asset_url }}"></script>
    {% endjavascripts %}

回答1:

After many days searching I found the packages option on AsseticBundle

http://symfony.com/doc/2.0/reference/configuration/framework.html#full-default-configuration

Using that config option I could do something like this:

{% javascripts file package='packageName' %}

or

{{asset(file,packageName)}}

Sample:

config.yml

framework:

    templating:
        engines: ['twig']
        assets_version: %assets_version%
        assets_version_format:  "stv%%2$s/%%1$s"
        packages:
             css:
                version: 6.1
                version_format: "stv%%2$s/%%1$s"
             jsApp:
                version: 4.2
                version_format: "stv%%2$s/%%1$s"

sometemplate.html.twig

<link rel=stylesheet href='{{ asset('bundles/webapp/css/funCommon.css','css') }}'>

{% javascripts 'bundles/webapp/js/app.js'
               'bundles/webapp/js/utils.js'
                filter='?closure'
                package='jsApp'
 %}
    <script src="{{ asset_url }}"></script>
 {% endjavascripts %}

The output of this is:

<link rel=stylesheet href="http://static.domain.com/stv6.1/css/HASH.css">

<script src="http://static.domain.com/stv4.2/js/HASH.js"></script>

For me that was the simplest way to manage assets version by file.



回答2:

If you're trying to use the assets_version parameter with the javascripts or stylesheets helpers, you still need to use the asset helper as well.

{% javascripts 'bundles/webapp/app.js'
               'bundles/webapp/utils.js'
               filter='?closure' %}
    <script src="{{ asset(asset_url) }}" type="text/javascript"></script>
{% endjavascripts %}

It is not added to asset_url automatically (which is a good thing).



回答3:

An easy and quick workaround is something like this:

{% set asset_version = 'xyz' %}

{% javascripts 'bundles/webapp/js/app.js'
           'bundles/webapp/js/utils.js'
            filter='?closure' %}
    <script src="{{ asset_url }}?{{ asset_version }}"></script>
{% endjavascripts %}

But you might want to move the logic to a twig extension receiving asset_url as argument.

The normal procedure would be to generate hashes of the files which will then be stored in a user cache.

You could then compare all the hashes against their current ones in a custom command and append the latest hash or something else to the filename to force a cache update.



回答4:

The following solution will append a timestamp instead of a version number. The important part is that he timestamp will only change when you clear your cache.

First create a compiler pass:

<?php

namespace App\Bundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;

/**
 * Created by PhpStorm.
 * User: hpenny
 * Date: 15/03/17
 * Time: 2:33 PM
 */
class AssetCompilerPass implements CompilerPassInterface
{
    /**
     * You can modify the container here before it is dumped to PHP code.
     *
     * @param ContainerBuilder $container
     */
    public function process(ContainerBuilder $container)
    {
        $container->removeDefinition('assets._version__default');
        $decorator = new DefinitionDecorator('assets.static_version_strategy');
        $decorator->replaceArgument(0, time());
        $container->setDefinition('assets._version__default', $decorator);
    }
}

Then add it to your main bundle:

namespace App\Bundle;

use App\Bundle\DependencyInjection\Compiler\AssetCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);

        $container->addCompilerPass(new AssetCompilerPass());
    }
}

This will only work on the default asset package. You would need to loop through the different package definitions you have set up if you are using that feature.

You would need to replace assets._version_<package name> instead of assets._version__default