Skip to content

Commit

Permalink
feature #4987 Added the documentation for the new Asset component (ja…
Browse files Browse the repository at this point in the history
…viereguiluz)

This PR was submitted for the master branch but it was merged into the 2.7 branch instead (closes #4987).

Discussion
----------

Added the documentation for the new Asset component

| Q             | A
| ------------- | ---
| Doc fix?      | no
| New docs?     | yes
| Applies to    | 2.7+
| Fixed tickets | #4982

This PR only adds the "Symfony-agnostic" documentation. After this doc is merged, we have to update the rest of the Symfony documentation with the following changes:

-----

**1. Changes in configuration**

```yaml
# OLD configuration
framework:
    templating:
        assets_version: 'v5'
        assets_version_format: '%%s?version=%%s'
        assets_base_urls:
            http: ['http://cdn.example.com']
            ssl:  ['https://secure.example.com']
        packages:
            # ...

# NEW configuration
framework:
    assets:
        version: 'v5'
        version_format: '%%s?version=%%s'
        base_path: ~
        base_urls: ['http://cdn.example.com', 'https://secure.example.com']
        packages:
            # ...
```

**2. Changes in Twig templates**

`{{ asset() }}` function is the same as before, but now it only allows to set two arguments:

```twig
{# Common case: no package name #}
{{ asset('logo.png') }}

{# When using packages #}
{{ asset('logo.png', 'images') }}
```

This means that `absolute` and `version` arguments are no longer available:

```twig
{# BEFORE #}
{{ asset('logo.png', absolute = true) }}

{# AFTER #}
{{ absolute_url(asset('logo.png')) }}

{# BEFORE #}
{{ asset('logo.png', version = 'v5') }}

{# AFTER #}
{# relative URLs - do nothing (version is automatically appended) #}
{{ asset('logo.png') }}

{# absolute URLs - get the version with asset_version() and append it manually #}
{{ absolute_url(asset('logo.png')) ~ '?' ~ asset_version('logo.png') }}
```

**3. Other changes**

  * The `asset*()` functions are now defined in the Twig Bridge instead of the Twig Bundle.
  * ...

-----

Lastly, I have a question when using Asset component in a Symfony application: How can I use a custom version strategy in a Symfony application?

Commits
-------

7ab379a Lots of fixes and rewordings
9e8de96 Fixed RST syntax issues
5ca85d1 Fixed some RST syntax issues
17269a3 Moved the component documentation to its own folder
fab3c89 Fixed a lot of errors and added an introduction
49548b3 Tweaked documentation and added links to API methods/classes
76b8bea Added a missing link reference
d39b26e Minor rewording
6d367b1 Added the documentation for the new Asset component
  • Loading branch information
weaverryan committed Mar 14, 2015
2 parents 78733c3 + 7ab379a commit 22eee86
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 0 deletions.
7 changes: 7 additions & 0 deletions components/asset/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Asset
=====

.. toctree::
:maxdepth: 2

introduction
289 changes: 289 additions & 0 deletions components/asset/introduction.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
.. index::
single: Asset
single: Components; Asset

The Asset Component
===================

The Asset component manages URL generation and versioning of web assets such
as CSS stylesheets, JavaScript files and image files.

In the past, it was common for web applications to hardcode URLs of web assets.
For example:

.. code-block:: html

<link rel="stylesheet" type="text/css" href="/css/main.css">

<!-- ... -->

<a href="/"><img src="/images/logo.png"></a>

This practice is no longer recommended unless the web application is extremely
simple. Hardcoding URLs can be a disadvantage because:

* **Templates get verbose**: you have to write the full path for each
asset. When using the Asset component, you can group assets in packages to
avoid repeating the common part of their path;
* **Versioning is difficult**: it has to be custom managed for each
application. Adding a version to the asset URLs is essential for some applications
because it allows to control how the assets are cached. The Asset component
allows to define different versioning strategies for each package;
* **Moving assets location** is cumbersome and error-prone: it requires you to
carefully update the URLs of all assets included in all templates. The Asset
component allows to move assets effortlessly just by changing the base path
value associated with the package of assets;
* **It's nearly impossible to use multiple CDNs**: this technique requires to
change the URL of the asset randomly for each request. The Asset component
provides out-of-the-box support for any number of multiple CDNs, both regular
(``http://``) and secure (``https://``).

Installation
------------

You can install the component in two different ways:

* :doc:`Install it via Composer </components/using_components>` (``symfony/asset`` on `Packagist`_);
* Use the official Git repository (https://github.com/symfony/Asset).

Usage
-----

Asset Packages
~~~~~~~~~~~~~~

The Asset component manages assets through packages. A package groups all the
assets which share the same properties: versioning strategy, base path, CDN hosts,
etc. In the following basic example, a package is created to manage assets without
any versioning::

use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy;

$package = new Package(new EmptyVersionStrategy());

echo $package->getUrl('/image.png');
// result: /image.png

Packages implement the :class:`Symfony\\Component\\Asset\\PackageInterface`,
which defines the following two methods:

:method:`Symfony\\Component\\Asset\\PackageInterface::getVersion`
Returns the asset version for an asset.

:method:`Symfony\\Component\\Asset\\PackageInterface::getUrl`
Returns an absolute or root-relative public path.

Versioned Assets
~~~~~~~~~~~~~~~~

One of the main features of the Asset component is to manage the versioning of
the application's assets. Asset versions are commonly used to control how these
assets are cached.

Instead of relying on a simple version mechanism, the Asset component allows to
define advanced versioning strategies via PHP classes. The two built-in strategies
provided by the component are :class:`Symfony\\Component\\Asset\\VersionStrategy\\EmptyVersionStrategy`,
which doesn't add any version to the asset and :class:`Symfony\\Component\\Asset\\VersionStrategy\\StaticVersionStrategy`,
which allows to set the version with a format string.

In this example, the ``StaticVersionStrategy`` is used to append the ``v1``
suffix to any asset path::

use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy;

$package = new Package(new StaticVersionStrategy('v1'));

echo $package->getUrl('/image.png');
// result: /image.png?v1

In case you want to modify the version format, pass a sprintf-compatible format
string as the second argument of the ``StaticVersionStrategy`` constructor::

// put the 'version' word before the version value
$package = new Package(new StaticVersionStrategy('v1', '%s?version=%s'));

echo $package->getUrl('/image.png');
// result: /image.png?version=v1

// put the asset version before its path
$package = new Package(new StaticVersionStrategy('v1', '%2$s/%1$s'));

echo $package->getUrl('/image.png');
// result: /v1/image.png

Custom Version Strategies
.........................

Use the :class:`Symfony\\Component\\Asset\\VersionStrategy\\VersionStrategyInterface`
to define your own versioning strategy. For example, your application may need
to append the current date to all its web assets in order to bust the cache
every day::

use Symfony\Component\Asset\VersionStrategy\VersionStrategyInterface;

class DateVersionStrategy implements VersionStrategyInterface
{
private $version;

public function __construct()
{
$this->version = date('Ymd');
}

public function getVersion($path)
{
return $this->version;
}

public function applyVersion($path)
{
return sprintf('%s?v=%s', $path, $this->getVersion($path));
}
}

Grouped Assets
~~~~~~~~~~~~~~

It's common for applications to store their assets in a common path. If that's
your case, replace the default :class:`Symfony\\Component\\Asset\\Package` class
by :class:`Symfony\\Component\\Asset\\PathPackage` to avoid repeating the same
path time and again::

use Symfony\Component\Asset\PathPackage;
// ...

$package = new PathPackage('/static/images', new StaticVersionStrategy('v1'));

echo $package->getUrl('/logo.png');
// result: /static/images/logo.png?v1

Request Context Aware Assets
............................

If you are also using the HttpFoundation component in your project, for example
in a Symfony application, the ``PathPackage`` class can take into account the
context of the current request::

use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\Context\RequestStackContext;
// ...

$package = new PathPackage(
'/static/images',
new StaticVersionStrategy('v1'),
new RequestStackContext($requestStack)
);

echo $package->getUrl('/logo.png');
// result: /somewhere/static/images/logo.png?v1

When the request context is set (via an optional third argument), in addition to
the configured base path, ``PathPackage`` also prepends the current request base
URL (``/somewhere/`` in this example) to assets. This allows your website to be
hosted anywhere under the web server root directory.

Absolute Assets and CDNs
~~~~~~~~~~~~~~~~~~~~~~~~

Applications that host their assets on different domains and CDNs (*Content
Delivery Networks*) should use the :class:`Symfony\\Component\\Asset\\UrlPackage`
class to generate absolute URLs for their assets::

use Symfony\Component\Asset\UrlPackage;
// ...

$package = new UrlPackage(
'http://static.example.com/images/',
new StaticVersionStrategy('v1')
);

echo $package->getUrl('/logo.png');
// result: http://static.example.com/images/logo.png?v1

In case you serve assets from more than one domain to improve application
performance, pass an array of URLs as the first argument of ``UrlPackage``
constructor::

use Symfony\Component\Asset\UrlPackage;
// ...

$urls = array(
'http://static1.example.com/images/',
'http://static2.example.com/images/',
);
$package = new UrlPackage($urls, new StaticVersionStrategy('v1'));

echo $package->getUrl('/logo.png');
// result: http://static1.example.com/images/logo.png?v1

The selection of the domain which will serve the asset is deterministic, meaning
that each asset will be always served by the same domain. This behavior simplifies
the management of HTTP cache.

Request Context Aware Assets
............................

Similarly to application-relative assets, absolute assets can also take into
account the context of the current request. In this case, only the request
scheme is considered, in order to select the appropriate base URL (HTTPs or
protocol-relative URLs for HTTPs requests, any base URL for HTTP requests)::

use Symfony\Component\Asset\UrlPackage;
use Symfony\Component\Asset\Context\RequestStackContext;
// ...

$package = new UrlPackage(
array('http://example.com/', 'https://example.com/'),
new StaticVersionStrategy('v1'),
new RequestStackContext($requestStack)
);

echo $package->getUrl('/logo.png');
// result: https://example.com/logo.png?v1

Named Packages
~~~~~~~~~~~~~~

Applications that manage lots of different assets may need to group them in
packages with the same versioning strategy and base path. The Asset component
includes a :class:`Symfony\\Component\\Asset\\Packages` class to simplify
management of several packages.

In the following example, all packages use the same versioning strategy, but
they all have different base paths::

use Symfony\Component\Asset\Package;
use Symfony\Component\Asset\PathPackage;
use Symfony\Component\Asset\UrlPackage;
use Symfony\Component\Asset\Packages;
// ...

$versionStrategy = new StaticVersionStrategy('v1');

$defaultPackage = new Package($versionStrategy);

$namedPackages = array(
'img' => new UrlPackage('http://img.example.com/', $versionStrategy),
'doc' => new PathPackage('/somewhere/deep/for/documents', $versionStrategy),
);

$packages = new Packages($defaultPackage, $namedPackages)

The ``Packages`` class allows to define a default package, which will be applied
to assets that don't define the name of package to use. In addition, this
application defines a package named ``img`` to serve images from an external
domain and a ``doc`` package to avoid repeating long paths when linking to a
document inside a template::

echo $packages->getUrl('/main.css');
// result: /main.css?v1

echo $packages->getUrl('/logo.png', 'img');
// result: http://img.example.com/logo.png?v1

echo $packages->getUrl('/resume.pdf', 'doc');
// result: /somewhere/deep/for/documents/resume.pdf?v1

.. _Packagist: https://packagist.org/packages/symfony/asset
1 change: 1 addition & 0 deletions components/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The Components
:hidden:

using_components
asset/index
class_loader/index
config/index
console/index
Expand Down
4 changes: 4 additions & 0 deletions components/map.rst.inc
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
* :doc:`/components/using_components`

* :doc:`/components/asset/index`

* :doc:`/components/asset/introduction`

* :doc:`/components/class_loader/index`

* :doc:`/components/class_loader/introduction`
Expand Down

0 comments on commit 22eee86

Please sign in to comment.