Symfony2 assets versioning by file - php

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 %}

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.

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

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.

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

Related

Twig - rendering some variables(constant)?

I need some variables(constant) in my project:
path_to_root
path_to_images
I would like to make it without php, just Twig, so i create main.twig:
main.twig:
{% set path_to_root = "/some_folder" %}
{% set path_to_images = "/some_images" %}
In second template i extends by main.twig, so
template.twig
{% extends "main.twig" %}
{% block some_block %}
some_text
<img src="{{ path_to_images }}/image.png" />
{% endblock %}
Yes, it works, bu my question:
Is this good way for having main.twig and in child extends with main?
In each file of templates will i must extends with main.twig?
Sorry for my english :(
Not sure about other php files but the images should go in the assets folder which then you can import using
<img src="{{ asset('path/to/folder/image.png') }}" />

How to reference named Assetic assets in a Twig template?

AsseticBundle gives a possibility to define named assets in the Symfony's configuration file. How can I reference them in Twig templates, with {% javascripts %} and {% stylesheets %} Twig tags?
Symfony's own Assetic documentation mentions only special notation for #BundleName/Resources/file.cs to include bundle assets, but tells nothing about the named assets.
Question Symfony2: How to properly include assets in conjunction with Twig template inheritance? has a short mention in the comments that this is possible, but does not tell how to do it.
Using only the asset's given name (like jquery in the Assetic's own Readme) does not work. Assetic tries to look the file web/jquery that apparently does not exist.
The short version:
Use "#name" in your templates:
The longer version:
If your assetic configuration looks like this :
[config.yml]
assetic:
assets:
bootstrap_css:
inputs:
- %kernel.root_dir%/../web/components/bootstrap/css/bootstrap.css
- %kernel.root_dir%/../web/components/bootstrap/css/bootstrap-theme.css
jquery_ui_css:
....
Now you can use this in your templates, for example in your base.html.twig
<html><head>
...
{% stylesheets output='compiled/main.css'
"#bootstrap_css"
"#jqueryui_css"
...etc
%}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}
</head></html
This one also defines that all css that is in there should be concatenated into one main.css file in the compiled directory.

Set templates defined on DB

I've a project with backend and frontend.
In the backend I want to add one option to change template
The style is defined on base.html.twig on this line:
<link href="{{ asset('css/themes/default.css') }}" rel="stylesheet" type="text/css" id="style_color"/>
My question is how can I change the style when I load this file.
The template option will be stored on database.
EDIT:
In the moment I'm doing like this: I created an action on default controller to return a new Response with the template defined on DB, then in base.html.twig.
{{ render(controller("AdminPageBundle:Default:getTemplateAdmin")) }}
it works like this, but I don't think is the best way to do this
I hope that I understood the question. I assume that you're looking for a way to 'bind' the correct stylesheet depending on settings from the database, the latter being already solved
Twig provides an OOP like inheritence that you may leverage to make this work.
Here's a solution in three steps :
Step one : write a "theme aware" action. In the controller
...
public function adminAction(){
//get the settings information, this is hardcoded for the sake of esample
//you may as well fetch this from the DB
$themeSettings = array('style'=>'blue');
//render the template and inject the variable
return $this->render('AdminPageBundle:Default:template.html.twig',
array('theme'=>$themeSettings));
}
Step2 : create a base template with the default theme (failsafe)
//let's call it base.html.twig
<!DOCTYPE html>
<html>
<head>
...
{% block stylesheets %}
<link href="{{ asset('css/themes/default.css') }}" rel="stylesheet" type="text/css"/>
{% endblock %}
....
</head>
...
Step3 : Override the 'stylesheets' block in the theme-aware template
//template.html.twig:
{# make it a child of base.html.twig #}
{% extends '::base.html.twig' %}
{# override the stylesheets block #}
{% block stylesheets %}
{# include what you already have in the parent template (link to default.css) #}
{{ parent }}
{# add your own theme from the database (using the 'theme' variable from the controller #}
<link href="{{ asset('css/themes/'~ theme.style ~ '.css') }}" rel="stylesheet" type="text/css"/>
{% endblock %}
With this example, rendering 'template.html.twig' will load the stylesheet at '/css/themes/blue.css'.
You may as well implement this directly in the base template without relying on inheritence, but I'm assuming that some templates are theme-aware and some aren't. Inheritence provides flexibility to implement this feature only where you need it.
Of course, there are several other ways to solve the problem. Writing a custom twig extension to handle theming would be the right thing to do for a reliable long term solution, especially if theming is not just about loading a stylesheet. You may also write an event handler (for the kernel.view event) to modify the template or inject theme settings just before rendering. This answer shows an example.

how to override a css of sonata admin bundle in symfony2

i want to override a css file i.e reside in sonata-project/admin-bundle/Sonata/AdminBundle/Resources/public/bootstrap/css path of sonata admin bundle project.
Please help me out.
One way you can you override the css files of sonata admin but remember this will override the block of stylesheets but still you can call the stylesheets of parent block by calling {{ parent() }}
{% block stylesheets %}
/* this will override the parent block you can define here your css files*/
<link rel="stylesheet" href="{{ asset('cssfilepath.css') }}"/>
{% endblock %}
{% block stylesheets %}
/*this will extend the parent block */
{{ parent() }}
<link rel="stylesheet" href="{{ asset('cssfilepath.css') }}"/>
{% endblock %}
I used the answer of M Khalid Junaid that is working like a charm. Here some facts I missed while implementing this.
Update your config.yml like this:
sonata_admin:
templates:
list: AppBundle::Admin/list.html.twig
The src/AppBundle/Resources/views/Admin/list.html.twig then can look like this:
{% extends 'SonataAdminBundle:CRUD:base_list.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style type="text/css">
* {
}
</style>
<!-- and/or -->
<link rel="stylesheet" href="{{ asset('cssfilepath.css') }}"/>
{% endblock %}
It is possible to remove/add stylesheets in Sonata Admin Bundle within configuration file without handling template files:
sonata_admin:
assets:
remove_stylesheets:
- bundles/sonataadmin/css/old.css # path to vendors css-file to remove
extra_stylesheets:
- build/admin/css/new.css # your css-file to add
Considered above options are listed in Sonata Admin Bundle CONFIGURATION in FULL CONFIGURATION OPTIONS section.

Symfony2 : using assets in sub-layout templates

I'm working on a Symfony2 application and I'm trying to import resources (here, css and js) in a specifig twig template, instead of in the layout templates. However, what I've tried doesn't seem to work when I use the extends property. Yet it does work when there is no layout inheritance.
Here's the code I used :
{% block stylesheets %}
{% stylesheets 'bundles/mybundlename/css/style.css'
filter='cssrewrite' %}
<link rel="stylesheet" href="{{ asset_url }}" type="text/css" />
{% endstylesheets %}
{% endblock %}
{% block javascripts %}
{% javascripts 'bundles/mybundlename/js/jquery-1.7.2.min.js'
'bundles/mybundlename/js/jquery-ui-1.8.20.custom.min.js'
%}
<script src='{{ asset_url }}' type='text/javascript'></script>
{% endjavascripts %}
{% endblock %}
{% extends "MyBundle::layout.html.twig" %}
... the rest of my template ...
Bottom line, how would I import those resources in my inheriting template?
Or is it simply better to just import them in the general layout (but then it loads for every page :/) ?
Edit : I have used #MyBundle route types as well, and they are being searched by the engine (if the path is wrong, I get an error), but the resources are not added the the html section.
Second edit : The reason why it was not working is likely that I hadn't declared the {{% blocks %}} in my top level layout. I did and used {{ parent() }} (as a matter of "cleanliness") in my sub layouts and pages, and it works.
{% extends ... %} must come first.
You might also want to use the parent function to append to the stylesheets and javascripts defined in the layout instead of overwriting them.

Categories