[Asset] added the component #13234

Merged
merged 4 commits into from Feb 10, 2015

Conversation

Projects
None yet
@fabpot
Member

fabpot commented Jan 4, 2015

Q A
Bug fix? yes
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets #10973, #11748, #11876, #4883, #12474
License MIT
Doc PR not yet

TODO:

  • submit documentation PR

The current Asset sub-namespace in Templating has several (major) problems:

  • It does not cover all use cases (see #10973 and #4883 for some example)
  • It has some design issues (relies on the Request instance and so requires the request scope, very coupled with the PHP templating sub-system, see #11748 and #11876)

To decouple this feature and make it reusable in Silex for instance, and to fix the design issues and make it more extensible, I've decided to extract and rework the features provided into a new Asset component.

Basically, this component allows the developer to easily manage asset URLs: versioning, paths, and hosts.

Both the new and the old asset management features are kept in this PR to avoid breaking BC; the old system is of course deprecated and automatically converted to the new one.

Even if the features are quite similar, and besides the flexilibity of the new system, here are some differences:

  • PathPackage always prepend the path (even if the given path starts with /).
  • Usage is stricter (for instance, PathPackage requires a basePath to be passed and UrlPackage requires that at least on URL is passed).
  • In the configuration, named packages inherits from the version and version format of the default package by default.
  • It is not possible to override the version when asking for a URL (instead, you can define your own version strategy implementation -- the use cases explained in #6092 are easily implemented this way).
  • It's not possible to generate absolute URLs (see #13264 for a better alternative using composition; so using absolute_url(asset_path('me.png')) should work).
    #10973 was about adding shortcuts for bundles, which is a good idea; but given that you rarely reference built-in or third-party bundle assets and because we now have a one-bundle default approach named AppBundle, the same can be achieved with just a simple piece of configuration with the new assets feature:
framework:
    assets:
        packages:
            app:
                base_path: /bundles/app/
            img:
                base_path: /bundles/app/images/

Then:

{{ asset('images/me.png', 'app') }}
# /bundles/app/images/me.png

{{ asset('me.png', 'img') }}
# /bundles/app/images/me.png

#12474 discussed the possibility to add a version for absolute URL. It's not possible to do that in a generic way as the version strategy involves both the version and the path, which obviously cannot work when the path is an absolute URL already. Instead, one should use the asset_version Twig function to add the version manually.

+ ->canBeUnset()
+ ->fixXmlConfig('base_url')
+ ->children()
+ ->scalarNode('version')->defaultValue(null)->end()

This comment has been minimized.

@acasademont

acasademont Jan 5, 2015

Contributor

wouldn't it be better defaultNull?

@acasademont

acasademont Jan 5, 2015

Contributor

wouldn't it be better defaultNull?

+ ->arrayNode('packages')
+ ->useAttributeAsKey('name')
+ ->prototype('array')
+ ->fixXmlConfig('base_url')

This comment has been minimized.

@acasademont

acasademont Jan 5, 2015

Contributor

could all this config below share the same code as the one before? extract it as a function maybe?

@acasademont

acasademont Jan 5, 2015

Contributor

could all this config below share the same code as the one before? extract it as a function maybe?

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

We could indeed use a private method returning an array node, and then using ->append() in both places

@stof

stof Jan 19, 2015

Member

We could indeed use a private method returning an array node, and then using ->append() in both places

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

Not sure it would bring us a lot.

@fabpot

fabpot Jan 19, 2015

Member

Not sure it would bring us a lot.

+
+ $this->createPackageDefinitions($config, $container);
+
+ // PHP templating engine

This comment has been minimized.

@acasademont

acasademont Jan 5, 2015

Contributor

should this be removed?

@acasademont

acasademont Jan 5, 2015

Contributor

should this be removed?

@acasademont

This comment has been minimized.

Show comment
Hide comment
@acasademont

acasademont Jan 5, 2015

Contributor

I like the overall simplification, no more weird behaviour depending on wether your packages contain ssl/non-ssl urls.

Contributor

acasademont commented Jan 5, 2015

I like the overall simplification, no more weird behaviour depending on wether your packages contain ssl/non-ssl urls.

+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "symfony/http-foundation": ""

This comment has been minimized.

@mickaelandrieu

mickaelandrieu Jan 5, 2015

Contributor

👍 thanks for this "totaly" standalone component !

@mickaelandrieu

mickaelandrieu Jan 5, 2015

Contributor

👍 thanks for this "totaly" standalone component !

@@ -442,6 +444,54 @@ private function addTemplatingSection(ArrayNodeDefinition $rootNode)
;
}
+ private function addAssetsSection(ArrayNodeDefinition $rootNode)
+ {

This comment has been minimized.

@@ -311,6 +312,7 @@ private function addRequestSection(ArrayNodeDefinition $rootNode)
private function addTemplatingSection(ArrayNodeDefinition $rootNode)
{
+ /** @deprecated since 2.7, will be removed in 3.0 */

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

the ->info() of the node should be updated to mark it as deprecated when dumping the config reference

@stof

stof Jan 5, 2015

Member

the ->info() of the node should be updated to mark it as deprecated when dumping the config reference

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

+ $oldAssetsConfigured = false;
+ if (
+ isset($config['templating'])
+ &&

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

too much indentation here

@stof

stof Jan 5, 2015

Member

too much indentation here

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

+ (
+ count($config['templating']['packages'])
+ ||
+ count($config['templating']['assets_base_urls']['http'])

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

I would use !empty(...), which would also remove the need for the initial isset($config['templating'])

@stof

stof Jan 5, 2015

Member

I would use !empty(...), which would also remove the need for the initial isset($config['templating'])

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

+
+ if ($container->has('assets.packages')) {
+ $container->getDefinition('twig.extension.assets')->addTag('twig.extension');
+ } elseif ($container->has('templating.asset.packages')) {

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

the issue with this logic is that it does not allow to have both the new asset_path() function and the old asset() function in the same project, forcing an all-or-nothing upgrade. This means that the whole bundle ecosystem has to upgrade at the same time to be able to be used together in a project.

IMO, the old extension should always be registered for BC reasons

@stof

stof Jan 5, 2015

Member

the issue with this logic is that it does not allow to have both the new asset_path() function and the old asset() function in the same project, forcing an all-or-nothing upgrade. This means that the whole bundle ecosystem has to upgrade at the same time to be able to be used together in a project.

IMO, the old extension should always be registered for BC reasons

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

The alternative solution is to have the new extension providing a BC layer for the asset() extension

@stof

stof Jan 5, 2015

Member

The alternative solution is to have the new extension providing a BC layer for the asset() extension

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

I've done that because the new function was named asset as well initially. Now that the name is different, it's easier to support both. I will update the patch.

@fabpot

fabpot Jan 5, 2015

Member

I've done that because the new function was named asset as well initially. Now that the name is different, it's easier to support both. I will update the patch.

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

fixed

@fabpot

fabpot Jan 5, 2015

Member

fixed

src/Symfony/Component/Asset/Package.php
+ */
+ public function getVersion($path)
+ {
+ return $this->versionStrategy ? $this->versionStrategy->getVersion($path) : '';

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

the ternary is useless, because we will always have a version strategy because of the constructor. The else branch is dead

@stof

stof Jan 5, 2015

Member

the ternary is useless, because we will always have a version strategy because of the constructor. The else branch is dead

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

obsolete code indeed.

@fabpot

fabpot Jan 5, 2015

Member

obsolete code indeed.

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

+ {
+ if ($this->isAbsoluteUrl($path)) {
+ return $path;
+ }

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

We have got some feature request about being able to apply versions on absolute URLs too when using the query string for cache busting (the main use case being image links generated by LiipImagineBundle IIRC). I cannot find the issue again though (searching for version in my github email notifications brings me all issues talking about libraries versions, making it hard).
It might be a good idea to try solving this case in the new componentt

@stof

stof Jan 5, 2015

Member

We have got some feature request about being able to apply versions on absolute URLs too when using the query string for cache busting (the main use case being image links generated by LiipImagineBundle IIRC). I cannot find the issue again though (searching for version in my github email notifications brings me all issues talking about libraries versions, making it hard).
It might be a good idea to try solving this case in the new componentt

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

It's not that easy as the version strategy can have any format involving the version and the path. So, not sure we want to mess up with the URL in such a case.

@fabpot

fabpot Jan 5, 2015

Member

It's not that easy as the version strategy can have any format involving the version and the path. So, not sure we want to mess up with the URL in such a case.

src/Symfony/Component/Asset/Package.php
+ $this->context = $context;
+ }
+
+ protected function getContext()

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

you should document it as @return ContextInterface|null. It is important for child classes

@stof

stof Jan 5, 2015

Member

you should document it as @return ContextInterface|null. It is important for child classes

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

src/Symfony/Component/Asset/Packages.php
+ *
+ * @return PackageInterface An asset package
+ *
+ * @throws \InvalidArgumentException If there is no package by that name

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

I suggest using a named exception here (extending \InvalidArgumentException) to allow it to be catched specifically

@stof

stof Jan 5, 2015

Member

I suggest using a named exception here (extending \InvalidArgumentException) to allow it to be catched specifically

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

@wouterj

This comment has been minimized.

Show comment
Hide comment
@wouterj

wouterj Jan 5, 2015

Member

Will this replace asset()? So should we use asset_path() instead after the merge?

I would also like to ask for a different name. Not that the name doesn't describe what the component is doing, but because this is already a very difficult thing to explain to beginners due to its naming. Lots of people are confusing asset() with Assetic. They don't know the difference, what they exactly do and how they work. Now, we add another thing named Asset. I think that'll confuse them even more.

Member

wouterj commented Jan 5, 2015

Will this replace asset()? So should we use asset_path() instead after the merge?

I would also like to ask for a different name. Not that the name doesn't describe what the component is doing, but because this is already a very difficult thing to explain to beginners due to its naming. Lots of people are confusing asset() with Assetic. They don't know the difference, what they exactly do and how they work. Now, we add another thing named Asset. I think that'll confuse them even more.

+ private function getContext($basePath)
+ {
+ $context = $this->getMock('Symfony\Component\Asset\Context\ContextInterface');
+ $context->expects($this->any())->method('isSecure')->will($this->returnValue($basePath));

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

isSecure returning the base path ? this looks wrong

@stof

stof Jan 5, 2015

Member

isSecure returning the base path ? this looks wrong

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

fixed, wrong var name.

@fabpot

fabpot Jan 5, 2015

Member

fixed, wrong var name.

+ throw new \LogicException('You must provide at least one base URL.');
+ }
+
+ $this->baseUrls = array();

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

I would move this to the property definition

@stof

stof Jan 5, 2015

Member

I would move this to the property definition

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

+ "name": "symfony/asset",
+ "type": "library",
+ "description": "Symfony Asset Component",
+ "keywords": [],

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

it would be better to define some keywords to make it easier to find in the Packagist search

@stof

stof Jan 5, 2015

Member

it would be better to define some keywords to make it easier to find in the Packagist search

+ "autoload": {
+ "psr-0": { "Symfony\\Component\\Asset\\": "" }
+ },
+ "target-dir": "Symfony/Component/Asset",

This comment has been minimized.

@stof

stof Jan 5, 2015

Member

please use PSR-4 instead of PSR-0 and the legacy target-dir feature. It is deprecated: https://getcomposer.org/doc/04-schema.md#target-dir

@stof

stof Jan 5, 2015

Member

please use PSR-4 instead of PSR-0 and the legacy target-dir feature. It is deprecated: https://getcomposer.org/doc/04-schema.md#target-dir

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot

fabpot Jan 5, 2015

Member

done

@fabpot fabpot changed the title from [WIP] [Asset] added the component to [Asset] added the component Jan 5, 2015

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot Jan 5, 2015

Member

@wouterj I will submit a PR for the docs soon, but basically, asset_path() replaces asset(). Not sure if we can find another name as asset is used by many popular frameworks out there (Rails being one of them). The "Assetic" name is unfortunate but I don't see a way around it.

Member

fabpot commented Jan 5, 2015

@wouterj I will submit a PR for the docs soon, but basically, asset_path() replaces asset(). Not sure if we can find another name as asset is used by many popular frameworks out there (Rails being one of them). The "Assetic" name is unfortunate but I don't see a way around it.

+ public function getFunctions()
+ {
+ return array(
+ new \Twig_SimpleFunction('asset_path', array($this, 'getAssetPath')),

This comment has been minimized.

@acasademont

acasademont Jan 5, 2015

Contributor

would it be possible to keep the old function name? and IMHO the possibility to pass a specific version for the asset was useful, could we restore that functionality back?

@acasademont

acasademont Jan 5, 2015

Contributor

would it be possible to keep the old function name? and IMHO the possibility to pass a specific version for the asset was useful, could we restore that functionality back?

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

Keeping the old name would not allow us to keep both layers as explained in the description of this PR. Passing a specific version is a non-sense and using a custom version strategy is the best way to achieve that feature (also explained in the description of the PR).

@fabpot

fabpot Jan 5, 2015

Member

Keeping the old name would not allow us to keep both layers as explained in the description of this PR. Passing a specific version is a non-sense and using a custom version strategy is the best way to achieve that feature (also explained in the description of the PR).

This comment has been minimized.

@acasademont

acasademont Jan 5, 2015

Contributor

Thanks!

@acasademont

acasademont Jan 5, 2015

Contributor

Thanks!

This comment has been minimized.

@acasademont

acasademont Jan 5, 2015

Contributor

The thing with the new name it is that it's a little bit misleading because it can indeed generate absolute url's (not only paths) if you are using an UrlPackage, it just can't generate an absolute url if you are using a PathPackage.

@acasademont

acasademont Jan 5, 2015

Contributor

The thing with the new name it is that it's a little bit misleading because it can indeed generate absolute url's (not only paths) if you are using an UrlPackage, it just can't generate an absolute url if you are using a PathPackage.

This comment has been minimized.

@fabpot

fabpot Jan 5, 2015

Member

I agree with you. Any ideas for another name?

@fabpot

fabpot Jan 5, 2015

Member

I agree with you. Any ideas for another name?

This comment has been minimized.

@acasademont

acasademont Jan 6, 2015

Contributor

I still think that we sould keep the old name, it's the best one. And it's a pitty that we have to struggle to find a new name just for BC reasons. I would prefer to have a ""harder"" approach like the one had before (only one layer at a time possible) and keep the same name.

Besides, keeping the old name makes it easier to upgrade, as the removed "absolute" and "version" params were mostly unused, and the asset name and package name remain in the new function, so there is almost full BC in the twig files if you choose to use the new layer, no need to rename anything.

@acasademont

acasademont Jan 6, 2015

Contributor

I still think that we sould keep the old name, it's the best one. And it's a pitty that we have to struggle to find a new name just for BC reasons. I would prefer to have a ""harder"" approach like the one had before (only one layer at a time possible) and keep the same name.

Besides, keeping the old name makes it easier to upgrade, as the removed "absolute" and "version" params were mostly unused, and the asset name and package name remain in the new function, so there is almost full BC in the twig files if you choose to use the new layer, no need to rename anything.

This comment has been minimized.

@stof

stof Jan 6, 2015

Member

@acasademont only one layer at a time means forcing all the open-source ecosystem to update at the same time to stay compatible (and then projects depending on them need to migrate when updating the dependencies).

The other solution is to find a way to implement a BC layer for the old API in the new system, to be able to reuse the names in a BC way.

@stof

stof Jan 6, 2015

Member

@acasademont only one layer at a time means forcing all the open-source ecosystem to update at the same time to stay compatible (and then projects depending on them need to migrate when updating the dependencies).

The other solution is to find a way to implement a BC layer for the old API in the new system, to be able to reuse the names in a BC way.

This comment has been minimized.

@acasademont

acasademont Jan 6, 2015

Contributor

Even with what you propose I don't see a clear update path. OS bundles will keep using the asset function because they won't know if in your project you have enabled the new component and can start using asset_path. So when Symfony 3 comes they'll have to update to the new function name to stay compatible.

Besides, I'm pretty sure that in 99.9% of the asset function calls out there in external bundles there are no packages and no use of the absolute or version params (or like the WebProfilerBundle, they removed all asset calls and changed them for inline base64-encoded images). So keeping the same name is the best option for them, they probably won't even have to bother about the change, the simple asset('/path/to/my/bundle/foo.jpg') call has the exact same syntax and result in the new layer if we keep the name.

Made a quick search here https://github.com/search?p=6&q=asset+NOT+url+extension%3Atwig&ref=searchresults&type=Code&utf8=%E2%9C%93 (unfortunately you can't search the parentheses symbol, which would narrow down the search a lot)

@acasademont

acasademont Jan 6, 2015

Contributor

Even with what you propose I don't see a clear update path. OS bundles will keep using the asset function because they won't know if in your project you have enabled the new component and can start using asset_path. So when Symfony 3 comes they'll have to update to the new function name to stay compatible.

Besides, I'm pretty sure that in 99.9% of the asset function calls out there in external bundles there are no packages and no use of the absolute or version params (or like the WebProfilerBundle, they removed all asset calls and changed them for inline base64-encoded images). So keeping the same name is the best option for them, they probably won't even have to bother about the change, the simple asset('/path/to/my/bundle/foo.jpg') call has the exact same syntax and result in the new layer if we keep the name.

Made a quick search here https://github.com/search?p=6&q=asset+NOT+url+extension%3Atwig&ref=searchresults&type=Code&utf8=%E2%9C%93 (unfortunately you can't search the parentheses symbol, which would narrow down the search a lot)

This comment has been minimized.

@stof

stof Jan 18, 2015

Member

@fabpot any idea whether it would be possible to build the previous API as a BC layer on top of the new one, to avoid having to change all templates everywhere when migrating to the new API (making the upgrade path much better) ?

@stof

stof Jan 18, 2015

Member

@fabpot any idea whether it would be possible to build the previous API as a BC layer on top of the new one, to avoid having to change all templates everywhere when migrating to the new API (making the upgrade path much better) ?

@Haehnchen Haehnchen referenced this pull request in Haehnchen/idea-php-symfony2-plugin Jan 5, 2015

Open

Add support for new Asset component #411

@stof

This comment has been minimized.

Show comment
Hide comment
@stof

stof Jan 6, 2015

Member

I found the issue about making asset version work with absolute URLs. It is #12474

Member

stof commented Jan 6, 2015

I found the issue about making asset version work with absolute URLs. It is #12474

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot Jan 6, 2015

Member

@stof thanks for finding it. As I said before, that's just not possible in a generic way. It's possible with the default version strategy which is to use the version as a query string, but we all know that this is not the way to go if you area serious about asset versioning and CDNs. Instead, you should use the asset_version Twig function (or the current assets_version one) to manually add the version to your URL. I've added a small paragraph about that in the description of the PR.

Member

fabpot commented Jan 6, 2015

@stof thanks for finding it. As I said before, that's just not possible in a generic way. It's possible with the default version strategy which is to use the version as a query string, but we all know that this is not the way to go if you area serious about asset versioning and CDNs. Instead, you should use the asset_version Twig function (or the current assets_version one) to manually add the version to your URL. I've added a small paragraph about that in the description of the PR.

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot Jan 10, 2015

Member

The decoupling part of this PR has been moved to #13354. This one will be rebased and merged after #13354

Member

fabpot commented Jan 10, 2015

The decoupling part of this PR has been moved to #13354. This one will be rebased and merged after #13354

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot Jan 10, 2015

Member

Just rebased this PR so it's now just about adding the new Asset Component.

Member

fabpot commented Jan 10, 2015

Just rebased this PR so it's now just about adding the new Asset Component.

+ protected function isAbsoluteUrl($url)
+ {
+ return false !== strpos($url, '://') || '//' === substr($url, 0, 2);
+ }

This comment has been minimized.

@jakzal

jakzal Jan 11, 2015

Member

Are these three methods intentionally protected, or should be private?

@jakzal

jakzal Jan 11, 2015

Member

Are these three methods intentionally protected, or should be private?

This comment has been minimized.

@jakzal

jakzal Jan 11, 2015

Member

Sorry, just noticed this class is being extended.

@jakzal

jakzal Jan 11, 2015

Member

Sorry, just noticed this class is being extended.

+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class PathPackage extends Package

This comment has been minimized.

@jakzal

jakzal Jan 11, 2015

Member

I'd consider decorating the PackageInterface instead of extending the Package. This way it would be possible to prepend the base path to any PackageInterface implementation. The same would apply to the UrlPackage.

@jakzal

jakzal Jan 11, 2015

Member

I'd consider decorating the PackageInterface instead of extending the Package. This way it would be possible to prepend the base path to any PackageInterface implementation. The same would apply to the UrlPackage.

This comment has been minimized.

@jakzal

jakzal Jan 11, 2015

Member

After reviewing this PR in full, decoration is probably not worth it. It would only complicate the code that the end-user needs to write, and the component is flexible enough with the version strategy. It shouldn't be needed to extend the Package classes (they could actually be final).

@jakzal

jakzal Jan 11, 2015

Member

After reviewing this PR in full, decoration is probably not worth it. It would only complicate the code that the end-user needs to write, and the component is flexible enough with the version strategy. It shouldn't be needed to extend the Package classes (they could actually be final).

@fabpot fabpot added the Templating label Jan 16, 2015

@fabpot

This comment has been minimized.

Show comment
Hide comment
@fabpot

fabpot Jan 19, 2015

Member

@acasademont @stof I've added a new commit where there is only one asset Twig function which tries to support the old and the new behavior. For the configuration, the old one is converted into the new one. The tests show the behavior differences between the two versions, and of course, the BC breaks explained in the description are still true.

Member

fabpot commented Jan 19, 2015

@acasademont @stof I've added a new commit where there is only one asset Twig function which tries to support the old and the new behavior. For the configuration, the old one is converted into the new one. The tests show the behavior differences between the two versions, and of course, the BC breaks explained in the description are still true.

@acasademont

This comment has been minimized.

Show comment
Hide comment
@acasademont

acasademont Jan 19, 2015

Contributor

Thanks @fabpot, I really think that keeping the old name is much better!

Contributor

acasademont commented Jan 19, 2015

Thanks @fabpot, I really think that keeping the old name is much better!

@stof

This comment has been minimized.

Show comment
Hide comment
@stof

stof Jan 19, 2015

Member

@fabpot the old Twig extension should be marked as deprecated

Member

stof commented Jan 19, 2015

@fabpot the old Twig extension should be marked as deprecated

@stof

This comment has been minimized.

Show comment
Hide comment
@stof

stof Jan 19, 2015

Member

the description of the PR needs to be updated, as it still talks about asset_path

Member

stof commented Jan 19, 2015

the description of the PR needs to be updated, as it still talks about asset_path

+
+ if ($version) {
+ $v->setValue($package, $currentVersionStrategy);
+ }

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

what if getting the url of the package throws an exception for some reason ? Resetting the internal state needs to be done in this case too, to avoid leaving it in a bad state

@stof

stof Jan 19, 2015

Member

what if getting the url of the package throws an exception for some reason ? Resetting the internal state needs to be done in this case too, to avoid leaving it in a bad state

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

fixed

@fabpot

fabpot Jan 19, 2015

Member

fixed

+ || count($v['templating']['assets_base_urls']['ssl'])
+ || count($v['templating']['packages'])
+ ) {
+ trigger_error('The framework.assets configuration key is deprecated since version 2.7 and will be removed in 3.0. Use the framework.assets configuration key instead', E_USER_DEPRECATED);

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

there is no framework.assets key in older versions. there is framework.templating.*

@stof

stof Jan 19, 2015

Member

there is no framework.assets key in older versions. there is framework.templating.*

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

fixed

@fabpot

fabpot Jan 19, 2015

Member

fixed

+ 'version' => (string) $config['version'],
+ 'version_format' => $config['version_format'],
+ 'base_path' => '',
+ 'base_urls' => array_unique(array_merge($config['base_urls']['http'], $config['base_urls']['ssl'])),

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

should be array_values(array_unique()), to have 0-based indexes to be consistent with the case of the new config

@stof

stof Jan 19, 2015

Member

should be array_values(array_unique()), to have 0-based indexes to be consistent with the case of the new config

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

fixed, and above as well

@fabpot

fabpot Jan 19, 2015

Member

fixed, and above as well

@@ -57,6 +61,8 @@ public function getFunctions()
*/
public function getAssetUrl($path, $packageName = null, $absolute = false, $version = null)
{
+ trigger_error('The Twig asset() function was deprecated in 2.7 and will be removed in 3.0. Please use asset_path() instead.', E_USER_DEPRECATED);

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

wrong deprecation message as the name is now the scene

@stof

stof Jan 19, 2015

Member

wrong deprecation message as the name is now the scene

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

I've removed this deprecation message and the one below as the whole class is now deprecated.

@fabpot

fabpot Jan 19, 2015

Member

I've removed this deprecation message and the one below as the whole class is now deprecated.

src/Symfony/Component/Asset/Package.php
+ return $this->versionStrategy->applyVersion($path);
+ }
+
+ public function setContext(ContextInterface $context)

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

what do you think about making the context a mandatory argument (or an optional argument in the constructor but building an instance internally in this case) instead of an optional deps, and providing a basic context using static values passed in its constructor (with the default values of these arguments matching the current behavior where there is no context) ? It would simplify the code by avoiding to check for null everywhere (null-object pattern)

@stof

stof Jan 19, 2015

Member

what do you think about making the context a mandatory argument (or an optional argument in the constructor but building an instance internally in this case) instead of an optional deps, and providing a basic context using static values passed in its constructor (with the default values of these arguments matching the current behavior where there is no context) ? It would simplify the code by avoiding to check for null everywhere (null-object pattern)

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

done

src/Symfony/Component/Asset/README.md
+You can run the unit tests with the following command:
+
+ $ cd path/to/Symfony/Component/Asset/
+ $ composer.phar install

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

should be composer install to match the usage of Composer in the documentation (and actually, we should use composer update here so that they run against uptodate deps given that we don't commit a lock file)

@stof

stof Jan 19, 2015

Member

should be composer install to match the usage of Composer in the documentation (and actually, we should use composer update here so that they run against uptodate deps given that we don't commit a lock file)

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

done

+ "symfony/http-foundation": "~2.4"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Asset\\": "Symfony/Component/Asset" }

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

this should be "psr-4": { "Symfony\\Component\\Asset\\": "" }, because the Symfony\\Component\\Asset namespace is at the root of the subtree package

@stof

stof Jan 19, 2015

Member

this should be "psr-4": { "Symfony\\Component\\Asset\\": "" }, because the Symfony\\Component\\Asset namespace is at the root of the subtree package

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

fixed

@fabpot

fabpot Jan 19, 2015

Member

fixed

@@ -13,7 +13,7 @@
use Symfony\Component\Templating\Helper\CoreAssetsHelper;
-class CoreAssetsHelperTest extends \PHPUnit_Framework_TestCase
+class LegacyCoreAssetsHelperTest extends \PHPUnit_Framework_TestCase

This comment has been minimized.

@stof

stof Jan 19, 2015

Member

you need the initSet calls in the setUp method for these 2 test classes

@stof

stof Jan 19, 2015

Member

you need the initSet calls in the setUp method for these 2 test classes

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

Right, good catch. Fixed now.

@fabpot

fabpot Jan 19, 2015

Member

Right, good catch. Fixed now.

+ {
+ // BC layer to be removed in 3.0
+ if (2 < $count = func_num_args()) {
+ trigger_error('Generating absolute URL with the Twig asset() function was deprecated in 2.7 and will be removed in 3.0. Please use absolute_url() instead.', E_USER_DEPRECATED);

This comment has been minimized.

@xabbuh

xabbuh Jan 19, 2015

Member

"Generating an absolute URL [...]" or "Generating absolute URLs [...]"?

@xabbuh

xabbuh Jan 19, 2015

Member

"Generating an absolute URL [...]" or "Generating absolute URLs [...]"?

This comment has been minimized.

@fabpot

fabpot Jan 19, 2015

Member

fixed

@fabpot

fabpot Jan 19, 2015

Member

fixed

src/Symfony/Component/Asset/Package.php
+ }
+
+ /**
+ * @return ContextInterface|null

This comment has been minimized.

@stof

stof Jan 20, 2015

Member

this cannot be null anymore

@stof

stof Jan 20, 2015

Member

this cannot be null anymore

+// http://img.example.com/me.png?v1
+
+echo $packages->getUrl('/me.pdf', 'doc');
+// /somewhere/deep/for/documents/me.pdf?v1

This comment has been minimized.

@stof

stof Jan 20, 2015

Member

Should we put so much details in the readme ? It would duplicate the documentation (and will probably not be updated when the doc team improves the doc because they won't remember that this component has a verbose readme). I think most of this content should rather be submitted in a doc PR

@stof

stof Jan 20, 2015

Member

Should we put so much details in the readme ? It would duplicate the documentation (and will probably not be updated when the doc team improves the doc because they won't remember that this component has a verbose readme). I think most of this content should rather be submitted in a doc PR

@fabpot fabpot merged commit 0750d02 into symfony:2.7 Feb 10, 2015

0 of 2 checks passed

continuous-integration/travis-ci The Travis CI build failed
Details
fabbot.io Doing some proofreading and checking your coding style.
Details

fabpot added a commit that referenced this pull request Feb 10, 2015

feature #13234 [Asset] added the component (fabpot)
This PR was merged into the 2.7 branch.

Discussion
----------

[Asset] added the component

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #10973, #11748, #11876, #4883, #12474
| License       | MIT
| Doc PR        | not yet

TODO:

 - [ ] submit documentation PR

The current Asset sub-namespace in Templating has several (major) problems:

 * It does not cover all use cases (see #10973 and #4883 for some example)
 * It has some design issues (relies on the Request instance and so requires the request scope, very coupled with the PHP templating sub-system, see #11748 and #11876)

To decouple this feature and make it reusable in Silex for instance, and to fix the design issues and make it more extensible, I've decided to extract and rework the features provided into a new Asset component.

Basically, this component allows the developer to easily manage asset URLs: versioning, paths, and hosts.

Both the new and the old asset management features are kept in this PR to avoid breaking BC; the old system is of course deprecated and automatically converted to the new one.

Even if the features are quite similar, and besides the flexilibity of the new system, here are some differences:

 * `PathPackage` always prepend the path (even if the given path starts with `/`).
 * Usage is stricter (for instance, `PathPackage` requires a basePath to be passed and `UrlPackage` requires that at least on URL is passed).
 * In the configuration, named packages inherits from the version and version format of the default package by default.
 * It is not possible to override the version when asking for a URL (instead, you can define your own version strategy implementation -- the use cases explained in #6092 are easily implemented this way).
 * It's not possible to generate absolute URLs (see #13264 for a better alternative using composition; so using `absolute_url(asset_path('me.png')) should work)`.

#10973 was about adding shortcuts for bundles, which is a good idea; but given that you rarely reference built-in or third-party bundle assets and because we now have a one-bundle default approach named AppBundle, the same can be achieved with just a simple piece of configuration with the new assets feature:

```yml
framework:
    assets:
        packages:
            app:
                base_path: /bundles/app/
            img:
                base_path: /bundles/app/images/
```

Then:

```jinja
{{ asset('images/me.png', 'app') }}
# /bundles/app/images/me.png

{{ asset('me.png', 'img') }}
# /bundles/app/images/me.png
```

#12474 discussed the possibility to add a version for absolute URL. It's not possible to do that in a generic way as the version strategy involves both the version and the path, which obviously cannot work when the path is an absolute URL already. Instead, one should use the `asset_version` Twig function to add the version manually.

Commits
-------

0750d02 removed usage of the deprecated forms of asset() in the core framework
f74a1f2 renamed asset_path() to asset() and added a BC layer
4d0adea [Asset] added a NullContext class
d33c41d [Asset] added the component

@javiereguiluz javiereguiluz referenced this pull request in symfony/symfony-docs Feb 10, 2015

Closed

Document the new Asset component + framework changes #4982

3 of 4 tasks complete

@fabpot fabpot deleted the fabpot:asset-component branch Feb 12, 2015

@lsmith77

This comment has been minimized.

Show comment
Hide comment
@lsmith77

lsmith77 May 1, 2015

Contributor

does the following imply an unwanted regression with this change? liip/LiipMonitorBundle#103

Contributor

lsmith77 commented on f74a1f2 May 1, 2015

does the following imply an unwanted regression with this change? liip/LiipMonitorBundle#103

This comment has been minimized.

Show comment
Hide comment
@xabbuh

xabbuh May 1, 2015

Member

@lsmith77 looks like what is described in #14368

Member

xabbuh replied May 1, 2015

@lsmith77 looks like what is described in #14368

@lsmith77

This comment has been minimized.

Show comment
Hide comment
@lsmith77

lsmith77 May 1, 2015

Contributor

does the following imply an unwanted regression with this change? liip/LiipMonitorBundle#103

Contributor

lsmith77 commented on f74a1f2 May 1, 2015

does the following imply an unwanted regression with this change? liip/LiipMonitorBundle#103

This comment has been minimized.

Show comment
Hide comment
@xabbuh

xabbuh May 1, 2015

Member

@lsmith77 looks like what is described in #14368

Member

xabbuh replied May 1, 2015

@lsmith77 looks like what is described in #14368

@Guite Guite referenced this pull request in zikula/core Jun 9, 2015

Merged

Updated to Symfony 2.7 LTS #2486

@svassaux

This comment has been minimized.

Show comment
Hide comment
@svassaux

svassaux Oct 18, 2015

The following does not work anymore:

img src="{{ asset('images/user.png', 'images') | imagine_filter('default') }}" alt="Image de profil" class="img-circle whitebg"

see http://stackoverflow.com/questions/33200339/symfony-2-7-asset-component-not-working-with-imagine-filter

The following does not work anymore:

img src="{{ asset('images/user.png', 'images') | imagine_filter('default') }}" alt="Image de profil" class="img-circle whitebg"

see http://stackoverflow.com/questions/33200339/symfony-2-7-asset-component-not-working-with-imagine-filter

@jakzal

This comment has been minimized.

Show comment
Hide comment
@jakzal

jakzal Oct 18, 2015

Member

@svassaux if you think there's a bug, please create a new issue.

Member

jakzal commented Oct 18, 2015

@svassaux if you think there's a bug, please create a new issue.

@12th

This comment has been minimized.

Show comment
Hide comment
@12th

12th Oct 25, 2015

If use absolute_url() for image from the CLI the url is without host, because masterRequest is empty.
Please tell me how get absolute url for image, for example in email?

12th commented Oct 25, 2015

If use absolute_url() for image from the CLI the url is without host, because masterRequest is empty.
Please tell me how get absolute url for image, for example in email?

@acasademont

This comment has been minimized.

Show comment
Hide comment
@12th

This comment has been minimized.

Show comment
Hide comment
@12th

12th Oct 25, 2015

@acasademont
Yes, I saw this example, but absolute route work only for twig url() for tag , and when try use absolute for asset(), this not work.

/**
     * Send email to player
     *
     * @param Letter $letter
     * @return int
     */
    public function sendLetter(Letter $letter)
    {
        $context = [];
        $toEmail = '';
        $fromEmail = '';
        $result = null;

        $email = $letter->getEmail();

        switch ($email->getType()) {
            case Email::TYPE_INVITE:
                $mailer = $this->parameters['mailers'][$letter->getEmail()->getGateway()->getSlug()];

                $invite = $this->_inviteHelper->findOneBy(['id' => $letter->getEntityId()]);

                $context['token'] = $letter->getToken();
                $context['entity'] = $this->_emailHelper->getEntity($letter);

                $toEmail = $invite->getEmail();
                $fromEmail = [$mailer['send_from'] => $mailer['sender_name']];

                break;
            default:
                $mailer = $this->parameters['mailers']['default'];
        }

        // For CLI mailing, because request does not has http request
        $requestContext = $this->router->getContext();
        $requestContext
            ->setHost($mailer['site_http_host'])
            ->setScheme($mailer['site_http_schema'])
        ;

        // Change default settings for sending special
        $transport = \Swift_SmtpTransport::newInstance($mailer['host'], $mailer['port'], true)
            ->setUsername($mailer['username'])
            ->setPassword($mailer['password'])
            ->setEncryption($mailer['encryption'])
        ;

        $this->mailer = \Swift_Mailer::newInstance($transport);

        $this->twig->setLoader(new \Twig_Loader_Array(['letter' => $email->getBodyHtml()]));

        $context  = $this->twig->mergeGlobals($context);
        $htmlBody = $this->twig->render('letter', $context);

        // Catch incorrect email
        try {
            $message = \Swift_Message::newInstance()
                ->setSubject($email->getSubject())
                ->setFrom($fromEmail)
                ->setTo($toEmail);

            if ( ! empty($htmlBody)) {
                $message
                    ->setBody($htmlBody, 'text/html')
                    ->addPart($email->getBodyText(), 'text/plain')
                ;
            } else {
                $message->setBody($email->getBodyText());
            }

            $result = $this->mailer->send($message);

        } catch (\Exception $ex) {
            $letter->setStatus(Letter::STATUS_ERROR_SEND);
            $this->_emailHelper->updateLetter($letter);

            VarDumper::dump($ex->getMessage());

            return false;
        }

        $letter->setStatus(Letter::STATUS_SENT);

        if ( ! (bool)$result) {
            $letter->setStatus(Letter::STATUS_ERROR_SEND);
        }

        $this->_emailHelper->updateLetter($letter);

        return $result;
    }

Services

app_main.mailer:
      class: App\MainBundle\Model\Mailer
      arguments:
          - @mailer
          - @router
          - @twig
          - @swiftmailer.transport.real

Config:

swiftmailer:
    transport:  "%mailer_transport%"
    host:       "%mailer_host%"
    username:   "%mailer_user%"
    password:   "%mailer_password%"
    port:       "%mailer_port%"
    encryption: "%mailer_encryption%"
    auth_mode:  login
    spool:     { type: memory }

When try use flush the queue after send, catch error User Error: Call to undefined method getSpool

...
$result = $this->mailer->send($message);

// now manually flush the queue
$spool = $this->mailer->getTransport()->getSpool();
$spool->flushQueue($this->_esmtpTransport);

If dump getMasterRequest() in generateAbsoluteUrl(), the request is null, and I get path without host

/**
     * Returns the absolute URL for the given absolute or relative path.
     *
     * This method returns the path unchanged if no request is available.
     *
     * @param string $path The path
     *
     * @return string The absolute URL
     *
     * @see Request::getUriForPath()
     */
    public function generateAbsoluteUrl($path)
    {
        if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
            return $path;
        }

       VarDumper::dump($this->requestStack->getMasterRequest());die;

        if (!$request = $this->requestStack->getMasterRequest()) {
            return $path;
        }



        if (!$path || '/' !== $path[0]) {
            $prefix = $request->getPathInfo();
            $last = strlen($prefix) - 1;
            if ($last !== $pos = strrpos($prefix, '/')) {
                $prefix = substr($prefix, 0, $pos).'/';
            }

            return $request->getUriForPath($prefix.$path);
        }

        return $request->getSchemeAndHttpHost().$path;
    }

12th commented Oct 25, 2015

@acasademont
Yes, I saw this example, but absolute route work only for twig url() for tag , and when try use absolute for asset(), this not work.

/**
     * Send email to player
     *
     * @param Letter $letter
     * @return int
     */
    public function sendLetter(Letter $letter)
    {
        $context = [];
        $toEmail = '';
        $fromEmail = '';
        $result = null;

        $email = $letter->getEmail();

        switch ($email->getType()) {
            case Email::TYPE_INVITE:
                $mailer = $this->parameters['mailers'][$letter->getEmail()->getGateway()->getSlug()];

                $invite = $this->_inviteHelper->findOneBy(['id' => $letter->getEntityId()]);

                $context['token'] = $letter->getToken();
                $context['entity'] = $this->_emailHelper->getEntity($letter);

                $toEmail = $invite->getEmail();
                $fromEmail = [$mailer['send_from'] => $mailer['sender_name']];

                break;
            default:
                $mailer = $this->parameters['mailers']['default'];
        }

        // For CLI mailing, because request does not has http request
        $requestContext = $this->router->getContext();
        $requestContext
            ->setHost($mailer['site_http_host'])
            ->setScheme($mailer['site_http_schema'])
        ;

        // Change default settings for sending special
        $transport = \Swift_SmtpTransport::newInstance($mailer['host'], $mailer['port'], true)
            ->setUsername($mailer['username'])
            ->setPassword($mailer['password'])
            ->setEncryption($mailer['encryption'])
        ;

        $this->mailer = \Swift_Mailer::newInstance($transport);

        $this->twig->setLoader(new \Twig_Loader_Array(['letter' => $email->getBodyHtml()]));

        $context  = $this->twig->mergeGlobals($context);
        $htmlBody = $this->twig->render('letter', $context);

        // Catch incorrect email
        try {
            $message = \Swift_Message::newInstance()
                ->setSubject($email->getSubject())
                ->setFrom($fromEmail)
                ->setTo($toEmail);

            if ( ! empty($htmlBody)) {
                $message
                    ->setBody($htmlBody, 'text/html')
                    ->addPart($email->getBodyText(), 'text/plain')
                ;
            } else {
                $message->setBody($email->getBodyText());
            }

            $result = $this->mailer->send($message);

        } catch (\Exception $ex) {
            $letter->setStatus(Letter::STATUS_ERROR_SEND);
            $this->_emailHelper->updateLetter($letter);

            VarDumper::dump($ex->getMessage());

            return false;
        }

        $letter->setStatus(Letter::STATUS_SENT);

        if ( ! (bool)$result) {
            $letter->setStatus(Letter::STATUS_ERROR_SEND);
        }

        $this->_emailHelper->updateLetter($letter);

        return $result;
    }

Services

app_main.mailer:
      class: App\MainBundle\Model\Mailer
      arguments:
          - @mailer
          - @router
          - @twig
          - @swiftmailer.transport.real

Config:

swiftmailer:
    transport:  "%mailer_transport%"
    host:       "%mailer_host%"
    username:   "%mailer_user%"
    password:   "%mailer_password%"
    port:       "%mailer_port%"
    encryption: "%mailer_encryption%"
    auth_mode:  login
    spool:     { type: memory }

When try use flush the queue after send, catch error User Error: Call to undefined method getSpool

...
$result = $this->mailer->send($message);

// now manually flush the queue
$spool = $this->mailer->getTransport()->getSpool();
$spool->flushQueue($this->_esmtpTransport);

If dump getMasterRequest() in generateAbsoluteUrl(), the request is null, and I get path without host

/**
     * Returns the absolute URL for the given absolute or relative path.
     *
     * This method returns the path unchanged if no request is available.
     *
     * @param string $path The path
     *
     * @return string The absolute URL
     *
     * @see Request::getUriForPath()
     */
    public function generateAbsoluteUrl($path)
    {
        if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
            return $path;
        }

       VarDumper::dump($this->requestStack->getMasterRequest());die;

        if (!$request = $this->requestStack->getMasterRequest()) {
            return $path;
        }



        if (!$path || '/' !== $path[0]) {
            $prefix = $request->getPathInfo();
            $last = strlen($prefix) - 1;
            if ($last !== $pos = strrpos($prefix, '/')) {
                $prefix = substr($prefix, 0, $pos).'/';
            }

            return $request->getUriForPath($prefix.$path);
        }

        return $request->getSchemeAndHttpHost().$path;
    }
@acasademont

This comment has been minimized.

Show comment
Hide comment
@12th

This comment has been minimized.

Show comment
Hide comment
@12th

12th Oct 25, 2015

And whether it is possible to dynamically change depending on the sender's e-mail settings?

12th commented Oct 25, 2015

And whether it is possible to dynamically change depending on the sender's e-mail settings?

@acasademont

This comment has been minimized.

Show comment
Hide comment
@acasademont

acasademont Oct 25, 2015

Contributor

mmm you would need to create a "package" for each of them and have different url's in there

http://symfony.com/doc/current/reference/configuration/framework.html#packages

Contributor

acasademont commented Oct 25, 2015

mmm you would need to create a "package" for each of them and have different url's in there

http://symfony.com/doc/current/reference/configuration/framework.html#packages

@12th

This comment has been minimized.

Show comment
Hide comment
@12th

12th Oct 25, 2015

Alternatively I try, but it's not the best option because the sender to the individual letters is different and is selected from the panel

12th commented Oct 25, 2015

Alternatively I try, but it's not the best option because the sender to the individual letters is different and is selected from the panel

@12th

This comment has been minimized.

Show comment
Hide comment
@12th

12th Oct 25, 2015

If set host in package base_path

framework:
    assets:
        version_format: %%s?%%s
        packages:
            img_email:
                version: 15.09.22
                base_path: http://site.com/email/

template lokk like:

<body style="margin:0; padding: 0; background:#2e2b2a url({{ absolute_url(asset('main/bg.png', 'img_email'))  }}) top center repeat;">

the html look like

<body style=3D"margin:0; padding: 0; background:#2e2b2a url(/http://site.com/email/main/bg.png) top center repeat;">

with slash at the beginning of the address

12th commented Oct 25, 2015

If set host in package base_path

framework:
    assets:
        version_format: %%s?%%s
        packages:
            img_email:
                version: 15.09.22
                base_path: http://site.com/email/

template lokk like:

<body style="margin:0; padding: 0; background:#2e2b2a url({{ absolute_url(asset('main/bg.png', 'img_email'))  }}) top center repeat;">

the html look like

<body style=3D"margin:0; padding: 0; background:#2e2b2a url(/http://site.com/email/main/bg.png) top center repeat;">

with slash at the beginning of the address

@12th

This comment has been minimized.

Show comment
Hide comment
@12th

12th Oct 25, 2015

as a solution to use

 // fix absolute url to image, because absolute_url() not work from CLI 
 $htmlBody = str_replace('%site_http_host%', 'http://'.$mailer['site_http_host'], $htmlBody);

12th commented Oct 25, 2015

as a solution to use

 // fix absolute url to image, because absolute_url() not work from CLI 
 $htmlBody = str_replace('%site_http_host%', 'http://'.$mailer['site_http_host'], $htmlBody);
@12th

This comment has been minimized.

Show comment
Hide comment

12th commented Oct 25, 2015

Solution there symfony/symfony#15448

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment