mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 06:15:19 +08:00
Fixes #4553: Smarty enhancements
This commit is contained in:
@ -106,9 +106,9 @@ Aliased class import:
|
||||
{{ use({'alias' => '/app/widgets/MyWidget'}) }}
|
||||
```
|
||||
|
||||
#### Referencing other views
|
||||
#### Referencing other templates
|
||||
|
||||
There are two ways of referencing views in `include` and `extends` statements:
|
||||
There are two ways of referencing templates in `include` and `extends` statements:
|
||||
|
||||
```
|
||||
{% include "comment.twig" %}
|
||||
@ -118,8 +118,8 @@ There are two ways of referencing views in `include` and `extends` statements:
|
||||
{% extends "@app/views/layouts/2columns.twig" %}
|
||||
```
|
||||
|
||||
In the first case the view will be searched relatively to the path current view is in. For `comment.twig` and `post.twig`
|
||||
that means these will be searched in the same directory as the view that's rendered currently.
|
||||
In the first case the view will be searched relatively to the current template path. For `comment.twig` and `post.twig`
|
||||
that means these will be searched in the same directory as the currently rendered template.
|
||||
|
||||
In the second case we're using path aliases. All the Yii aliases such as `@app` are available by default.
|
||||
|
||||
@ -264,8 +264,9 @@ Then in the template you can apply filter using the following syntax:
|
||||
Smarty
|
||||
------
|
||||
|
||||
To use Smarty, you need to create templates in files that have the `.tpl` extension (or use another file extension but configure the component accordingly). Unlike standard view files, when using Smarty you must include the extension in your `$this->render()`
|
||||
or `$this->renderPartial()` controller calls:
|
||||
To use Smarty, you need to create templates in files that have the `.tpl` extension (or use another file extension but
|
||||
configure the component accordingly). Unlike standard view files, when using Smarty you must include the extension in
|
||||
your `$this->render()` or `$this->renderPartial()` controller calls:
|
||||
|
||||
```php
|
||||
return $this->render('renderer.tpl', ['username' => 'Alex']);
|
||||
@ -277,20 +278,173 @@ The best resource to learn Smarty template syntax is its official documentation
|
||||
[www.smarty.net](http://www.smarty.net/docs/en/). Additionally there are Yii-specific syntax extensions
|
||||
described below.
|
||||
|
||||
#### Additional functions
|
||||
#### Setting object properties
|
||||
|
||||
Yii adds the following construct to the standard Smarty syntax:
|
||||
There's a special function called `set` that allows you to set common properties of the view and controller. Currently
|
||||
available properties are `title`, `theme` and `layout`:
|
||||
|
||||
```
|
||||
{set title="My Page"}
|
||||
{set theme="frontend"}
|
||||
{set layout="main.tpl"}
|
||||
```
|
||||
|
||||
For title there's dedicated block as well:
|
||||
|
||||
```
|
||||
{title}My Page{/title}
|
||||
```
|
||||
|
||||
#### Setting meta tags
|
||||
|
||||
Meta tags could be set like to following:
|
||||
|
||||
```
|
||||
{meta keywords="Yii,PHP,Smarty,framework"}
|
||||
```
|
||||
|
||||
There's also dedicated block for description:
|
||||
|
||||
```
|
||||
{description}This is my page about Smarty extension{/description}
|
||||
```
|
||||
|
||||
#### Calling object methods
|
||||
|
||||
Sometimes you need calling
|
||||
|
||||
#### Importing static classes, using widgets as functions and blocks
|
||||
|
||||
You can import additional static classes right in the template:
|
||||
|
||||
```
|
||||
{use class="yii\helpers\Html"}
|
||||
{Html::mailto('eugenia@example.com')}
|
||||
```
|
||||
|
||||
If you want you can set custom alias:
|
||||
|
||||
```
|
||||
{use class="yii\helpers\Html" as="Markup"}
|
||||
{Markup::mailto('eugenia@example.com')}
|
||||
```
|
||||
|
||||
Extension helps using widgets in convenient way converting their syntax to function calls or blocks. For regular widgets
|
||||
function could be used like the following:
|
||||
|
||||
```
|
||||
{use class='@yii\grid\GridView' type='function'}
|
||||
{GridView dataProvider=$provider}
|
||||
```
|
||||
|
||||
For widgets with `begin` and `end` methods such as ActiveForm it's better to use block:
|
||||
|
||||
```
|
||||
{use class='yii\widgets\ActiveForm' type='block'}
|
||||
{ActiveForm assign='form' id='login-form' action='/form-handler' options=['class' => 'form-horizontal']}
|
||||
{$form->field($model, 'firstName')}
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-1 col-lg-11">
|
||||
<input type="submit" value="Login" class="btn btn-primary" />
|
||||
</div>
|
||||
</div>
|
||||
{/ActiveForm}
|
||||
```
|
||||
|
||||
If you're using particular widget a lot, it is a good idea to declare it in application config and remove `{use class`
|
||||
call from templates:
|
||||
|
||||
```php
|
||||
'components' => [
|
||||
'view' => [
|
||||
// ...
|
||||
'renderers' => [
|
||||
'tpl' => [
|
||||
'class' => 'yii\smarty\ViewRenderer',
|
||||
'widgets' => [
|
||||
'blocks' => [
|
||||
'ActiveForm' => '\yii\widgets\ActiveForm',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
#### Referencing other templates
|
||||
|
||||
There are two main ways of referencing templates in `include` and `extends` statements:
|
||||
|
||||
```
|
||||
{include 'comment.tpl'}
|
||||
{extends 'post.tpl'}
|
||||
|
||||
{include '@app/views/snippets/avatar.tpl'}
|
||||
{extends '@app/views/layouts/2columns.tpl'}
|
||||
```
|
||||
|
||||
In the first case the view will be searched relatively to the current template path. For `comment.tpl` and `post.tpl`
|
||||
that means these will be searched in the same directory as the currently rendered template.
|
||||
|
||||
In the second case we're using path aliases. All the Yii aliases such as `@app` are available by default.
|
||||
|
||||
#### CSS, JavaScript and asset bundles
|
||||
|
||||
In order to register JavaScript and CSS files the following syntax could be used:
|
||||
|
||||
```
|
||||
{registerJsFile url='http://maps.google.com/maps/api/js?sensor=false' position='POS_END'}
|
||||
{registerCssFile url='@assets/css/normalizer.css'}
|
||||
```
|
||||
|
||||
If you need JavaScript and CSS directly in the template there are convenient blocks:
|
||||
|
||||
```
|
||||
{registerJs key='show' position='POS_LOAD'}
|
||||
$("span.show").replaceWith('<div class="show">');
|
||||
{/registerJs}
|
||||
|
||||
{registerCss}
|
||||
div.header {
|
||||
background-color: #3366bd;
|
||||
color: white;
|
||||
}
|
||||
{/registerCss}
|
||||
```
|
||||
|
||||
Asset bundles could be registered the following way:
|
||||
|
||||
```
|
||||
{use class="yii\web\JqueryAsset"}
|
||||
{JqueryAsset::register($this)|void}
|
||||
```
|
||||
|
||||
Here we're using `void` modifier because we don't need method call result.
|
||||
|
||||
#### URLs
|
||||
|
||||
There are two functions you can use for building URLs:
|
||||
|
||||
```php
|
||||
<a href="{path route='blog/view' alias=$post.alias}">{$post.title}</a>
|
||||
<a href="{url route='blog/view' alias=$post.alias}">{$post.title}</a>
|
||||
```
|
||||
|
||||
Internally, the `path()` function calls Yii's `Url::to()` method.
|
||||
`path` generates relative URL while `url` generates absolute one. Internally both are using [[\yii\helpers\Url]].
|
||||
|
||||
#### Additional variables
|
||||
|
||||
Within Smarty templates, you can also make use of these variables:
|
||||
Within Smarty templates the following variables are always defined:
|
||||
|
||||
- `$app`, which equates to `\Yii::$app`
|
||||
- `$this`, which equates to the current `View` object
|
||||
|
||||
#### Accessing config params
|
||||
|
||||
Yii parameters that are available in your application through `Yii::$app->params->something` could be used the following
|
||||
way:
|
||||
|
||||
```
|
||||
`{#something#}`
|
||||
```
|
||||
|
@ -4,8 +4,26 @@ Yii Framework 2 smarty extension Change Log
|
||||
2.0.0-rc under development
|
||||
--------------------------
|
||||
|
||||
- no changes in this release.
|
||||
|
||||
- Enh #4619 (samdark, hwmaier)
|
||||
- New functions:
|
||||
- `url` generates absolute URL.
|
||||
- `set` allows setting commonly used view paramters: `title`, `theme` and `layout`.
|
||||
- `meta` registers meta tag.
|
||||
- `registerJsFile` registers JavaScript file.
|
||||
- `registerCssFile` registers CSS file.
|
||||
- `use` allows importing classes to the template and optionally provides these as functions and blocks.
|
||||
- New blocks:
|
||||
- `title`.
|
||||
- `description`.
|
||||
- `registerJs`.
|
||||
- `registerCss`.
|
||||
- New modifier `void` that allows calling functions and ignoring result.
|
||||
- Moved most of Yii custom syntax into `\yii\smarty\Extension` class that could be extended via `extensionClass` property.
|
||||
- Added ability to set Smarty options via config using `options`.
|
||||
- Added `imports` property that accepts an array of classes imported into template namespace.
|
||||
- Added `widgets` property that can be used to import widgets as Smarty tags.
|
||||
- `Yii::$app->params['paramKey']` values are now accessible as Smarty config variables `{#paramKey#}`.
|
||||
- Added ability to use Yii aliases in `extends` and `require`.
|
||||
|
||||
2.0.0-beta April 13, 2014
|
||||
-------------------------
|
||||
|
429
extensions/smarty/Extension.php
Normal file
429
extensions/smarty/Extension.php
Normal file
@ -0,0 +1,429 @@
|
||||
<?php
|
||||
/**
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
namespace yii\smarty;
|
||||
|
||||
use Smarty;
|
||||
use Yii;
|
||||
use yii\helpers\ArrayHelper;
|
||||
use yii\helpers\Url;
|
||||
use yii\web\View;
|
||||
|
||||
/**
|
||||
* Extension provides Yii-specific syntax for Smarty templates.
|
||||
*
|
||||
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||
* @author Henrik Maier <hwmaier@gmail.com>
|
||||
*/
|
||||
class Extension
|
||||
{
|
||||
/**
|
||||
* @var ViewRenderer
|
||||
*/
|
||||
protected $viewRenderer;
|
||||
|
||||
/**
|
||||
* @var Smarty
|
||||
*/
|
||||
protected $smarty;
|
||||
|
||||
/**
|
||||
* @param ViewRenderer $viewRenderer
|
||||
* @param Smarty $smarty
|
||||
*/
|
||||
public function __construct($viewRenderer, $smarty)
|
||||
{
|
||||
$this->viewRenderer = $viewRenderer;
|
||||
$smarty = $this->smarty = $smarty;
|
||||
|
||||
$smarty->registerPlugin('function', 'path', [$this, 'functionPath']);
|
||||
$smarty->registerPlugin('function', 'url', [$this, 'functionUrl']);
|
||||
$smarty->registerPlugin('function', 'set', [$this, 'functionSet']);
|
||||
$smarty->registerPlugin('function', 'meta', [$this, 'functionMeta']);
|
||||
$smarty->registerPlugin('function', 'registerJsFile', [$this, 'functionRegisterJsFile']);
|
||||
$smarty->registerPlugin('function', 'registerCssFile', [$this, 'functionRegisterCssFile']);
|
||||
$smarty->registerPlugin('block', 'title', [$this, 'blockTitle']);
|
||||
$smarty->registerPlugin('block', 'description', [$this, 'blockDescription']);
|
||||
$smarty->registerPlugin('block', 'registerJs', [$this, 'blockJavaScript']);
|
||||
$smarty->registerPlugin('block', 'registerCss', [$this, 'blockCss']);
|
||||
$smarty->registerPlugin('compiler', 'use', [$this, 'compilerUse']);
|
||||
$smarty->registerPlugin('modifier', 'void', [$this, 'modifierVoid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty template function to get relative URL for using in links
|
||||
*
|
||||
* Usage is the following:
|
||||
*
|
||||
* {path route='blog/view' alias=$post.alias user=$user.id}
|
||||
*
|
||||
* where route is Yii route and the rest of parameters are passed as is.
|
||||
*
|
||||
* @param array $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function functionPath($params, \Smarty_Internal_Template $template)
|
||||
{
|
||||
if (!isset($params['route'])) {
|
||||
trigger_error("path: missing 'route' parameter");
|
||||
}
|
||||
|
||||
array_unshift($params, $params['route']) ;
|
||||
unset($params['route']);
|
||||
|
||||
return Url::to($params, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty template function to get absolute URL for using in links
|
||||
*
|
||||
* Usage is the following:
|
||||
*
|
||||
* {path route='blog/view' alias=$post.alias user=$user.id}
|
||||
*
|
||||
* where route is Yii route and the rest of parameters are passed as is.
|
||||
*
|
||||
* @param array $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function functionUrl($params, \Smarty_Internal_Template $template)
|
||||
{
|
||||
if (!isset($params['route'])) {
|
||||
trigger_error("path: missing 'route' parameter");
|
||||
}
|
||||
|
||||
array_unshift($params, $params['route']) ;
|
||||
unset($params['route']);
|
||||
|
||||
return Url::to($params, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty compiler function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {use class="app\assets\AppAsset"}
|
||||
* {use class="yii\helpers\Html"}
|
||||
* {use class='yii\widgets\ActiveForm' type='block'}
|
||||
* {use class='@app\widgets\MyWidget' as='my_widget' type='function'}
|
||||
*
|
||||
* Supported attributes: class, as, type. Type defaults to 'static'.
|
||||
*
|
||||
* @param $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function compilerUse($params, $template)
|
||||
{
|
||||
if (!isset($params['class'])) {
|
||||
trigger_error("use: missing 'class' parameter");
|
||||
}
|
||||
// Compiler plugin parameters may include quotes, so remove them
|
||||
foreach ($params as $key => $value) {
|
||||
$params[$key] = trim($value, '\'""');
|
||||
}
|
||||
|
||||
$class = $params['class'];
|
||||
$alias = ArrayHelper::getValue($params, 'as', basename($params['class']));
|
||||
$type = ArrayHelper::getValue($params, 'type', 'static');
|
||||
|
||||
// Register the class during compile time
|
||||
$this->smarty->registerClass($alias, $class);
|
||||
|
||||
if ($type === 'block') {
|
||||
// Register widget tag during compile time
|
||||
$this->viewRenderer->widgets['blocks'][$alias] = $class;
|
||||
$this->smarty->registerPlugin('block', $alias, [$this->viewRenderer, '_widget_block__' . $alias]);
|
||||
|
||||
// Inject code to re-register widget tag during run-time
|
||||
return <<<PHP
|
||||
<?php
|
||||
\$_smarty_tpl->getGlobal('_viewRenderer')->widgets['blocks']['$alias'] = '$class';
|
||||
try {
|
||||
\$_smarty_tpl->registerPlugin('block', '$alias', [\$_smarty_tpl->getGlobal('_viewRenderer'), '_widget_block__$alias']);
|
||||
}
|
||||
catch (SmartyException \$e) {
|
||||
/* Ignore already registered exception during first execution after compilation */
|
||||
}
|
||||
?>
|
||||
PHP;
|
||||
} elseif ($type === 'function') {
|
||||
// Register widget tag during compile time
|
||||
$this->viewRenderer->widgets['functions'][$alias] = $class;
|
||||
$this->smarty->registerPlugin('function', $alias, [$this->viewRenderer, '_widget_function__' . $alias]);
|
||||
|
||||
// Inject code to re-register widget tag during run-time
|
||||
return <<<PHP
|
||||
<?php
|
||||
\$_smarty_tpl->getGlobal('_viewRenderer')->widgets['functions']['$alias'] = '$class';
|
||||
try {
|
||||
\$_smarty_tpl->registerPlugin('function', '$alias', [\$_smarty_tpl->getGlobal('_viewRenderer'), '_widget_function__$alias']);
|
||||
}
|
||||
catch (SmartyException \$e) {
|
||||
/* Ignore already registered exception during first execution after compilation */
|
||||
}
|
||||
?>
|
||||
PHP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty modifier plugin
|
||||
* Converts any output to void
|
||||
* @param mixed $arg
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function modifierVoid($arg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {set title="My Page"}
|
||||
* {set theme="frontend"}
|
||||
* {set layout="main.tpl"}
|
||||
*
|
||||
* Supported attributes: title, theme, layout
|
||||
*
|
||||
* @param $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function functionSet($params, $template)
|
||||
{
|
||||
if (isset($params['title'])) {
|
||||
$template->tpl_vars['this']->value->title = Yii::$app->getView()->title = ArrayHelper::remove($params, 'title');
|
||||
}
|
||||
if (isset($params['theme'])) {
|
||||
$template->tpl_vars['this']->value->theme = Yii::$app->getView()->theme = ArrayHelper::remove($params, 'theme');
|
||||
}
|
||||
if (isset($params['layout'])) {
|
||||
Yii::$app->controller->layout = ArrayHelper::remove($params, 'layout');
|
||||
}
|
||||
|
||||
// We must have consumed all allowed parameters now, otherwise raise error
|
||||
if (!empty($params)) {
|
||||
trigger_error('set: Unsupported parameter attribute');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {meta keywords="Yii,PHP,Smarty,framework"}
|
||||
*
|
||||
* Supported attributes: any; all attributes are passed as
|
||||
* parameter array to Yii's registerMetaTag function.
|
||||
*
|
||||
* @param $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function functionMeta($params, $template)
|
||||
{
|
||||
$key = isset($params['name']) ? $params['name'] : null;
|
||||
|
||||
Yii::$app->getView()->registerMetaTag($params, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty block function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {title} Web Site Login {/title}
|
||||
*
|
||||
* Supported attributes: none.
|
||||
*
|
||||
* @param $params
|
||||
* @param $content
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @param $repeat
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function blockTitle($params, $content, $template, &$repeat)
|
||||
{
|
||||
if ($content !== null) {
|
||||
Yii::$app->getView()->title = $content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty block function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {description}
|
||||
* The text between the opening and closing tags is added as
|
||||
* meta description tag to the page output.
|
||||
* {/description}
|
||||
*
|
||||
* Supported attributes: none.
|
||||
*
|
||||
* @param $params
|
||||
* @param $content
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @param $repeat
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function blockDescription($params, $content, $template, &$repeat)
|
||||
{
|
||||
if ($content !== null) {
|
||||
// Clean-up whitespace and newlines
|
||||
$content = preg_replace('/\s+/', ' ', trim($content));
|
||||
|
||||
Yii::$app->getView()->registerMetaTag(['name' => 'description',
|
||||
'content' => $content],
|
||||
'description');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {registerJsFile url='http://maps.google.com/maps/api/js?sensor=false' position='POS_END'}
|
||||
*
|
||||
* Supported attributes: url, key, depends, position and valid HTML attributes for the script tag.
|
||||
* Refer to Yii documentation for details.
|
||||
* The position attribute is passed as text without the class prefix.
|
||||
* Default is 'POS_END'.
|
||||
*
|
||||
* @param $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function functionRegisterJsFile($params, $template)
|
||||
{
|
||||
if (!isset($params['url'])) {
|
||||
trigger_error("registerJsFile: missing 'url' parameter");
|
||||
}
|
||||
|
||||
$url = ArrayHelper::remove($params, 'url');
|
||||
$key = ArrayHelper::remove($params, 'key', null);
|
||||
$depends = ArrayHelper::remove($params, 'depends', null);
|
||||
if (isset($params['position']))
|
||||
$params['position'] = $this->getViewConstVal($params['position'], View::POS_END);
|
||||
|
||||
Yii::$app->getView()->registerJsFile($url, $depends, $params, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty block function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {registerJs key='show' position='POS_LOAD'}
|
||||
* $("span.show").replaceWith('<div class="show">');
|
||||
* {/registerJs}
|
||||
*
|
||||
* Supported attributes: key, position. Refer to Yii documentation for details.
|
||||
* The position attribute is passed as text without the class prefix.
|
||||
* Default is 'POS_READY'.
|
||||
*
|
||||
* @param $params
|
||||
* @param $content
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @param $repeat
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function blockJavaScript($params, $content, $template, &$repeat)
|
||||
{
|
||||
if ($content !== null) {
|
||||
$key = isset($params['key']) ? $params['key'] : null;
|
||||
$position = isset($params['position']) ? $params['position'] : null;
|
||||
|
||||
Yii::$app->getView()->registerJs($content,
|
||||
$this->getViewConstVal($position, View::POS_READY),
|
||||
$key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {registerCssFile url='@assets/css/normalizer.css'}
|
||||
*
|
||||
* Supported attributes: url, key, depends and valid HTML attributes for the link tag.
|
||||
* Refer to Yii documentation for details.
|
||||
*
|
||||
* @param $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function functionRegisterCssFile($params, $template)
|
||||
{
|
||||
if (!isset($params['url'])) {
|
||||
trigger_error("registerCssFile: missing 'url' parameter");
|
||||
}
|
||||
|
||||
$url = ArrayHelper::remove($params, 'url');
|
||||
$key = ArrayHelper::remove($params, 'key', null);
|
||||
$depends = ArrayHelper::remove($params, 'depends', null);
|
||||
|
||||
Yii::$app->getView()->registerCssFile($url, $depends, $params, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty block function plugin
|
||||
* Usage is the following:
|
||||
*
|
||||
* {registerCss}
|
||||
* div.header {
|
||||
* background-color: #3366bd;
|
||||
* color: white;
|
||||
* }
|
||||
* {/registerCss}
|
||||
*
|
||||
* Supported attributes: key and valid HTML attributes for the style tag.
|
||||
* Refer to Yii documentation for details.
|
||||
*
|
||||
* @param $params
|
||||
* @param $content
|
||||
* @param \Smarty_Internal_Template $template
|
||||
* @param $repeat
|
||||
* @return string
|
||||
* @note Even though this method is public it should not be called directly.
|
||||
*/
|
||||
public function blockCss($params, $content, $template, &$repeat)
|
||||
{
|
||||
if ($content !== null) {
|
||||
$key = isset($params['key']) ? $params['key'] : null;
|
||||
|
||||
Yii::$app->getView()->registerCss($content, $params, $key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a textual constant identifier to a View class
|
||||
* integer constant value.
|
||||
*
|
||||
* @param string $string Constant identifier name
|
||||
* @param integer $default Default value
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getViewConstVal($string, $default)
|
||||
{
|
||||
$val = @constant('yii\web\View::' . $string);
|
||||
return isset($val) ? $val : $default;
|
||||
}
|
||||
}
|
@ -9,14 +9,17 @@ namespace yii\smarty;
|
||||
|
||||
use Yii;
|
||||
use Smarty;
|
||||
use yii\base\View;
|
||||
use yii\web\View;
|
||||
use yii\base\Widget;
|
||||
use yii\base\ViewRenderer as BaseViewRenderer;
|
||||
use yii\helpers\Url;
|
||||
use yii\base\InvalidConfigException;
|
||||
use yii\helpers\ArrayHelper;
|
||||
|
||||
/**
|
||||
* SmartyViewRenderer allows you to use Smarty templates in views.
|
||||
*
|
||||
* @author Alexander Makarov <sam@rmcreative.ru>
|
||||
* @author Henrik Maier <hwmaier@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
class ViewRenderer extends BaseViewRenderer
|
||||
@ -29,45 +32,202 @@ class ViewRenderer extends BaseViewRenderer
|
||||
* @var string the directory or path alias pointing to where Smarty compiled templates will be stored.
|
||||
*/
|
||||
public $compilePath = '@runtime/Smarty/compile';
|
||||
|
||||
/**
|
||||
* @var Smarty
|
||||
* @var array Add additional directories to Smarty's search path for plugins.
|
||||
*/
|
||||
public $smarty;
|
||||
public $pluginDirs = [];
|
||||
/**
|
||||
* @var array Class imports similar to the use tag
|
||||
*/
|
||||
public $imports = [];
|
||||
/**
|
||||
* @var array Widget declarations
|
||||
*/
|
||||
public $widgets = ['functions' => [], 'blocks' => []];
|
||||
/**
|
||||
* @var Smarty The Smarty object used for rendering
|
||||
*/
|
||||
protected $smarty;
|
||||
|
||||
/**
|
||||
* @var array additional Smarty options
|
||||
* @see http://www.smarty.net/docs/en/api.variables.tpl
|
||||
*/
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* @var string extension class name
|
||||
*/
|
||||
public $extensionClass = '\yii\smarty\Extension';
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates and configures the Smarty object.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->smarty = new Smarty();
|
||||
$this->smarty->setCompileDir(Yii::getAlias($this->compilePath));
|
||||
$this->smarty->setCacheDir(Yii::getAlias($this->cachePath));
|
||||
|
||||
$this->smarty->registerPlugin('function', 'path', [$this, 'smarty_function_path']);
|
||||
foreach ($this->options as $key => $value) {
|
||||
$this->smarty->$key = $value;
|
||||
}
|
||||
|
||||
$this->smarty->setTemplateDir([
|
||||
dirname(Yii::$app->getView()->getViewFile()),
|
||||
Yii::$app->getViewPath(),
|
||||
]);
|
||||
|
||||
// Add additional plugin dirs from configuration array, apply Yii's dir convention
|
||||
foreach ($this->pluginDirs as &$dir) {
|
||||
$dir = $this->resolveTemplateDir($dir);
|
||||
}
|
||||
$this->smarty->addPluginsDir($this->pluginDirs);
|
||||
|
||||
if (isset($this->imports)) {
|
||||
foreach(($this->imports) as $tag => $class) {
|
||||
$this->smarty->registerClass($tag, $class);
|
||||
}
|
||||
}
|
||||
// Register block widgets specified in configuration array
|
||||
if (isset($this->widgets['blocks'])) {
|
||||
foreach(($this->widgets['blocks']) as $tag => $class) {
|
||||
$this->smarty->registerPlugin('block', $tag, [$this, '_widget_block__' . $tag]);
|
||||
$this->smarty->registerClass($tag, $class);
|
||||
}
|
||||
}
|
||||
// Register function widgets specified in configuration array
|
||||
if (isset($this->widgets['functions'])) {
|
||||
foreach(($this->widgets['functions']) as $tag => $class) {
|
||||
$this->smarty->registerPlugin('function', $tag, [$this, '_widget_func__' . $tag]);
|
||||
$this->smarty->registerClass($tag, $class);
|
||||
}
|
||||
}
|
||||
|
||||
new $this->extensionClass($this, $this->smarty);
|
||||
|
||||
$this->smarty->default_template_handler_func = [$this, 'aliasHandler'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty template function to get a path for using in links
|
||||
* The directory can be specified in Yii's standard convention
|
||||
* using @, // and / prefixes or no prefix for view relative directories.
|
||||
*
|
||||
* Usage is the following:
|
||||
*
|
||||
* {path route='blog/view' alias=$post.alias user=$user.id}
|
||||
*
|
||||
* where route is Yii route and the rest of parameters are passed as is.
|
||||
*
|
||||
* @param $params
|
||||
* @param \Smarty_Internal_Template $template
|
||||
*
|
||||
* @return string
|
||||
* @param string $dir directory name to be resolved
|
||||
* @return string the resolved directory name
|
||||
*/
|
||||
public function smarty_function_path($params, \Smarty_Internal_Template $template)
|
||||
protected function resolveTemplateDir($dir)
|
||||
{
|
||||
if (!isset($params['route'])) {
|
||||
trigger_error("path: missing 'route' parameter");
|
||||
if (strncmp($dir, '@', 1) === 0) {
|
||||
// e.g. "@app/views/dir"
|
||||
$dir = Yii::getAlias($dir);
|
||||
} elseif (strncmp($dir, '//', 2) === 0) {
|
||||
// e.g. "//layouts/dir"
|
||||
$dir = Yii::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($dir, '/');
|
||||
} elseif (strncmp($dir, '/', 1) === 0) {
|
||||
// e.g. "/site/dir"
|
||||
if (Yii::$app->controller !== null) {
|
||||
$dir = Yii::$app->controller->module->getViewPath() . DIRECTORY_SEPARATOR . ltrim($dir, '/');
|
||||
} else {
|
||||
// No controller, what to do?
|
||||
}
|
||||
} else {
|
||||
// relative to view file
|
||||
$dir = dirname(Yii::$app->getView()->getViewFile()) . DIRECTORY_SEPARATOR . $dir;
|
||||
}
|
||||
|
||||
array_unshift($params, $params['route']) ;
|
||||
unset($params['route']);
|
||||
return $dir;
|
||||
}
|
||||
|
||||
return Url::to($params);
|
||||
/**
|
||||
* Mechanism to pass a widget's tag name to the callback function.
|
||||
*
|
||||
* Using a magic function call would not be necessary if Smarty would
|
||||
* support closures. Smarty closure support is announced for 3.2,
|
||||
* until its release magic function calls are used to pass the
|
||||
* tag name to the callback.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @throws InvalidConfigException
|
||||
* @throws \BadMethodCallException
|
||||
* @return string
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$methodInfo = explode('__', $method);
|
||||
if (count($methodInfo) === 2) {
|
||||
$alias = $methodInfo[1];
|
||||
if (isset($this->widgets['functions'][$alias])) {
|
||||
if (($methodInfo[0] === '_widget_func') && (count($args) === 2)) {
|
||||
return $this->widgetFunction($this->widgets['functions'][$alias], $args[0], $args[1]);
|
||||
}
|
||||
} elseif (isset($this->widgets['blocks'][$alias])) {
|
||||
if (($methodInfo[0] === '_widget_block') && (count($args) === 4)) {
|
||||
return $this->widgetBlock($this->widgets['blocks'][$alias], $args[0], $args[1], $args[2], $args[3]);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidConfigException('Widget "' . $alias . '" not declared.');
|
||||
}
|
||||
}
|
||||
|
||||
throw new \BadMethodCallException('Method does not exist: ' . $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty plugin callback function to support widget as Smarty blocks.
|
||||
* This function is not called directly by Smarty but through a
|
||||
* magic __call wrapper.
|
||||
*
|
||||
* Example usage is the following:
|
||||
*
|
||||
* {ActiveForm assign='form' id='login-form'}
|
||||
* {$form->field($model, 'username')}
|
||||
* {$form->field($model, 'password')->passwordInput()}
|
||||
* <div class="form-group">
|
||||
* <input type="submit" value="Login" class="btn btn-primary" />
|
||||
* </div>
|
||||
* {/ActiveForm}
|
||||
*/
|
||||
private function widgetBlock($class, $params, $content, \Smarty_Internal_Template $template, &$repeat)
|
||||
{
|
||||
// Check if this is the opening ($content is null) or closing tag.
|
||||
if ($content === null) {
|
||||
$params['class'] = $class;
|
||||
// Figure out where to put the result of the widget call, if any
|
||||
$assign = ArrayHelper::remove($params, 'assign', false);
|
||||
ob_start();
|
||||
ob_implicit_flush(false);
|
||||
$widget = Yii::createObject($params);
|
||||
Widget::$stack[] = $widget;
|
||||
if ($assign) {
|
||||
$template->assign($assign, $widget);
|
||||
}
|
||||
} else {
|
||||
$widget = array_pop(Widget::$stack);
|
||||
echo $content;
|
||||
$out = $widget->run();
|
||||
return ob_get_clean() . $out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Smarty plugin callback function to support widgets as Smarty functions.
|
||||
* This function is not called directly by Smarty but through a
|
||||
* magic __call wrapper.
|
||||
*
|
||||
* Example usage is the following:
|
||||
*
|
||||
* {GridView dataProvider=$provider}
|
||||
*
|
||||
*/
|
||||
private function widgetFunction($class, $params, \Smarty_Internal_Template $template)
|
||||
{
|
||||
$repeat = false;
|
||||
$this->widgetBlock($class, $params, null, $template, $repeat); // $widget->init(...)
|
||||
return $this->widgetBlock($class, $params, '', $template, $repeat); // $widget->run()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,17 +239,35 @@ class ViewRenderer extends BaseViewRenderer
|
||||
* @param View $view the view object used for rendering the file.
|
||||
* @param string $file the view file.
|
||||
* @param array $params the parameters to be passed to the view file.
|
||||
*
|
||||
* @return string the rendering result
|
||||
*/
|
||||
public function render($view, $file, $params)
|
||||
{
|
||||
/* @var $template \Smarty_Internal_Template */
|
||||
$template = $this->smarty->createTemplate($file, null, null, empty($params) ? null : $params, true);
|
||||
$template = $this->smarty->createTemplate($file, null, null, empty($params) ? null : $params, false);
|
||||
|
||||
// Make Yii params available as smarty config variables
|
||||
$template->config_vars = Yii::$app->params;
|
||||
|
||||
$template->assign('app', \Yii::$app);
|
||||
$template->assign('this', $view);
|
||||
|
||||
return $template->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves Yii alias into file path
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $name
|
||||
* @param string $content
|
||||
* @param string $modified
|
||||
* @param Smarty $smarty
|
||||
* @return bool|string path to file or false if it's not found
|
||||
*/
|
||||
public function aliasHandler($type, $name, &$content, &$modified, Smarty $smarty)
|
||||
{
|
||||
$file = Yii::getAlias($name);
|
||||
return is_file($file) ? $file : false;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ namespace yiiunit\extensions\smarty;
|
||||
use yii\web\AssetManager;
|
||||
use yii\web\View;
|
||||
use Yii;
|
||||
use yiiunit\data\base\Singer;
|
||||
use yiiunit\TestCase;
|
||||
|
||||
/**
|
||||
@ -41,6 +42,47 @@ class ViewRendererTest extends TestCase
|
||||
$this->assertEquals('test view Hello World!.', $content);
|
||||
}
|
||||
|
||||
public function testLayoutAssets()
|
||||
{
|
||||
$view = $this->mockView();
|
||||
$content = $view->renderFile('@yiiunit/extensions/smarty/views/layout.tpl');
|
||||
|
||||
$this->assertEquals(1, preg_match('#<script src="/assets/[0-9a-z]+/jquery\\.js"></script>\s*</body>#', $content), 'Content does not contain the jquery js:' . $content);
|
||||
}
|
||||
|
||||
|
||||
public function testChangeTitle()
|
||||
{
|
||||
$view = $this->mockView();
|
||||
$view->title = 'Original title';
|
||||
|
||||
$content = $view->renderFile('@yiiunit/extensions/smarty/views/changeTitle.tpl');
|
||||
$this->assertTrue(strpos($content, 'New title') !== false, 'New title should be there:' . $content);
|
||||
$this->assertFalse(strpos($content, 'Original title') !== false, 'Original title should not be there:' . $content);
|
||||
}
|
||||
|
||||
public function testForm()
|
||||
{
|
||||
$view = $this->mockView();
|
||||
$model = new Singer();
|
||||
$content = $view->renderFile('@yiiunit/extensions/smarty/views/form.tpl', ['model' => $model]);
|
||||
$this->assertEquals(1, preg_match('#<form id="login-form" class="form-horizontal" action="/form-handler" method="post">.*?</form>#s', $content), 'Content does not contain form:' . $content);
|
||||
}
|
||||
|
||||
public function testInheritance()
|
||||
{
|
||||
$view = $this->mockView();
|
||||
$content = $view->renderFile('@yiiunit/extensions/smarty/views/extends2.tpl');
|
||||
$this->assertTrue(strpos($content, 'Hello, I\'m inheritance test!') !== false, 'Hello, I\'m inheritance test! should be there:' . $content);
|
||||
$this->assertTrue(strpos($content, 'extends2 block') !== false, 'extends2 block should be there:' . $content);
|
||||
$this->assertFalse(strpos($content, 'extends1 block') !== false, 'extends1 block should not be there:' . $content);
|
||||
|
||||
$content = $view->renderFile('@yiiunit/extensions/smarty/views/extends3.tpl');
|
||||
$this->assertTrue(strpos($content, 'Hello, I\'m inheritance test!') !== false, 'Hello, I\'m inheritance test! should be there:' . $content);
|
||||
$this->assertTrue(strpos($content, 'extends3 block') !== false, 'extends3 block should be there:' . $content);
|
||||
$this->assertFalse(strpos($content, 'extends1 block') !== false, 'extends1 block should not be there:' . $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return View
|
||||
*/
|
||||
@ -50,6 +92,9 @@ class ViewRendererTest extends TestCase
|
||||
'renderers' => [
|
||||
'tpl' => [
|
||||
'class' => 'yii\smarty\ViewRenderer',
|
||||
'options' => [
|
||||
'force_compile' => true, // always recompile templates, don't do it in production
|
||||
],
|
||||
],
|
||||
],
|
||||
'assetManager' => $this->mockAssetManager(),
|
||||
|
3
tests/unit/extensions/smarty/views/changeTitle.tpl
Normal file
3
tests/unit/extensions/smarty/views/changeTitle.tpl
Normal file
@ -0,0 +1,3 @@
|
||||
{set title='New title'}
|
||||
|
||||
<title>{$this->title}</title>
|
5
tests/unit/extensions/smarty/views/extends1.tpl
Normal file
5
tests/unit/extensions/smarty/views/extends1.tpl
Normal file
@ -0,0 +1,5 @@
|
||||
Hello, I'm inheritance test!
|
||||
|
||||
{block name=test}
|
||||
extends1 block
|
||||
{/block}
|
5
tests/unit/extensions/smarty/views/extends2.tpl
Normal file
5
tests/unit/extensions/smarty/views/extends2.tpl
Normal file
@ -0,0 +1,5 @@
|
||||
{extends file="@yiiunit/extensions/smarty/views/extends1.tpl"}
|
||||
|
||||
{block name=test}
|
||||
extends2 block
|
||||
{/block}
|
5
tests/unit/extensions/smarty/views/extends3.tpl
Normal file
5
tests/unit/extensions/smarty/views/extends3.tpl
Normal file
@ -0,0 +1,5 @@
|
||||
{extends file="@yiiunit/extensions/smarty/views/extends1.tpl"}
|
||||
|
||||
{block name=test}
|
||||
extends3 block
|
||||
{/block}
|
9
tests/unit/extensions/smarty/views/form.tpl
Normal file
9
tests/unit/extensions/smarty/views/form.tpl
Normal file
@ -0,0 +1,9 @@
|
||||
{use class='yii\widgets\ActiveForm' type='block'}
|
||||
{ActiveForm assign='form' id='login-form' action='/form-handler' options=['class' => 'form-horizontal']}
|
||||
{$form->field($model, 'firstName')}
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-1 col-lg-11">
|
||||
<input type="submit" value="Login" class="btn btn-primary" />
|
||||
</div>
|
||||
</div>
|
||||
{/ActiveForm}
|
16
tests/unit/extensions/smarty/views/layout.tpl
Normal file
16
tests/unit/extensions/smarty/views/layout.tpl
Normal file
@ -0,0 +1,16 @@
|
||||
{use class="yii\web\JqueryAsset"}
|
||||
{JqueryAsset::register($this)|void}
|
||||
{$this->beginPage()}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="{$app->charset}"/>
|
||||
<title>{$this->title|escape}</title>
|
||||
{$this->head()}
|
||||
</head>
|
||||
<body>
|
||||
{$this->beginBody()}
|
||||
body
|
||||
{$this->endBody()}
|
||||
</body>
|
||||
{$this->endPage()}
|
Reference in New Issue
Block a user