Symfony 2中 CSS 文件中资产的路径

问题

我有一个 CSS文件,其中包含一些路径(图像、字体等等)。

我的路径结构是这样的:

...
+-src/
| +-MyCompany/
|   +-MyBundle/
|     +-Resources/
|       +-assets/
|         +-css/
|           +-stylesheets...
+-web/
| +-images/
|   +-images...
...

我想在样式表中引用我的图像。

第一个解决方案

我将 CSS 文件中的所有路径都更改为绝对路径。这不是解决方案,因为应用程序应该(也必须)这样做也在子目录中工作。

第二个解决方案

filter="cssrewrite"一起使用 Assetic。

因此,我将 CSS 文件中的所有路径都更改为

url("../../../../../../web/images/myimage.png")

表示从 resources 目录到 /web/images目录的实际路径。这不起作用,因为 cssrewrite 产生以下代码:

url("../../Resources/assets/")

这显然是错误的道路。

assetic:dump之后,这条路径被创建,但仍然是错误的:

url("../../../web/images/myimage.png")

资产阶级的细枝密码:

{% stylesheets
'@MyCompanyMyBundle/Resources/assets/css/*.css'
filter="cssrewrite"
%}
<link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}

当前(第三)解决方案

因为所有的 CSS 文件最终都在 /web/css/stylexyz.css中,所以我将 CSS 文件中的所有路径都更改为相对路径:

url("../images/myimage.png")

这个(糟糕的)解决方案可以工作,除了在 dev环境中: CSS 路径是 /app_dev.php/css/stylexyz.css,因此由此产生的图像路径是 /app_dev.php/images/myimage.png,这将产生一个 NotFoundHttpException

是否有更好和可行的解决方案?

109913 次浏览

The cssrewrite filter is not compatible with the @bundle notation for now. So you have two choices:

  • Reference the CSS files in the web folder (after: console assets:install --symlink web)

    {% stylesheets '/bundles/myCompany/css/*." filter="cssrewrite" %}
    
  • Use the cssembed filter to embed images in the CSS like this.

    {% stylesheets '@MyCompanyMyBundle/Resources/assets/css/*.css' filter="cssembed" %}
    

I have came across the very-very-same problem.

In short:

  • Willing to have original CSS in an "internal" dir (Resources/assets/css/a.css)
  • Willing to have the images in the "public" dir (Resources/public/images/devil.png)
  • Willing that twig takes that CSS, recompiles it into web/css/a.css and make it point the image in /web/bundles/mynicebundle/images/devil.png

I have made a test with ALL possible (sane) combinations of the following:

  • @notation, relative notation
  • Parse with cssrewrite, without it
  • CSS image background vs direct <img> tag src= to the very same image than CSS
  • CSS parsed with assetic and also without parsing with assetic direct output
  • And all this multiplied by trying a "public dir" (as Resources/public/css) with the CSS and a "private" directory (as Resources/assets/css).

This gave me a total of 14 combinations on the same twig, and this route was launched from

  • "/app_dev.php/"
  • "/app.php/"
  • and "/"

thus giving 14 x 3 = 42 tests.

Additionally, all this has been tested working in a subdirectory, so there is no way to fool by giving absolute URLs because they would simply not work.

The tests were two unnamed images and then divs named from 'a' to 'f' for the CSS built FROM the public folder and named 'g to 'l' for the ones built from the internal path.

I observed the following:

Only 3 of the 14 tests were shown adequately on the three URLs. And NONE was from the "internal" folder (Resources/assets). It was a pre-requisite to have the spare CSS PUBLIC and then build with assetic FROM there.

These are the results:

  1. Result launched with /app_dev.php/ Result launched with /app_dev.php/

  2. Result launched with /app.php/ Result launched with /app.php/

  3. Result launched with / enter image description here

So... ONLY - The second image - Div B - Div C are the allowed syntaxes.

Here there is the TWIG code:

<html>
<head>
{% stylesheets 'bundles/commondirty/css_original/container.css' filter="cssrewrite" %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{# First Row: ABCDEF #}


<link href="\{\{ '../bundles/commondirty/css_original/a.css' }}" rel="stylesheet" type="text/css" />
<link href="\{\{ asset( 'bundles/commondirty/css_original/b.css' ) }}" rel="stylesheet" type="text/css" />


{% stylesheets 'bundles/commondirty/css_original/c.css' filter="cssrewrite" %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{% stylesheets 'bundles/commondirty/css_original/d.css' %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{% stylesheets '@CommonDirtyBundle/Resources/public/css_original/e.css' filter="cssrewrite" %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{% stylesheets '@CommonDirtyBundle/Resources/public/css_original/f.css' %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{# First Row: GHIJKL #}


<link href="\{\{ '../../src/Common/DirtyBundle/Resources/assets/css/g.css' }}" rel="stylesheet" type="text/css" />
<link href="\{\{ asset( '../src/Common/DirtyBundle/Resources/assets/css/h.css' ) }}" rel="stylesheet" type="text/css" />


{% stylesheets '../src/Common/DirtyBundle/Resources/assets/css/i.css' filter="cssrewrite" %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{% stylesheets '../src/Common/DirtyBundle/Resources/assets/css/j.css' %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{% stylesheets '@CommonDirtyBundle/Resources/assets/css/k.css' filter="cssrewrite" %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


{% stylesheets '@CommonDirtyBundle/Resources/assets/css/l.css' %}
<link href="\{\{ asset_url }}" rel="stylesheet" type="text/css" />
{% endstylesheets %}


</head>
<body>
<div class="container">
<p>
<img alt="Devil" src="../bundles/commondirty/images/devil.png">
<img alt="Devil" src="\{\{ asset('bundles/commondirty/images/devil.png') }}">
</p>
<p>
<div class="a">
A
</div>
<div class="b">
B
</div>
<div class="c">
C
</div>
<div class="d">
D
</div>
<div class="e">
E
</div>
<div class="f">
F
</div>
</p>
<p>
<div class="g">
G
</div>
<div class="h">
H
</div>
<div class="i">
I
</div>
<div class="j">
J
</div>
<div class="k">
K
</div>
<div class="l">
L
</div>
</p>
</div>
</body>
</html>

The container.css:

div.container
{
border: 1px solid red;
padding: 0px;
}


div.container img, div.container div
{
border: 1px solid green;
padding: 5px;
margin: 5px;
width: 64px;
height: 64px;
display: inline-block;
vertical-align: top;
}

And a.css, b.css, c.css, etc: all identical, just changing the color and the CSS selector.

.a
{
background: red url('../images/devil.png');
}

The "directories" structure is:

Directories Directories

All this came, because I did not want the individual original files exposed to the public, specially if I wanted to play with "less" filter or "sass" or similar... I did not want my "originals" published, only the compiled one.

But there are good news. If you don't want to have the "spare CSS" in the public directories... install them not with --symlink, but really making a copy. Once "assetic" has built the compound CSS, and you can DELETE the original CSS from the filesystem, and leave the images:

Compilation process Compilation process

Note I do this for the --env=prod environment.

Just a few final thoughts:

  • This desired behaviour can be achieved by having the images in "public" directory in Git or Mercurial and the "css" in the "assets" directory. That is, instead of having them in "public" as shown in the directories, imagine a, b, c... residing in the "assets" instead of "public", than have your installer/deployer (probably a Bash script) to put the CSS temporarily inside the "public" dir before assets:install is executed, then assets:install, then assetic:dump, and then automating the removal of CSS from the public directory after assetic:dump has been executed. This would achive EXACTLY the behaviour desired in the question.

  • Another (unknown if possible) solution would be to explore if "assets:install" can only take "public" as the source or could also take "assets" as a source to publish. That would help when installed with the --symlink option when developing.

  • Additionally, if we are going to script the removal from the "public" dir, then, the need of storing them in a separate directory ("assets") disappears. They can live inside "public" in our version-control system as there will be dropped upon deploy to the public. This allows also for the --symlink usage.

BUT ANYWAY, CAUTION NOW: As now the originals are not there anymore (rm -Rf), there are only two solutions, not three. The working div "B" does not work anymore as it was an asset() call assuming there was the original asset. Only "C" (the compiled one) will work.

So... there is ONLY a FINAL WINNER: Div "C" allows EXACTLY what it was asked in the topic: To be compiled, respect the path to the images and do not expose the original source to the public.

The winner is C

The winner is C

I'll post what worked for me, thanks to @xavi-montero.

Put your CSS in your bundle's Resource/public/css directory, and your images in say Resource/public/img.

Change assetic paths to the form 'bundles/mybundle/css/*.css', in your layout.

In config.yml, add rule css_rewrite to assetic:

assetic:
filters:
cssrewrite:
apply_to: "\.css$"

Now install assets and compile with assetic:

$ rm -r app/cache/* # just in case
$ php app/console assets:install --symlink
$ php app/console assetic:dump --env=prod

This is good enough for the development box, and --symlink is useful, so you don't have to reinstall your assets (for example, you add a new image) when you enter through app_dev.php.

For the production server, I just removed the '--symlink' option (in my deployment script), and added this command at the end:

$ rm -r web/bundles/*/css web/bundles/*/js # all this is already compiled, we don't need the originals

All is done. With this, you can use paths like this in your .css files: ../img/picture.jpeg

If it can help someone, we have struggled a lot with Assetic, and we are now doing the following in development mode:

  • Set up like in Dumping Asset Files in the dev Environmen so in config_dev.yml, we have commented:

    #assetic:
    #    use_controller: true
    

    And in routing_dev.yml

    #_assetic:
    #    resource: .
    #    type:     assetic
    
  • Specify the URL as absolute from the web root. For example, background-image: url("/bundles/core/dynatree/skins/skin/vline.gif"); Note: our vhost web root is pointing on web/.

  • No usage of cssrewrite filter

I had the same problem and I just tried using the following as a workaround. Seems to work so far. You can even create a dummy template that just contains references to all those static assets.

{% stylesheets
output='assets/fonts/glyphicons-halflings-regular.ttf'
'bundles/bootstrap/fonts/glyphicons-halflings-regular.ttf'
%}{% endstylesheets %}

Notice the omission of any output which means nothing shows up on the template. When I run assetic:dump the files are copied over to the desired location and the css includes work as expected.

I offen manage css/js plugin with composer which install it under vendor. I symlink those to the web/bundles directory, that's let composer update bundles as needed.

exemple:

1 - symlink once at all (use command fromweb/bundles/

ln -sf vendor/select2/select2/dist/ select2

2 - use asset where needed, in twig template :

\{\{ asset('bundles/select2/css/fileinput.css) }}

Regards.