我该如何在Zend_Form文件元素上使用ViewScripts?

19

我正在使用这个ViewScript来处理我的标准表单元素:

<div class="field" id="field_<?php echo $this->element->getId(); ?>">
   <?php if (0 < strlen($this->element->getLabel())) : ?>
      <?php echo $this->formLabel($this->element->getName(), $this->element->getLabel());?>
   <?php endif; ?>
   <span class="value"><?php echo $this->{$this->element->helper}(
      $this->element->getName(),
      $this->element->getValue(),
      $this->element->getAttribs()
   ) ?></span>
   <?php if (0 < $this->element->getMessages()->length) : ?>
       <?php echo $this->formErrors($this->element->getMessages()); ?>
   <?php endif; ?>
   <?php if (0 < strlen($this->element->getDescription())) : ?>
      <span class="hint"><?php echo $this->element->getDescription(); ?></span>
   <?php endif; ?>
</div>

仅使用ViewScript会导致错误:

表单异常:找不到文件装饰器...无法渲染文件元素

查看此 FAQ后,我更新了我的表单元素装饰器如下:

'decorators' => array(
   array('File'),
   array('ViewScript', array('viewScript' => 'form/field.phtml'))
)

现在它会将文件元素呈现两次,一次在我的视图脚本中,另外一些额外的元素带有文件元素并在我的视图脚本之外:

<input type="hidden" name="MAX_FILE_SIZE" value="8388608" id="MAX_FILE_SIZE" />
<input type="hidden" name="UPLOAD_IDENTIFIER" value="4b5f7335a55ee" id="progress_key" />
<input type="file" name="upload_file" id="upload_file" />
<div class="field" id="field_upload_file">
    <label for="upload_file">Upload File</label>
    <span class="value"><input type="file" name="upload_file" id="upload_file" /></span>
</div>

有没有关于如何使用ViewScript正确处理这个问题的想法?


更新:基于Shaun的解决方案,以下是我的最终代码:

表单元素:

$this->addElement('file', 'upload_file', array(
    'disableLoadDefaultDecorators' => true,
    'decorators' => array('File', array('ViewScript', array(
        'viewScript' => '_form/file.phtml',
        'placement' => false,
    ))),
    'label' => 'Upload',
    'required' => false,
    'filters' => array(),
    'validators' => array(array('Count', false, 1),),
));

查看脚本:

<?php
$class .= 'field ' . strtolower(end(explode('_',$this->element->getType())));
if ($this->element->isRequired()) {
    $class .= ' required';
}
if ($this->element->hasErrors()) {
    $class .= ' errors';
}
?>
<div class="<?php echo $class; ?>" id="field_<?php echo $this->element->getId(); ?>">
    <?php if (0 < strlen($this->element->getLabel())): ?>
        <?php echo $this->formLabel($this->element->getFullyQualifiedName(), $this->element->getLabel());?>
    <?php endif; ?>
    <span class="value"><?php echo $this->content; ?></span>
    <?php if ($this->element->hasErrors()): ?>
        <?php echo $this->formErrors($this->element->getMessages()); ?>
    <?php endif; ?>
    <?php if (0 < strlen($this->element->getDescription())): ?>
        <p class="hint"><?php echo $this->element->getDescription(); ?></p>
    <?php endif; ?>
</div>
7个回答

19

答案其实很简单。你只需要先指定 File 装饰器,为文件输入创建一个特定的视图脚本,并在 viewScript 装饰器参数中将放置设置为 false,这将有效地将 File 装饰器的输出注入到 viewScript 装饰器中。

$element->setDecorators(array('File', array('ViewScript', array('viewScript' => 'decorators/file.phtml', 'placement' => false))));

然后在新的文件元素视图脚本中,您只需在想要放置文件输入标记的位置处回显 $this->content 即可。以下是最近项目中的示例,如果标记看起来有点奇怪,请忽略它,希望它能说明问题。

<label for="<?php echo $this->element->getName(); ?>" class="element <?php if ($this->element->hasErrors()): ?> error<?php endif; ?>" id="label_<?php echo $this->element->getName(); ?>"> 
<span><?php echo $this->element->getLabel(); ?></span>

<?php echo $this->content; ?>

<?php if ($this->element->hasErrors()): ?>

    <span class="error">
        <?php echo $this->formErrors($this->element->getMessages()); ?>
    </span>

<?php endif; ?>

</label>

渲染后您会看到该元素的HTML代码

<label for="photo" class="element" id="label_photo"> 
<span>Photo</span>

<input type="hidden" name="MAX_FILE_SIZE" value="6291456" id="MAX_FILE_SIZE">
<input type="file" name="photo" id="photo">

</label>

这看起来正是我所需要的。如果它能够像我需要的那样工作,我会尝试并将您的标记为答案! - Sonny
我只是想再次感谢你,这比我的以前的解决方案好多了。 - Sonny

4
这并不是一个简单或理想的解决方案,因为它需要扩展File装饰器...但令人沮丧的是,他们没有将隐藏元素生成逻辑与文件输入生成逻辑分开。我不确定文件视图助手是否处理了元素是数组的问题(这似乎是他们这样做的原因)。 文件装饰器的扩展: (被注释掉的部分是导致额外输入被生成的原因。)
<?php

class Sys_Form_Decorator_File extends Zend_Form_Decorator_File {

  public function render($content) {

    $element = $this->getElement();
    if (!$element instanceof Zend_Form_Element) {return $content;}

    $view = $element->getView();
    if (!$view instanceof Zend_View_Interface) {return $content;}

    $name = $element->getName();
    $attribs = $this->getAttribs();
    if (!array_key_exists('id', $attribs)) {$attribs['id'] = $name;}

    $separator = $this->getSeparator();
    $placement = $this->getPlacement();
    $markup = array();
    $size = $element->getMaxFileSize();

    if ($size > 0) {

      $element->setMaxFileSize(0);
      $markup[] = $view->formHidden('MAX_FILE_SIZE', $size);

    }

    if (Zend_File_Transfer_Adapter_Http::isApcAvailable()) {

      $apcAttribs = array('id' => 'progress_key');
      $markup[] = $view->formHidden('APC_UPLOAD_PROGRESS', uniqid(), $apcAttribs);

    }

    else if (Zend_File_Transfer_Adapter_Http::isUploadProgressAvailable()) {

      $uploadIdAttribs = array('id' => 'progress_key');
      $markup[] = $view->formHidden('UPLOAD_IDENTIFIER', uniqid(), $uploadIdAttribs);

    }

    /*

    if ($element->isArray()) {

      $name .= "[]";
      $count = $element->getMultiFile();

      for ($i = 0; $i < $count; ++$i) {

        $htmlAttribs = $attribs;
        $htmlAttribs['id'] .= '-' . $i;
        $markup[] = $view->formFile($name, $htmlAttribs);

      }

    }

    else {$markup[] = $view->formFile($name, $attribs);} 

    */

    $markup = implode($separator, $markup);

    switch ($placement) {

      case self::PREPEND: return $markup . $separator . $content;
      case self::APPEND:
      default: return $content . $separator . $markup;

    }

  }

 }

?>

在控制器操作中设置表单:

$form = new Zend_Form();
$form->addElement(new Zend_Form_Element_File('file'));
$form->file->setLabel('File');
$form->file->setDescription('Description goes here.');

$decorators = array();
$decorators[] = array('File' => new Sys_Form_Decorator_File());
$decorators[] = array('ViewScript', array('viewScript' => '_formElementFile.phtml'));
$form->file->setDecorators($decorators);

$this->view->form = $form;

在 Action View 中:

<?php echo $this->form; ?>

在script元素中:
<div class="field" id="field_<?php echo $this->element->getId(); ?>">

<?php if (0 < strlen($this->element->getLabel())) : ?>
<?php echo $this->formLabel($this->element->getName(), $this->element->getLabel());?>
<?php endif; ?>

<span class="value">
<?php 

echo $this->{$this->element->helper}(

  $this->element->getName(),
  $this->element->getValue(),
  $this->element->getAttribs()

);

?>
</span>

<?php if (0 < $this->element->getMessages()->length) : ?>
<?php echo $this->formErrors($this->element->getMessages()); ?>
<?php endif; ?>

<?php if (0 < strlen($this->element->getDescription())) : ?>
<span class="hint"><?php echo $this->element->getDescription(); ?></span>
<?php endif; ?>

</div>

输出应该是:

<form enctype="multipart/form-data" action="" method="post">
<dl class="zend_form">
<input type="hidden" name="MAX_FILE_SIZE" value="134217728" id="MAX_FILE_SIZE" />
<div class="field" id="field_file">
<label for="file">File</label>
<span class="value"><input type="file" name="file" id="file" /></span>
<span class="hint">Description goes here.</span>
</div>
</dl>
</form>

这种解决方案的问题是,隐藏元素不会在视图脚本中呈现;如果您在客户端脚本中使用div作为选择器,则可能会出现问题...

谢谢建议 Robin,但是用那种方式仍然会抛出“没有文件装饰器”异常。 - Sonny
这真的很奇怪。在发布我的答案之前,我进行了测试,它仍然对我有效。 - Robin M. Canaday
顺便提一下,我使用的是Zend 1.96。不知道这会不会在这种情况下有所区别。 - Robin M. Canaday
我正在使用1.9.7版本,所以可能是个问题。我再试了一次,但还是出现了同样的错误。 - Sonny
我明白我做错了什么,以及为什么会出现错误,但是你的解决方案没有将“File”修饰符的输出包装在“ViewScript”修饰符内。它首先呈现,然后才是“ViewScript”的输出,类似于我上面的输出示例。 - Sonny
显示剩余2条评论

2
我发现,一个自定义的装饰器类可以处理大多数字段,除了文件字段。 请确保您的类实现以下接口:
class CC_Form_Decorator_Pattern 
extends Zend_Form_Decorator_Abstract 
implements Zend_Form_Decorator_Marker_File_Interface

这对我很有效。

这将是基于类的装饰器,而不是视图脚本,对吗? - Sonny

1

这帮助我解决了我的问题。我调整了代码,将文件元素包装在表格中。为了使其工作,只需从viewdecorator中删除标签,并按以下方式添加文件元素:

$form->addElement('file', 'upload_file', array(
        'disableLoadDefaultDecorators' => true,
        'decorators' => array(
            'Label',
            array(array('labelTd' => 'HtmlTag'), array('tag' => 'td', 'class' => 'labelElement')),
            array(array('elemTdOpen' => 'HtmlTag'), array('tag' => 'td', 'class' => 'dataElement','openOnly' => true, 'placement' => 'append')),
            'File',
            array('ViewScript', array(
            'viewScript' => 'decorators/file.phtml',
            'placement' => false,
            )),
            array(array('elemTdClose' => 'HtmlTag'), array('tag' => 'td', 'closeOnly' => true, 'placement' => 'append')),
            array(array('row' => 'HtmlTag'), array('tag' => 'tr'))
        ),
        'label' => 'Upload',
        'required' => false,
        'filters' => array(),
        'validators' => array(array('Count', false, 1), ),
    ));

0

我找到了一个解决方法,可以避免完全使用ViewScript。

首先,元素定义:

$this->addElement('file', 'upload_file', array(
    'disableLoadDefaultDecorators' => true,
    'decorators' => array(
        'File',
        array(array('Value'=>'HtmlTag'), array('tag'=>'span','class'=>'value')),
        'Errors',
        'Description',
        'Label',
        array(array('Field'=>'HtmlTag'), array('tag'=>'div','class'=>'field file')),
    ),
    'label' => 'Upload File',
    'required' => false,
    'filters' => array('StringTrim'),
    'validators' => array(),
));

其次,在表单类被实例化后,我会模仿我的ViewScript的行为:
$field = $form->getElement('upload_file');
$decorator = $field->getDecorator('Field');
$options = $decorator->getOptions();
$options['id'] = 'field_' . $field->getId();
if ($field->hasErrors()) {
    $options['class'] .= ' errors';
}
$decorator->setOptions($options);

我想我应该研究一下基于类的装饰器。也许那里有更多的灵活性?


我想保留这个问题的开放性,以防有一种使用ViewScript的方法来解决它。 - Sonny

0

如果你按照@Shaun的答案操作后仍然出现错误,请确保已经禁用了该元素的默认修饰器(请查看第2行):

$this->addElement('file', 'upload_file', array(
'disableLoadDefaultDecorators' => true,
'decorators' => array('File', array('ViewScript', array(
    'viewScript' => '_form/file.phtml',
    'placement' => false,
))),
'label' => 'Upload',
'required' => false,
'filters' => array(),
'validators' => array(array('Count', false, 1),),
));

0

最简单的方法是在自定义文件装饰器中不添加任何标记:

class Custom_Form_Decorator_File extends Zend_Form_Decorator_File {
        public function render($content) {
                return $content;
        }
}

现在,您可以在文件元素的视图脚本中执行任何操作(自行输出文件输入字段和所需的所有隐藏字段)。


我还没有机会尝试你的解决方案。我确实想要框架提供的所有隐藏字段,只是想将它们包装在我喜欢的HTML元素中。你的解决方案能够实现这一点吗? - Sonny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接