您的位置:首页 > 编程语言 > PHP开发

Zend Framework 1.10.1 理解和使用 Zend 表单装饰器之五:创建和呈现复合元素

2010-04-08 14:21 519 查看
在上一节中,我们有一个展示一个出生日期元素的例子:

<div class="element">

<?php echo $form->dateOfBirth->renderLabel(0 ?>

<?php echo $this->formText('dateOfBirth[day]', '', array('size' => 2, 'maxlength' =>2)) ?>

/

<?php echo $this->formText('dateOfBirth[month]', '', array('size' => 2, 'maxlength' =>2)) ?>

/

<?php echo $this->formText('dateOfBirth[year]', '', array('size' => 4, 'maxlength' => 4)) ?>

</div>

你如何把这个元素当作一个 Zend_Form_Element 来展现?你如何写一个装饰器来呈现它?

元素

元素是如何工作的,这个问题包括:

你如何设置和检索值?

你如何验证值?

不管其它的,你如何允许分离的表单 input 接受这三个片断(日,月,年)?

头两个问题围绕表单元素它自己:setValue() 和 getValue() 是如何工作的?事实上由这个问题可以暗示到关于装饰器的一个问题:你如何检索从元素得来的分离的数据片断以及/或者设置它们?

解决的办法是重写你的元素的 setValue() 方法来提供一些定制的逻辑。在这个特定的例子,我们的元素应该有三个分离的行为:

如果提供一个整型的时间戳,它应该被用作确定和储存日,月和年。

如果提供一个文字字符串,它应该能被转换成一个时间戳,然后这个值会用作确定和储存日,月和年。

如果提供一个包括日,月和年为键值的数组,那些值将会被储存。

内部的,日,月和年将被分别储存。当元素的值被检索到的时候,它将以一种规范化的字符串格式储存。我们重写 getValue() 也把分离的数据片断组合进一个最终的字符串。

我们的类将看起来是这样的:

class My_Form_Element_Date extends Zend_Form_Element_Xhtml

{

protected $_dateFormat = '%year%-%month%-%day%';

protected $_day;

protected $_month;

protected $_year;

public function setDay($value)

{

$this->_day = (int) $value;

return $this;

}

public function getDay()

{

return $this->_day;

}

public function setMonth($value)

{

$this->_month = (int) $value;

return $this;

}

public function getMonth()

{

return $this->_month;

}

public function setYear($value)

{

$this->_year = (int) $value;

return $this;

}

public function getYear()

{

return $this->_year;

}

public function setValue($value)

{

if (is_int($value)) {

$this->setDay(date('d', $value))

->setMonth(date('m', $value))

->setYear(date('Y', $value));

} elseif (is_string($value)) {

$date = strtotime($value);

$this->setDay(date('d', $date))

->setMonth(date('m', $date))

->setYear(date('Y', $date));

} elseif (is_array($value)

&& (isset($value['day'])

&& isset($value['month'])

&& isset($value['year'])

)

) {

$this->setDay($value['day'])

->setMonth($value['month'])

->setYear($value['year']);

} else {

throw new Exception('Invalid date value provided');

}

return $this;

}

public function getValue()

{

return str_replace(

array('%year%', '%month%', '%day%'),

array($this->getYear(), $this->getMonth(), $this->getDay()),

$this->_dateFormat

);

}

}

这个类提供了一些不错的扩展性--我们可以从我们的数据库中设置默认的值,同时确信值将会被保存而且正确的表现。另外,我们允许通过表单 input 用一个数组来传递值。最后,对于每一个日期片断,我们有了分离的访问器(accessors),我们现在可以把这些访问器应用在一个装饰器中,来创建一个合成元素。

装饰器

再看一下上一节提到的例子,让我们假设我们想让用户分别输入年,月,日。PHP 很幸运的允许我们在创建元素的时候使用数组表示法,所以它仍有可能采集这三个实体合成一个单一的值 -- 同时我们已经创建了一个 Zend_Form 元素可以处理这样一个数组。

装饰器相对简单:它会从元素抓取日,月和年,把每一个传递给一个分离视图帮助器来呈现个人的表单 input,然后这些会被合并,形成最后的标记(markup)。

class My_Form_Decorator_Date extends Zend_Form_Decorator_Abstract

{

public function render($content)

{

$element = $this->getElement();

if (!$element instanceof My_Form_Element_Date) {

// only want to render Date elements

return $content;

}

$view = $element->getView();

if (!$view instanceof Zend_View_Interface)

// using view helpers, so do nothing in no view present

return $content;

}

$day = $element->getDay();

$month = $element->getMonth();

$year = $element->getYear();

$name = $element->getFullyQualifiedName();

$params = array(

'size' => 2,

'maxlength' => 2,

);

$yearParams = array(

'size' => 2,

'maxlength' => 2,

);

$markup = $view->formText($name . '[day]', $day, $params)

. ' / ' . $view->formText($name . '[month]', $month, $params)

. ' / ' . $view->formText($name . '[year]', $year, $yearParams);

switch ($this->getPlacement()) {

case self::PREPEND:

return $markup . $this->getSeparator() . $content;

case self::APPEND:

default:

return $content . $this->getSeparator() . $markup;

}

}

}

我们现在得对我们的表单元素做一个小调整,告诉它我们想默认地使用上面的装饰器。这需要两个步骤。首先,我们需要把装饰器的路径通知这个元素。我们可以在构造函数中完成:

class My_Form_Element_Date extends Zend_Form_Element_Xhtml

{

// ...

public function __construct($spec, $options = null)

{

$this->addPrefixPath(

'My_Form_Decorator',

'My/Form/Decorator',

'decorator'

);

parent::__construct($spec, $options);

}

// ...

}

注意这些是在构造函数中完成,而不是在 init()。这有两个原因。首先,它允许以后在 init 扩展元素添加逻辑,而不必担心调用 parent::init()。其次,它允许通过设置(configuration)或者在一个 init 方法内传递额外的插件,然后允许用我自己的替代者来重写默认的日期装饰器。

下一步,我们需要使用我们新的日期装饰器来重写 loadDefaultDecorators() 方法:

class My_Form_Element_Date extends Zend_Form_Element_Xhtml

{

// ...

public function loadDefaultDecorators()

{

if ($this->loadDefualtDecoratorsIsDisabled()) {

return;

}

$decorators = $this->getDecorators();

if ($empty($decorators)) {

$this->addDecorator('Date')

->addDecorator('Errors')

->addDecorator('Description', array(

'tag' => 'p',

'class' => 'description'

))

->addDecorator('HtmlTag', array(

'tag' => 'dd',

'id' => $this->getName() . '-element'

))

->addDecorator('Label', array('tag' => 'dt'));

}

}

// ...

}

最后的输出是什么样子的?让我们想一下下面的元素:

$d = new My_Form_Element_Date('dateOfBirth');

$d->setLabel('Date of Birth: ')

->setView(new Zend_View());

// There are equivalent:

$d->setValue('20 April 2009');

$d->setValue(array('year' => '2009', 'month' => '04', 'day' => '20'));

如果你之后输出这个元素,你会得到下面的标记(markup)(对空格已经做过修改以便更好的阅读):

<dt id="dateOfBirth-label"><labe for="dateOfBirth" class="optional">

Date of Birth:

</label></dt>

<dd id="dateOfBirth-element">

<input type="text" name="dateOfBirth[day]" id="dateOfBirth-day"

value="20" size="2" maxlength="2"> /

<input type="text" name="dateOfBirth[month]" id="dateOfBirth-month"

value="4" size="2" maxlength="2"> /

<input type="text" name="dateOfBirth[year]" id="dateOfBirth-year"

value="2009" size="4" maxlength="4">

</dd>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐