Zend_Framework 装饰器将 Label 和 ViewHelper 包裹在一个 div 中 [英] Zend_Framework Decorators Wrap Label and ViewHelper inside a div

查看:22
本文介绍了Zend_Framework 装饰器将 Label 和 ViewHelper 包裹在一个 div 中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新手,zend 装饰混乱,但我有两个重要的问题,我无法解决.问题一后面是一些例子

$decorate = array(数组('视图助手'),数组('说明'),数组('错误', 数组('class'=>'error')),数组('标签',数组('标签'=>'div','分隔符'=>'')),array('HtmlTag', array('tag' => 'li', 'class'=>'element')),);...$name = new Zend_Form_Element_Text('title');$name->setLabel('标题')->setDescription("No --- way");$name->setDecorator($decorate);

哪些输出

  • <label for="title" class="required">Title</label><input type="text" name="title" id="title" value=""><p class="hint">不---方式</p><ul class="错误"><li>值是必需的,不能为空</li>
  • 问题 #1

    如何将 labelinput 包裹在 div 标签周围?所以输出如下:

  • <div><label for="title" class="required">Title</label><input type="text" name="title" id="title" value="">
  • <p class="hint">不---方式</p><ul class="错误"><li>值是必需的,不能为空</li>

    <小时>

    问题#2

    $decorate 数组中 elements 的顺序是什么?它们毫无意义!

    解决方案

    装饰器模式是一种设计模式,用于添加现有类的功能而不改变那些现有的类.相反,装饰器类将自己包裹在另一个类周围,并且通常公开与被装饰类相同的接口.

    基本示例:

    interface 可渲染{公共函数渲染();}类 HelloWorld实现可渲染{公共函数渲染(){'你好世界!';}}类 BoldDecorator实现可渲染{受保护的 $_decoratee;公共函数 __construct( Renderable $decoratee ){$this->_decoratee = $decoratee;}公共函数渲染(){返回<b>".$this->_decoratee->render() .'</b>';}}//用 BoldDecorator 包装(装饰)HelloWorld$decorator = new BoldDecorator( new HelloWorld() );echo $decorator->render();//会输出<b>你好世界!</b>

    现在,你可能会想,因为 Zend_Form_Decorator_* 类是装饰器,并且有一个 render 方法,这自动意味着被装饰类的输出'render 方法将始终由装饰器包含附加内容.但是在检查我们上面的基本示例时,我们可以很容易地看到这当然不一定是这种情况,正如这个额外的(尽管相当无用)示例所示:

    class DivDecorator实现可渲染{const PREPEND = '前置';const APPEND = '追加';const WRAP = '包装';受保护的 $_placement;受保护的 $_decoratee;公共函数 __construct( Renderable $decoratee, $placement = self::WRAP ){$this->_decoratee = $decoratee;$this->_placement = $placement;}公共函数渲染(){$content = $this->_decoratee->render();开关( $this->_placement ){案例自我::准备:$content = '

    '.$内容;休息;案例自我::附加:$content = $content .'<div></div>';休息;案例自我::包裹:默认:$content = '

    '.$内容.'</div>';}返回 $content;}}//用 BoldDecorator 和 DivDecorator 包装(装饰)HelloWorld(使用 DivDecorator::APPEND)$decorator = new DivDecorator( new BoldDecorator( new HelloWorld() ), DivDecorator::APPEND );echo $decorator->render();//会输出<b>你好世界!</b><div></div>

    这实际上是许多 Zend_Form_Decorator_* 装饰器的基本工作方式,如果它们具有这种放置功能是有意义的.

    对于有意义的装饰器,您可以使用 setOption( 'placement', 'append' ) 来控制位置,例如,或通过传递选项 'placement' =>例如,追加" 到选项数组.

    例如,对于 Zend_Form_Decorator_PrepareElements,这个放置选项是无用的,因此被忽略,因为它准备表单元素以供 ViewScript 装饰器使用,使其成为不接触被装饰元素的渲染内容的装饰器.

    根据各个装饰器的默认功能,或者装饰类的内容被包装、附加、前置、丢弃或者对装饰类做一些完全不同的事情,而不直接添加一些东西到内容,然后将内容传递给下一个装饰器.考虑这个简单的例子:

    class ErrorClassDecorator实现可渲染{受保护的 $_decoratee;公共函数 __construct( Renderable $decoratee ){$this->_decoratee = $decoratee;}公共函数渲染(){//想象下面两个虚构的方法if( $this->_decoratee->hasErrors() ){$this->_decoratee->setAttribute('class', 'errors');}//我们没有触及渲染的内容,我们只是将 css 类设置为上面的 'errors'返回 $this->_decoratee->render();}}//用 BoldDecorator 和 ErrorClassDecorator 包装(装饰)HelloWorld$decorator = new ErrorClassDecorator( new BoldDecorator( new HelloWorld() ) );echo $decorator->render();//可能会输出类似的东西<b class="errors">Hello world!</b>

    现在,当您为 Zend_Form_Element_* 元素设置装饰器时,它们将按照添加的顺序进行包装和执行.所以,按照你的例子:

    $decorate = array(数组('视图助手'),数组('说明'),数组('错误', 数组('class'=>'error')),数组('标签',数组('标签'=>'div','分隔符'=>'')),array('HtmlTag', array('tag' => 'li', 'class'=>'element')),);

    ...基本上会发生以下情况(为简洁起见,实际类名被截断):

    $decorator = new HtmlTag( new Label( new Errors( new Description( new ViewHelper() ) ) ) );echo $decorator->render();

    因此,在检查您的示例输出时,我们应该能够提取各个装饰器的默认放置行为:

    //ViewHelper->render()<input type="text" name="title" id="title" value="">//描述->render()<input type="text" name="title" id="title" value=""><p class="hint">不---方式</p>//放置:追加//错误->render()<input type="text" name="title" id="title" value=""><p class="hint">不---方式</p><ul class="错误">//放置:追加<li>值是必需的,不能为空</li>//标签->render()<label for="title" class="required">Title</label>//放置:前置<input type="text" name="title" id="title" value=""><p class="hint">不---方式</p><ul class="错误"><li>值是必需的,不能为空</li>//HtmlTag->render()
  • //放置:包装<label for="title" class="required">Title</label><input type="text" name="title" id="title" value=""><p class="hint">不---方式</p><ul class="错误"><li>值是必需的,不能为空</li>
  • 你知道什么;这实际上所有相应装饰器的默认位置.

    但现在是困难的部分,我们需要做什么才能得到您想要的结果?为了包装 labelinput 我们不能简单地这样做:

    $decorate = array(数组('视图助手'),数组('说明'),数组('错误', 数组('class'=>'error')),数组('标签',数组('标签'=>'div','分隔符'=>'')),array('HtmlTag', array('tag' => 'div')),//默认位置:wraparray('HtmlTag', array('tag' => 'li', 'class'=>'element')),);

    ... 因为这将包装所有前面的内容(ViewHelperDescriptionErrorsLabel)带个div,对吧?甚至......添加的装饰器将被下一个替换,因为如果装饰器属于同一类,则装饰器将被下一个装饰器替换.相反,你必须给它一个唯一的键:

    $decorate = array(数组('视图助手'),数组('说明'),数组('错误', 数组('class'=>'error')),数组('标签',数组('标签'=>'div','分隔符'=>'')),array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')),//我们称之为 divWrapperarray('HtmlTag', array('tag' => 'li', 'class'=>'element')),);

    现在,我们仍然面临divWrapper 会包装所有前面的内容(ViewHelperDescriptionErrors标签).所以我们需要在这里发挥创造力.有很多方法可以实现我们想要的.我举一个例子,这可能是最简单的:

    $decorate = array(数组('视图助手'),array('Label', array('tag'=>'div', 'separator'=>' ')),//默认位置:前置array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')),//默认位置:wraparray('Description'),//默认位置:追加array('Errors', array('class'=>'error')),//默认位置:追加array('HtmlTag', array('tag' => 'li', 'class'=>'element')),//默认位置:wrap);

    有关 Zend_Form 装饰器的更多解释,我建议阅读 Zend Framework 的首席开发人员 Matthew Weier O'Phinney 的 关于 Zend 表单装饰器的文章

    I am new to this, zend decoration malarchy, but i have two significant questions that i cant get my head around. Question one is followed by some example

    $decorate = array(
        array('ViewHelper'),
        array('Description'),
        array('Errors', array('class'=>'error')),
        array('Label', array('tag'=>'div', 'separator'=>' ')),
        array('HtmlTag', array('tag' => 'li', 'class'=>'element')),
    );
    
    ...
    
    $name = new Zend_Form_Element_Text('title');
    $name->setLabel('Title')
        ->setDescription("No --- way");
    
    $name->setDecorator($decorate);
    

    Which outputs

    <li class="element">
        <label for="title" class="required">Title</label> 
        <input type="text" name="title" id="title" value="">
        <p class="hint">No --- way</p>
        <ul class="error">
            <li>Value is required and can't be empty</li>
        </ul>
    </li>
    

    The Question #1

    How do i wrap the label and the input around a div tag? So the output is as follows:

    <li class="element">
        <div>
            <label for="title" class="required">Title</label> 
            <input type="text" name="title" id="title" value="">
        </div>
        <p class="hint">No --- way</p>
        <ul class="error">
            <li>Value is required and can't be empty</li>
        </ul>
    </li>
    


    The Question #2

    What is up with the order of the elements in the $decorate array? They MAKE NO SENSE!

    解决方案

    The decorator pattern is a design pattern for adding functionality to existing classes without altering those existing classes. In stead, a decorator class wraps itself around another class, and generally exposes the same interface as the decorated class.

    Basic example:

    interface Renderable
    {
        public function render();
    }
    
    class HelloWorld
        implements Renderable
    {
        public function render()
        {
            return 'Hello world!';
        }
    }
    
    class BoldDecorator
        implements Renderable
    {
        protected $_decoratee;
    
        public function __construct( Renderable $decoratee )
        {
            $this->_decoratee = $decoratee;
        }
    
        public function render()
        {
            return '<b>' . $this->_decoratee->render() . '</b>';
        }
    }
    
    // wrapping (decorating) HelloWorld in a BoldDecorator
    $decorator = new BoldDecorator( new HelloWorld() );
    echo $decorator->render();
    
    // will output
    <b>Hello world!</b>
    

    Now, you might be tempted to think that because the Zend_Form_Decorator_* classes are decorators, and have a render method, this automatically means the output of the decorated class' render method will always be wrapped with additional content by the decorator. But on inspection of our basic example above, we can easily see this doesn't necessarily have to be the case at all of course, as illustrated by this additional (albeit fairly useless) example:

    class DivDecorator
        implements Renderable
    {
        const PREPEND = 'prepend';
        const APPEND  = 'append';
        const WRAP    = 'wrap';
    
        protected $_placement;
    
        protected $_decoratee;
    
        public function __construct( Renderable $decoratee, $placement = self::WRAP )
        {
            $this->_decoratee = $decoratee;
            $this->_placement = $placement;
        }
    
        public function render()
        {
            $content = $this->_decoratee->render();
            switch( $this->_placement )
            {
                case self::PREPEND:
                    $content = '<div></div>' . $content;
                    break;
                case self::APPEND:
                    $content = $content . '<div></div>';
                    break;
                case self::WRAP:
                default:
                    $content = '<div>' . $content . '</div>';
            }
    
            return $content;
        }
    }
    
    // wrapping (decorating) HelloWorld in a BoldDecorator and a DivDecorator (with DivDecorator::APPEND)
    $decorator = new DivDecorator( new BoldDecorator( new HelloWorld() ), DivDecorator::APPEND );
    echo $decorator->render();
    
    // will output
    <b>Hello world!</b><div></div>
    

    This is in fact basically how a lot of Zend_Form_Decorator_* decorators work, if it makes sense for them to have this placement functionality.

    For decorators where it makes sense, you can control the placement with the setOption( 'placement', 'append' ) for instance, or by passing the option 'placement' => 'append' to the options array, for instance.

    For Zend_Form_Decorator_PrepareElements, for instance, this placement option is useless and therefor ignored, as it prepares form elements to be used by a ViewScript decorator, making it one of the decorators that doesn't touch the rendered content of the decorated element.

    Depending on the default functionality of the individual decorators, either the content of the decorated class is wrapped, appended, prepended, discarded or something completely different is done to the decorated class, without adding something directly to the content, before passing along the content to the next decorator. Consider this simple example:

    class ErrorClassDecorator
        implements Renderable
    {
        protected $_decoratee;
    
        public function __construct( Renderable $decoratee )
        {
            $this->_decoratee = $decoratee;
        }
    
        public function render()
        {
            // imagine the following two fictional methods
            if( $this->_decoratee->hasErrors() )
            {
                $this->_decoratee->setAttribute( 'class', 'errors' );
            }
    
            // we didn't touch the rendered content, we just set the css class to 'errors' above
            return $this->_decoratee->render();
        }
    }
    
    // wrapping (decorating) HelloWorld in a BoldDecorator and an ErrorClassDecorator
    $decorator = new ErrorClassDecorator( new BoldDecorator( new HelloWorld() ) );
    echo $decorator->render();
    
    // might output something like
    <b class="errors">Hello world!</b>
    

    Now, when you set the decorators for a Zend_Form_Element_* element, they will be wrapped, and consequently executed, in the order in which they are added. So, going by your example:

    $decorate = array(
        array('ViewHelper'),
        array('Description'),
        array('Errors', array('class'=>'error')),
        array('Label', array('tag'=>'div', 'separator'=>' ')),
        array('HtmlTag', array('tag' => 'li', 'class'=>'element')),
    );
    

    ... basically what happens is the following (actual class names truncated for brevity):

    $decorator = new HtmlTag( new Label( new Errors( new Description( new ViewHelper() ) ) ) );
    echo $decorator->render();
    

    So, on examining your example output, we should be able to distill the default placement behaviour of the individual decorators:

    // ViewHelper->render()
    <input type="text" name="title" id="title" value="">
    
    // Description->render()
    <input type="text" name="title" id="title" value="">
    <p class="hint">No --- way</p> // placement: append
    
    // Errors->render()
    <input type="text" name="title" id="title" value="">
    <p class="hint">No --- way</p>
    <ul class="error"> // placement: append
        <li>Value is required and cant be empty</li>
    </ul>
    
    // Label->render()
    <label for="title" class="required">Title</label> // placement: prepend
    <input type="text" name="title" id="title" value="">
    <p class="hint">No --- way</p>
    <ul class="error">
        <li>Value is required and cant be empty</li>
    </ul>
    
    // HtmlTag->render()
    <li class="element"> // placement: wrap
        <label for="title" class="required">Title</label>
        <input type="text" name="title" id="title" value="">
        <p class="hint">No --- way</p>
        <ul class="error">
            <li>Value is required and cant be empty</li>
        </ul>
    </li>
    

    And what do you know; this actually is the default placement of all respective decorators.

    But now comes the hard part, what do we need to do to get the result you are looking for? In order to wrap the label and input we can't simply do this:

    $decorate = array(
        array('ViewHelper'),
        array('Description'),
        array('Errors', array('class'=>'error')),
        array('Label', array('tag'=>'div', 'separator'=>' ')),
        array('HtmlTag', array('tag' => 'div')), // default placement: wrap
        array('HtmlTag', array('tag' => 'li', 'class'=>'element')),
    );
    

    ... as this will wrap all preceding content (ViewHelper, Description, Errors and Label) with a div, right? Not even... the added decorator will be replaced by the next one, as decorators are replaced by a following decorator if it is of the same class. In stead you would have to give it a unique key:

    $decorate = array(
        array('ViewHelper'),
        array('Description'),
        array('Errors', array('class'=>'error')),
        array('Label', array('tag'=>'div', 'separator'=>' ')),
        array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')), // we'll call it divWrapper
        array('HtmlTag', array('tag' => 'li', 'class'=>'element')),
    );
    

    Now, we're still faced with the problem that divWrapper will wrap all preceding content (ViewHelper, Description, Errors and Label). So we need to be creative here. There's numerous ways to achieve what we want. I'll give one example, that probably is the easiest:

    $decorate = array(
        array('ViewHelper'),
        array('Label', array('tag'=>'div', 'separator'=>' ')), // default placement: prepend
        array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')), // default placement: wrap
        array('Description'), // default placement: append
        array('Errors', array('class'=>'error')), // default placement: append
        array('HtmlTag', array('tag' => 'li', 'class'=>'element')), // default placement: wrap
    );
    

    For more explanation about Zend_Form decorators I'd recommend reading Zend Framework's lead developer Matthew Weier O'Phinney's article about Zend Form Decorators

    这篇关于Zend_Framework 装饰器将 Label 和 ViewHelper 包裹在一个 div 中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆