Декораторы Zend_Framework обертывают ярлык и ViewHelper внутри div - PullRequest
10 голосов
/ 25 сентября 2011

Я новичок в этом, Zend Художественная маархия, но у меня есть два важных вопроса, которые я не могу придумать.За первым вопросом следует некоторый пример

$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);

Какие выходы

<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>

Вопрос № 1

Как мне обернуть label и inputвокруг тега div?Таким образом, вывод выглядит следующим образом:

<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>

Вопрос № 2

Что происходит с порядком elements в массиве $decorate? Они не имеют смысла!

Ответы [ 2 ]

21 голосов
/ 25 сентября 2011

Шаблон декоратора является шаблоном проектирования для добавления функциональности к существующим классам без изменения этих существующих классов.Вместо этого, класс декоратора оборачивается вокруг другого класса и, как правило, предоставляет тот же интерфейс, что и декорированный класс.

Базовый пример:

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>

Теперь вы можете подумать, чтопоскольку классы Zend_Form_Decorator_* являются декораторами и имеют метод render, это автоматически означает, что выходные данные метода декорированного класса 'render всегда будут упакованы дополнительным содержимым декоратором.Но при рассмотрении нашего основного примера, приведенного выше, мы можем легко увидеть, что это совсем не обязательно должно быть так, как показано на этом дополнительном (хотя и довольно бесполезном) примере:

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>

Этона самом деле именно так работают многие Zend_Form_Decorator_* декораторы, если для них имеет смысл иметь такую ​​функциональность размещения.

Для декораторов, где это имеет смысл, вы можете управлять размещением с помощью setOption( 'placement', 'append' ) длянапример, или путем передачи опции 'placement' => 'append' в массив опций, например.

Например, для Zend_Form_Decorator_PrepareElements эта опция размещения бесполезна и поэтому игнорируется, так как она подготавливает элементы формы для использованияViewScript декоратор, что делает его одним из декораторов, которые не касаются визуализированного содержимого декорированного элемента.

В зависимости от функциональности по умолчанию отдельных декораторов, либо содержимое декорированного класса переносится, добавляется, добавляется, отбрасывается или что-то совершенно другое делается сecorated класс, без добавления чего-либо непосредственно к контенту, перед передачей контента следующему декоратору.Рассмотрим простой пример:

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>

Теперь, когда вы установите декораторы для элемента Zend_Form_Element_*, они будут упакованы и, следовательно, выполнены в том порядке, в котором они были добавлены.Итак, в соответствии с вашим примером:

$decorate = array(
    array('ViewHelper'),
    array('Description'),
    array('Errors', array('class'=>'error')),
    array('Label', array('tag'=>'div', 'separator'=>' ')),
    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="">

// 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>

А что вы знаете;на самом деле - это размещение по умолчанию всех соответствующих декораторов.

Но теперь самое сложное, что нам нужно сделать, чтобы получить результат, который вы ищете?Чтобы обернуть label и input, мы не можем просто сделать это:

$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')),
);

... так как это обернет весь предыдущий контент (ViewHelper, Description, Errors и Label) с div, верно?Даже не ... добавленный декоратор будет заменен следующим, так как декораторы заменяются следующим декоратором, если он того же класса.Вместо этого вам нужно было бы дать ему уникальный ключ:

$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')),
);

Теперь мы все еще сталкиваемся с проблемой, что divWrapper обернет все предыдущее содержимое (ViewHelper, Description, Errors и Label).Таким образом, мы должны быть креативными здесь.Есть множество способов достичь того, чего мы хотим.Я приведу один пример, который, вероятно, является самым простым:

$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
);

Для более подробного объяснения о Zend_Form декораторах я бы рекомендовал прочитать статью ведущего разработчика Zend Framework Мэтью Вейера О'Пинни о ZendФорма декораторов

2 голосов
/ 25 сентября 2011

Вопрос № 1

Измените порядок декораторов и добавьте помощник HtmlTag следующим образом:

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

Вопрос № 2

Декораторы - это цепочки, выходные данные каждого из которых передаются на вход следующего, чтобы «декорироваться» им.

По умолчанию они добавляют контент (описание, ошибки), добавьте содержимое (метка ..) и / или оберните что-нибудь вокруг (HtmlTag).Но это поведение по умолчанию, и вы можете изменить его для большинства из них:

array('HtmlTag', array('tag' => 'span', placement=>'APPEND'));
//this would append <span></span> to the output of the previous decorator instead of wrapping it inside the <span>

Давайте более подробно рассмотрим, что происходит в вашей цепочке:

  1. ViewHelper отображает ваш элемент формы, используя его viewHelper по умолчанию, объявленный в классе элемента формы.

  2. Label добавляет метку к предыдущему выводу

  3. HtmlTag переносит <div> вокруг

  4. Description добавляет описание элементов

  5. Errors добавляет сообщения об ошибках,если есть

  6. HtmlTag, все это оборачивается в <li>

EDIT

Я написал этот ответ, ничего не проверяя, поэтому здесь и там могут быть небольшие неточности.Уважаемый читатель, если вы видите некоторые, просто оставьте комментарий, и я обновлю.

...