The best Smarty + Zend View Helpers solution!

csdn的blog真垃圾, 我费了一个下午翻译出来的文章, 结果保存的时候丢了, 白费功夫了, 算了,只接转帖英文原文吧, 顺便强烈BS一下CSDN。

The best Smarty + Zend View Helpers solution!

Tags: , The solutions in this post will be based on the SmartyView class, introduced in , so check it out if you feel it might help you understand this one.

Originally posted in my old blog at My Opera

Coming on again with the Smarty and Zend View related articles, let's this time take a look at how to get Zend's View Helpers running with Smarty.

Other examples for this that I have seen use a syntax like this: {helper helper=Url p='array(something)' p2=stuff}, which is kind of ugly and the array parsing is done with eval, and we know that
Eval is Evil.

Wouldn't a more elegant solution let you use helpers just like you use Smarty plugins? In the style of just typing the name of the helper and simple parameters? Let's see how to make that happen!

 

The solutions in this post will be based on the SmartyView class, introduced in Smarty + Zend View, take three, so check it out if you feel it might help you understand this one.

Starting


So, we want to have a nice syntax for calling Zend's viewhelpers from Smarty templates. First, we have some issues we have to find solutions for:

  • Smarty does not “know” about View Helpers
  • View helpers may need arrays or associative arrays as parameters. Smarty has no support for doing this out-of-the-box

    The first one is quite simple to solve: Since the Zend_View class knows about viewhelpers and can call them, the SmartyView class also does that. Since we need to create a new class based on Smarty anyway, we can add a method for saving the View instance to it so that the new Smarty class can also call view helpers.

    The second one needs more work. Since Smarty's syntax for passing parameters to functions differs a lot from the typical syntax, we need to think of a nice Smarty-like way of passing arrays and associative arrays as parameters. We also need to dig in the Smarty compiler class which converts the Smarty templates into PHP code. Isn't it great that I did the digging for you and you can just read about it here? :up:

    For the syntax, I decided on this:
{helperName param=foo arr=bar arr=baz assoc.one=test assoc.two=hello}
This would be the same as writing this in normal Zend_View templates:
$this->helperName('foo', array('bar','baz'), array('one' => 'test', 'two' => 'hello');


I think this is very Smarty-like and at least I like the fact that I can call the parameters whatever I want as I could use it to make it more obvious what the parameters will change. Consider the following:

{makeList source=$myData sortBy=name}
or something like
{makeList $myData name}

Extending Smarty


So, let's first extend the Smarty class to add our shiny new features!

I'm going to call the class SmartyAdvanced, since I'm bad at making up good catchy names. Feel free to suggest anything better ;)

So as we learnt above, we need to add some methods into the Smarty class, namely a method for saving a Zend_View instance into it and a method for calling View Helpers from the compiled templates.

Thus…

<? php
require_once   " smarty/libs/Smarty.class.php " ;
 
class  SmartyAdvanced  extends  Smarty
{
  
private   $_zendView ;
 
  
public   function  __construct()
  {
    parent
:: __construct();
    
$this -> compiler_class  =   " SmartyAdvanced_Compiler " ;
  }
 
  
public   function  setZendView(Zend_View_Abstract  $view )
  {
    
$this -> _zendView  =   $view ;
  }
 
  
public   function  callViewHelper( $name , $args )
  {
    
$helper   =   $this -> _zendView -> getHelper( $name );
 
    
return   call_user_func_array ( array ( $helper , $name ) , $args );
  }  
}
?>


So here's our brand new SmartyAdvanced class. The constructor calls the Smarty class' constructor and sets the compiler class name to SmartyAdvanced_Compiler… we'll look at it a bit later.

The other two functions serve as the view helper methods. setZendView should be pretty obvious, and callViewHelper simply gets the view helper from the Zend_View class and calls it with the parameters provided.

Extending Smarty_Compiler


As you know, Smarty templates are compiled into PHP code to speed up the execution. Since some of the things we do are tampering with very internal things in Smarty, like the syntax, we need to extend the compiler to do things our way and not their way.

So we need to modify the compiler to understand our custom syntax and to make it so that Smarty will try to call View Helpers if it can't parse a template function call with its own functions.

There's one thing I want to point out before the code: In the constructor, the Zend_View object is specified by just making a new Zend_View instance. This is bad, but necessary.

Unless we want to go in and modify Smarty's code, possibly breaking our code in the case of an update, we have to instantiate a view here. A better option would be to use a view factory,
as shown in one of my previous posts.


    
    
<? php
require_once   " smarty/libs/Smarty_Compiler.class.php " ;
 
class  SmartyAdvanced_Compiler  extends  Smarty_Compiler
{
  
private  $_ zendView ;
 
  
public   function  __construct()
  {
    parent
:: __construct();
    
$this -> _zendView  =   new  Zend_View();
  }
 
  
function  _compile_compiler_tag( $tagCommand ,   $tagArgs ,   & $output )
  {
    
// We first try to use Smarty"s own functionality to parse the tag
     $found   =  parent :: _compile_compiler_tag( $tagCommand , $tagArgs , $output );
 
    
if ( ! $found )
    {
      
try
      {
        
// Check if helper exists and create output
         $helper   =   $this -> _zendView -> getHelper( $tagCommand );
 
        
$helperArgs   =   array ();
 
        
if ( $tagArgs   !==   null )
        {
          
// Start parsing our custom syntax
           $params   =   explode ( "   " , $tagArgs );
          
foreach ( $params   as   $p )
          {
            
// Split each key=value pair to vars
             list ( $key , $value =   explode ( " = " , $p , 2 );
            
$section   =   "" ;
 
            
// If there"s a dot in the key, it means we
            //need to use associative arrays

             if ( strpos ( " . " , $key !=   - 1 )
              
list ( $key , $section =   explode ( " . " , $key );
 
            
// Use Smarty"s own functions to parse the value
            //so that if there"s a variable, it gets changed to
            //properly point at a template variable etc.

             $value   =   $this -> _parse_var_props( $value );
 
            
// Put the value into the arg array
             if ( $section   ==   "" )
            {
              
if ( array_key_exists ( $key , $helperArgs ))
              {
                
if ( is_array ( $helperArgs [ $key ]))
                  
$helperArgs [ $key ][]  =   $value ;
                
else
                  
$helperArgs [ $key =   array ( $helperArgs [ $key ] , $value );
 
              }
              
else
                
$helperArgs [ $key =   $value ;
            }
            
else
            {
              
if ( ! is_array ( $helperArgs [ $key ]))
                
$helperArgs [ $key =   array ();
 
              
$helperArgs [ $key ][ $section =   $value ;
            }
          }
        }
 
        
// Save the code to put to the template in the output
         $output   =   " <?php echo $this->callViewHelper( " $tagCommand " ,array( " . $this -> _createParameterCode( $helperArgs ) . " )); ?> " ;
        
$found   =   true ;
      }
      
catch (Zend_View_Exception  $e )
      {
        
// Exception means the helper was not found
         $found   =   false ;
      }
    }
 
    
return   $found ;
  }
 
  
// This function creates the code for the helper params
   private   function  _createParameterCode( $params )
  {
    
$code   =   "" ;
 
    
$i   =   1 ;
    
$pCount   =   count ( $params );
    
foreach ( $params   as   $p )
    {
      
if ( is_array ( $p ))
        
$code   .=   " array( " . $this -> _createParameterCode( $p ) . " ) " ;
      
else
        
$code   .=   $p ;
 
      
if ( $i   !=   $pCount )
        
$code   .=   " , " ;
 
      
$i ++ ;
    }
 
    
return   $code ;
  }
}
?>




Okay so that's a bit more than previously. With that, we're all done for Smarty mods!




Using the new classes



Using the new classes is pretty easy. We need to do a small change to the SmartyView class' constructor. Replace…

$this -> _smarty  =   new  Smarty();

with the following

if ( isset ( $config [ " smartyClass " ]))
  
$this -> _smarty  =   new   $config [ " smartyClass " ];
else
  
$this -> _smarty  =   new  Smarty();


Doing this will enable us to pass the Smarty class we want to use as a parameter.

Like this…

$view   =   new  SmartyView( array (
        
" compileDir "   =>   " ./template_c " ,
        
" helperPath "   =>   " ./application/views/helpers " ,
        
" pluginDir "   =>   " ./plugins " ,
        
" smartyClass "   =>   " SmartyAdvanced "
        ));
 
$view -> getEngine() -> setZendView( $view );

All that is needed is to give the new SmartyAdvanced class name as the smartyClass parameter for the SmartyView configuration. Then we need to pass the view instance to setZendView so that Smarty will be able to call helpers.

Conclusion


Despite Smarty's code being not very modification/extension-friendly, it's possible to add a lot of interesting features to it as you can see. With these extensions, using View Helpers with Smarty as the template engine is much nicer in my opinion.

The code for SmartyAdvanced and SmartyAdvanced_Compiler can be found
here.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值