diff --git a/doc/book/helpers/asset.md b/doc/book/helpers/asset.md new file mode 100644 index 00000000..7a7dc8e4 --- /dev/null +++ b/doc/book/helpers/asset.md @@ -0,0 +1,74 @@ +# Asset + +The `Asset` helper is used to translate asset names. +It could be used to prevent browser caching for assets. + +## Configuration and Basic Usage + +`Zend\View\Helper\Service\AssetFactory` checks the application +configuration, making it possible to set up the resource map through +your `module.config.php`. The next example will set up `Asset` helper: + +```php +'view_helper_config' => [ + 'asset' => [ + 'resource_map' => [ + 'css/style.css' => 'css/style-3a97ff4ee3.css', + 'js/vendor.js' => 'js/vendor-a507086eba.js', + ], + ], +], +``` + +Then in your view you can use: + +```php +// Usable in any of your .phtml files: +echo $this->asset('css/style.css'); +``` + +and you would receive following output: + +```html +css/style-3a97ff4ee3.css +``` + +The first argument of the `asset` helper is the regular asset name, +which will be replaced by versioned asset name defined in `resource_map` +of the configuration. + +> ### Note +> +> When `asset` key is defined but `resource_map` is not provided or is not +> an array exception `Zend\View\Exception\RuntimeException` will be +thrown. +> +> When you call `asset` helper with parameter which is not defined on your +> `resource_map` exception `Zend\View\Exception\InvalidArgumentException` +> will be thrown. + +## Resource map in JSON file + +If you have JSON file with resource map, for example +`rev-manifest.json`: + +```javascript +{ + "css/style.css": "css/style-3a97ff4ee3.css", + "js/vendor.js": "js/vendor-a507086eba.js" +} +``` + +then you can have in your configuration: + +```php +'view_helper_config' => [ + 'asset' => [ + 'resource_map' => json_decode(file_get_contents('/path/to/rev-manifest.json'), true), + ], +], +``` + +and when you have enabled cache config this file will be also cached in +compiled configuration cache, so it prevents reading the file on each +page load. diff --git a/doc/book/helpers/intro.md b/doc/book/helpers/intro.md index 34f10de8..35ef7228 100644 --- a/doc/book/helpers/intro.md +++ b/doc/book/helpers/intro.md @@ -55,6 +55,7 @@ variables. Additionally, there are a rich set of helpers for providing values for, and rendering, the various HTML `<head>` tags, such as `HeadTitle`, `HeadLink`, and `HeadScript`. The currently shipped helpers include: +- [Asset](asset.md) - [BasePath](base-path.md) - [Cycle](cycle.md) - [Doctype](doctype.md) diff --git a/mkdocs.yml b/mkdocs.yml index 0163b550..ad6ac9c5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,6 +9,7 @@ pages: - "The ViewEvent": view-event.md - Helpers: - Intro: helpers/intro.md + - Asset: helpers/asset.md - BasePath: helpers/base-path.md - Cycle: helpers/cycle.md - Doctype: helpers/doctype.md diff --git a/src/Helper/Asset.php b/src/Helper/Asset.php new file mode 100644 index 00000000..d7342822 --- /dev/null +++ b/src/Helper/Asset.php @@ -0,0 +1,54 @@ +<?php +/** + * @see https://github.com/zendframework/zend-view for the canonical source repository + * @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com) + * @license https://github.com/zendframework/zend-view/blob/master/LICENSE.md New BSD License + */ + +namespace Zend\View\Helper; + +use Zend\View\Exception; + +/** + * View helper plugin to fetch asset from resource map. + */ +class Asset extends AbstractHelper +{ + /** + * @var array + */ + protected $resourceMap = []; + + /** + * @param string $asset + * @return string + * @throws Exception\InvalidArgumentException + */ + public function __invoke($asset) + { + if (! array_key_exists($asset, $this->resourceMap)) { + throw new Exception\InvalidArgumentException('Asset is not defined.'); + } + + return $this->resourceMap[$asset]; + } + + /** + * @param array $resourceMap + * @return $this + */ + public function setResourceMap(array $resourceMap) + { + $this->resourceMap = $resourceMap; + + return $this; + } + + /** + * @return array + */ + public function getResourceMap() + { + return $this->resourceMap; + } +} diff --git a/src/Helper/Service/AssetFactory.php b/src/Helper/Service/AssetFactory.php new file mode 100644 index 00000000..2f06c50c --- /dev/null +++ b/src/Helper/Service/AssetFactory.php @@ -0,0 +1,60 @@ +<?php +/** + * @see https://github.com/zendframework/zend-view for the canonical source repository + * @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com) + * @license https://github.com/zendframework/zend-view/blob/master/LICENSE.md New BSD License + */ + +namespace Zend\View\Helper\Service; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\View\Exception; +use Zend\View\Helper\Asset; + +class AssetFactory implements FactoryInterface +{ + /** + * {@inheritDoc} + * + * @param ContainerInterface $container + * @param string $name + * @param null|array $options + * @return Asset + * @throws Exception\RuntimeException + */ + public function __invoke(ContainerInterface $container, $name, array $options = null) + { + // test if we are using Zend\ServiceManager v2 or v3 + if (! method_exists($container, 'configure')) { + $container = $container->getServiceLocator(); + } + $helper = new Asset(); + + $config = $container->get('config'); + if (isset($config['view_helper_config']['asset'])) { + $configHelper = $config['view_helper_config']['asset']; + if (isset($configHelper['resource_map']) && is_array($configHelper['resource_map'])) { + $helper->setResourceMap($configHelper['resource_map']); + } else { + throw new Exception\RuntimeException('Invalid resource map configuration.'); + } + } + + return $helper; + } + + /** + * Create service + * + * @param ServiceLocatorInterface $serviceLocator + * @param string|null $rName + * @param string|null $cName + * @return Asset + */ + public function createService(ServiceLocatorInterface $serviceLocator, $rName = null, $cName = null) + { + return $this($serviceLocator, $cName); + } +} diff --git a/src/HelperPluginManager.php b/src/HelperPluginManager.php index 652980be..6a07792b 100644 --- a/src/HelperPluginManager.php +++ b/src/HelperPluginManager.php @@ -37,6 +37,8 @@ class HelperPluginManager extends AbstractPluginManager * @var string[] */ protected $aliases = [ + 'asset' => Helper\Asset::class, + 'Asset' => Helper\Asset::class, 'basePath' => Helper\BasePath::class, 'BasePath' => Helper\BasePath::class, 'basepath' => Helper\BasePath::class, @@ -148,6 +150,7 @@ class HelperPluginManager extends AbstractPluginManager * @var array */ protected $factories = [ + Helper\Asset::class => Helper\Service\AssetFactory::class, Helper\FlashMessenger::class => Helper\Service\FlashMessengerFactory::class, Helper\Identity::class => Helper\Service\IdentityFactory::class, Helper\BasePath::class => InvokableFactory::class, @@ -186,6 +189,7 @@ class HelperPluginManager extends AbstractPluginManager // v2 canonical FQCNs + 'zendviewhelperasset' => Helper\Service\AssetFactory::class, 'zendviewhelperflashmessenger' => Helper\Service\FlashMessengerFactory::class, 'zendviewhelperidentity' => Helper\Service\IdentityFactory::class, 'zendviewhelperbasepath' => InvokableFactory::class, diff --git a/src/Renderer/PhpRenderer.php b/src/Renderer/PhpRenderer.php index aac1fcd9..fd6a76c3 100644 --- a/src/Renderer/PhpRenderer.php +++ b/src/Renderer/PhpRenderer.php @@ -31,6 +31,7 @@ * * Convenience methods for build in helpers (@see __call): * + * @method string asset($asset) * @method string|null basePath($file = null) * @method \Zend\View\Helper\Cycle cycle(array $data = array(), $name = \Zend\View\Helper\Cycle::DEFAULT_NAME) * @method \Zend\View\Helper\DeclareVars declareVars() diff --git a/test/Helper/AssetTest.php b/test/Helper/AssetTest.php new file mode 100644 index 00000000..bb5236f3 --- /dev/null +++ b/test/Helper/AssetTest.php @@ -0,0 +1,87 @@ +<?php +/** + * @see https://github.com/zendframework/zend-view for the canonical source repository + * @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com) + * @license https://github.com/zendframework/zend-view/blob/master/LICENSE.md New BSD License + */ + +namespace ZendTest\View\Helper; + +use PHPUnit_Framework_TestCase as TestCase; +use Zend\ServiceManager\ServiceManager; +use Zend\View\Exception; +use Zend\View\Helper\Asset; +use Zend\View\HelperPluginManager; + +class AssetTest extends TestCase +{ + /** @var array */ + protected $resourceMap = [ + 'css/style.css' => 'css/style-3a97ff4ee3.css', + 'js/vendor.js' => 'js/vendor-a507086eba.js', + ]; + + /** @var Asset */ + protected $asset; + + protected function setUp() + { + parent::setUp(); + + $this->asset = new Asset(); + $this->asset->setResourceMap($this->resourceMap); + } + + public function testHelperPluginManagerReturnsAssetHelper() + { + $helpers = $this->getHelperPluginManager(); + $asset = $helpers->get('asset'); + + $this->assertInstanceOf(Asset::class, $asset); + } + + public function testHelperPluginManagerReturnsAssetHelperByClassName() + { + $helpers = $this->getHelperPluginManager(); + $asset = $helpers->get(Asset::class); + + $this->assertInstanceOf(Asset::class, $asset); + } + + public function testInvalidAssetName() + { + $this->setExpectedException(Exception\InvalidArgumentException::class, 'Asset is not defined.'); + + $this->asset->__invoke('unknown'); + } + + /** + * @dataProvider assets + * + * @param string $name + * @param string $expected + */ + public function testInvokeResult($name, $expected) + { + $result = $this->asset->__invoke($name); + + $this->assertEquals($expected, $result); + } + + public function assets() + { + $data = []; + foreach ($this->resourceMap as $key => $value) { + $data[] = [$key, $value]; + } + return $data; + } + + protected function getHelperPluginManager(array $config = []) + { + $services = $this->prophesize(ServiceManager::class); + $services->get('config')->willReturn($config); + + return new HelperPluginManager($services->reveal()); + } +} diff --git a/test/Helper/Service/AssetFactoryTest.php b/test/Helper/Service/AssetFactoryTest.php new file mode 100644 index 00000000..e983666f --- /dev/null +++ b/test/Helper/Service/AssetFactoryTest.php @@ -0,0 +1,89 @@ +<?php +/** + * @see https://github.com/zendframework/zend-view for the canonical source repository + * @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com) + * @license https://github.com/zendframework/zend-view/blob/master/LICENSE.md New BSD License + */ + +namespace ZendTest\View\Helper\Service; + +use PHPUnit_Framework_TestCase as TestCase; +use Zend\ServiceManager\ServiceManager; +use Zend\View\Exception; +use Zend\View\Helper\Asset; +use Zend\View\Helper\Service\AssetFactory; +use Zend\View\HelperPluginManager; + +class AssetFactoryTest extends TestCase +{ + public function testAssetFactoryCreateServiceCreatesAssetInstance() + { + $services = $this->getServices(); + + $assetFactory = new AssetFactory(); + $asset = $assetFactory->createService($services); + + $this->assertInstanceOf(Asset::class, $asset); + } + + public function testAssetFactoryInvokableCreatesAssetInstance() + { + $services = $this->getServices(); + + $assetFactory = new AssetFactory(); + $asset = $assetFactory($services, ''); + + $this->assertInstanceOf(Asset::class, $asset); + } + + public function testValidConfiguration() + { + $config = [ + 'view_helper_config' => [ + 'asset' => [ + 'resource_map' => [ + 'css/style.css' => 'css/style-3a97ff4ee3.css', + 'js/vendor.js' => 'js/vendor-a507086eba.js', + ], + ], + ], + ]; + + $services = $this->getServices($config); + $assetFactory = new AssetFactory(); + + $asset = $assetFactory($services, ''); + + $this->assertEquals($config['view_helper_config']['asset']['resource_map'], $asset->getResourceMap()); + } + + public function testInvalidConfiguration() + { + $config = [ + 'view_helper_config' => [ + 'asset' => [], + ], + ]; + $services = $this->getServices($config); + + $assetFactory = new AssetFactory(); + + $this->setExpectedException(Exception\RuntimeException::class, 'Invalid resource map configuration.'); + $assetFactory($services, ''); + } + + protected function getServices(array $config = []) + { + $services = $this->prophesize(ServiceManager::class); + $services->get('config')->willReturn($config); + + $helpers = new HelperPluginManager($services->reveal()); + + // test if we are using Zend\ServiceManager v3 + if (method_exists($helpers, 'configure')) { + return $services->reveal(); + } + + return $helpers; + } +}