Skip to content

Commit 48c9985

Browse files
committed
feature #10451 [TwigBundle] Add possibility to generate absolute assets urls (romainneutron)
This PR was merged into the 2.5-dev branch. Discussion ---------- [TwigBundle] Add possibility to generate absolute assets urls | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | License | MIT | Doc PR | symfony/symfony-docs#3683 This is another approach of #7722 - [x] Add unit tests - [x] Update doc Commits ------- 76b8851 [TwigBundle] Add possibility to generate absolute assets urls
2 parents a635c4f + 76b8851 commit 48c9985

File tree

4 files changed

+154
-6
lines changed

4 files changed

+154
-6
lines changed

src/Symfony/Bundle/TwigBundle/Extension/AssetsExtension.php

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bundle\TwigBundle\Extension;
1313

1414
use Symfony\Component\DependencyInjection\ContainerInterface;
15+
use Symfony\Component\Routing\RequestContext;
1516

1617
/**
1718
* Twig extension for Symfony assets helper
@@ -21,10 +22,12 @@
2122
class AssetsExtension extends \Twig_Extension
2223
{
2324
private $container;
25+
private $context;
2426

25-
public function __construct(ContainerInterface $container)
27+
public function __construct(ContainerInterface $container, RequestContext $requestContext)
2628
{
2729
$this->container = $container;
30+
$this->context = $requestContext;
2831
}
2932

3033
/**
@@ -45,14 +48,21 @@ public function getFunctions()
4548
*
4649
* Absolute paths (i.e. http://...) are returned unmodified.
4750
*
48-
* @param string $path A public path
49-
* @param string $packageName The name of the asset package to use
51+
* @param string $path A public path
52+
* @param string $packageName The name of the asset package to use
53+
* @param Boolean $absolute Whether to return an absolute URL or a relative one
5054
*
5155
* @return string A public path which takes into account the base path and URL path
5256
*/
53-
public function getAssetUrl($path, $packageName = null)
57+
public function getAssetUrl($path, $packageName = null, $absolute = false)
5458
{
55-
return $this->container->get('templating.helper.assets')->getUrl($path, $packageName);
59+
$url = $this->container->get('templating.helper.assets')->getUrl($path, $packageName);
60+
61+
if (!$absolute) {
62+
return $url;
63+
}
64+
65+
return $this->ensureUrlIsAbsolute($url);
5666
}
5767

5868
/**
@@ -76,4 +86,33 @@ public function getName()
7686
{
7787
return 'assets';
7888
}
89+
90+
/**
91+
* Ensures an URL is absolute, if possible.
92+
*
93+
* @param string $url The URL that has to be absolute
94+
*
95+
* @return string The absolute URL
96+
*/
97+
private function ensureUrlIsAbsolute($url)
98+
{
99+
if (false !== strpos($url, '://') || 0 === strpos($url, '//')) {
100+
return $url;
101+
}
102+
103+
if ('' === $host = $this->context->getHost()) {
104+
return $url;
105+
}
106+
107+
$scheme = $this->context->getScheme();
108+
$port = '';
109+
110+
if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
111+
$port = ':'.$this->context->getHttpPort();
112+
} elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
113+
$port = ':'.$this->context->getHttpsPort();
114+
}
115+
116+
return $scheme.'://'.$host.$port.$url;
117+
}
79118
}

src/Symfony/Bundle/TwigBundle/Resources/config/twig.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<service id="twig.extension.assets" class="%twig.extension.assets.class%" public="false">
6767
<tag name="twig.extension" />
6868
<argument type="service" id="service_container" />
69+
<argument type="service" id="router.request_context" />
6970
</service>
7071

7172
<service id="twig.extension.actions" class="%twig.extension.actions.class%" public="false">
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\TwigBundle\Tests\Extension;
13+
14+
use Symfony\Bundle\TwigBundle\Extension\AssetsExtension;
15+
use Symfony\Bundle\TwigBundle\Tests\TestCase;
16+
use Symfony\Component\Routing\RequestContext;
17+
18+
class AssetsExtensionTest extends TestCase
19+
{
20+
/**
21+
* @dataProvider provideGetGetAssetUrlArguments
22+
*/
23+
public function testGetAssetUrl($path, $packageName, $absolute, $relativeUrl, $expectedUrl, $scheme, $host, $httpPort, $httpsPort)
24+
{
25+
$helper = $this->createHelperMock($path, $packageName, $relativeUrl);
26+
$container = $this->createContainerMock($helper);
27+
28+
$context = $this->createRequestContextMock($scheme, $host, $httpPort, $httpsPort);
29+
30+
$extension = new AssetsExtension($container, $context);
31+
$this->assertEquals($expectedUrl, $extension->getAssetUrl($path, $packageName, $absolute));
32+
}
33+
34+
public function testGetAssetWithtoutHost()
35+
{
36+
$path = '/path/to/asset';
37+
$packageName = null;
38+
$relativeUrl = '/bundle-name/path/to/asset';
39+
40+
$helper = $this->createHelperMock($path, $packageName, $relativeUrl);
41+
$container = $this->createContainerMock($helper);
42+
43+
$context = $this->createRequestContextMock('http', '', 80, 443);
44+
45+
$extension = new AssetsExtension($container, $context);
46+
$this->assertEquals($relativeUrl, $extension->getAssetUrl($path, $packageName, true));
47+
}
48+
49+
public function provideGetGetAssetUrlArguments()
50+
{
51+
return array(
52+
array('/path/to/asset', 'package-name', false, '/bundle-name/path/to/asset', '/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
53+
array('/path/to/asset', 'package-name', false, 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
54+
array('/path/to/asset', null, false, '/bundle-name/path/to/asset', '/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
55+
array('/path/to/asset', 'package-name', true, '/bundle-name/path/to/asset', 'http://symfony.com/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
56+
array('/path/to/asset', 'package-name', true, 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http://subdomain.symfony.com/bundle-name/path/to/asset', 'http', 'symfony.com', 80, null),
57+
array('/path/to/asset', null, true, '/bundle-name/path/to/asset', 'https://symfony.com:92/bundle-name/path/to/asset', 'https', 'symfony.com', null, 92),
58+
array('/path/to/asset', null, true, '/bundle-name/path/to/asset', 'http://symfony.com:660/bundle-name/path/to/asset', 'http', 'symfony.com', 660, null),
59+
);
60+
}
61+
62+
private function createRequestContextMock($scheme, $host, $httpPort, $httpsPort)
63+
{
64+
$context = $this->getMockBuilder('Symfony\Component\Routing\RequestContext')
65+
->disableOriginalConstructor()
66+
->getMock();
67+
$context->expects($this->any())
68+
->method('getScheme')
69+
->will($this->returnValue($scheme));
70+
$context->expects($this->any())
71+
->method('getHost')
72+
->will($this->returnValue($host));
73+
$context->expects($this->any())
74+
->method('getHttpPort')
75+
->will($this->returnValue($httpPort));
76+
$context->expects($this->any())
77+
->method('getHttpsPort')
78+
->will($this->returnValue($httpsPort));
79+
80+
return $context;
81+
}
82+
83+
private function createContainerMock($helper)
84+
{
85+
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
86+
$container->expects($this->any())
87+
->method('get')
88+
->with('templating.helper.assets')
89+
->will($this->returnValue($helper));
90+
91+
return $container;
92+
}
93+
94+
private function createHelperMock($path, $packageName, $returnValue)
95+
{
96+
$helper = $this->getMockBuilder('Symfony\Component\Templating\Helper\CoreAssetsHelper')
97+
->disableOriginalConstructor()
98+
->getMock();
99+
$helper->expects($this->any())
100+
->method('getUrl')
101+
->with($path, $packageName)
102+
->will($this->returnValue($returnValue));
103+
104+
return $helper;
105+
}
106+
}

src/Symfony/Bundle/TwigBundle/composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
"require-dev": {
2424
"symfony/stopwatch": "~2.2",
2525
"symfony/dependency-injection": "~2.0",
26-
"symfony/config": "~2.2"
26+
"symfony/config": "~2.2",
27+
"symfony/routing": "~2.1",
28+
"symfony/templating": "~2.1"
2729
},
2830
"autoload": {
2931
"psr-0": { "Symfony\\Bundle\\TwigBundle\\": "" }

0 commit comments

Comments
 (0)