如何在 Zend_Form 文件元素上使用 ViewScript?

发布于 2024-08-19 06:17:15 字数 3548 浏览 10 评论 0原文

我将此 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 会导致错误:

表单捕获异常:无文件 发现装饰器...无法渲染 文件元素

查看此常见问题解答揭示了我的部分问题,我更新了我的表单像这样的元素装饰器:

'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 正确处理这个问题有什么想法吗?


更新:基于肖恩的解决方案,这是我的最终代码:

表单元素:

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

I am using this ViewScript for my standard form elements:

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

Trying to use that ViewScript alone results in an error:

Exception caught by form: No file
decorator found... unable to render
file element

Looking at this FAQ revealed part of my problem, and I updated my form element decorators like this:

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

Now it's rendering the file element twice, once within my view script, and extra elements with the file element outside my view script:

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

Any ideas on how to handle this properly with a ViewScript?


UPDATE: Based on Shaun's solution, here's my final code:

Form Element:

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

View Script:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

难理解 2024-08-26 06:17:15

答案相对简单。您需要做的就是首先指定文件装饰器,为文件输入创建一个特定的视图脚本,并为 viewScript 装饰器参数中的放置指定 false,这将有效地将文件装饰器的输出注入到 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>

The answer is relatively simple as it happens. All you need do is specify the File decorator first, create a specific view script for the file input and specify false for the placement in the viewScript decorator arguments, this will effectively inject the output from the File decorator into the viewScript decorator e.g.

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

Then in the new file element view script you simply echo $this->content in the script where you'd like the file input markup to be placed. Here's an example from a recent project, so ignore the markup if it looks a little odd, hopefully it will illustrate the point.

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

When rendered you will see this html for the element

<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>
还在原地等你 2024-08-26 06:17:15

这不是一个简单或理想的解决方案,因为它需要文件装饰器的扩展......但令人沮丧的是,他们没有努力将隐藏元素生成逻辑与文件输入生成逻辑分开。我不确定文件视图助手是否处理元素为数组的问题(这似乎是他们这样做的原因。)

文件装饰器的扩展:
(注释掉的部分是导致生成额外输入的原因。)

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

在操作视图中:

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

在元素脚本中:

<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 作为选择器,这可能会出现问题...

This is not a simple or ideal solution because it requires an extension of the File decorator... but it's rather frustrating that they didn't make the effort to separate the hidden element generation logic from the file input generation logic. I'm not sure if the file view helper handles the issue of an element being an array (that seems to be the reason they did it this way.)

Extension of File Decorator:
(the commented out part is what causes the extra input to be generated.)

<?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 setup in controller action:

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

In action view:

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

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

Output should be:

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

A problem with this solution is that the hidden elements don't render within the viewscript; this might be a problem if you're using the div as a selector in a client-side script...

榆西 2024-08-26 06:17:15

我意识到,自定义装饰器类将处理除文件字段之外的大多数字段。
确保您的类实现如下所示的接口:

class CC_Form_Decorator_Pattern 
extends Zend_Form_Decorator_Abstract 
implements Zend_Form_Decorator_Marker_File_Interface

这对我有用。

What I've realized is, a custom decorator class will handle most fields except File fields.
Make sure your class implements the interface like so:

class CC_Form_Decorator_Pattern 
extends Zend_Form_Decorator_Abstract 
implements Zend_Form_Decorator_Marker_File_Interface

This worked for me.

兮子 2024-08-26 06:17:15

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

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

This helped me fix my problem. I adjusted the code to wrap the file element inside a table. To make it work, simply remove the label from the viewdecorator and add the file element as follows:

$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), ),
    ));
最美不过初阳 2024-08-26 06:17:15

我找到了一种完全避免 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);

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

I've found a work-around that avoids the ViewScript altogether.

First, the element definition:

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

Second, after the form class has been instantiated, I mimic the behavior of my 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);

I guess that I should look into class-based decorators. Maybe there's more flexibility there?

最简单的事情是在自定义文件装饰器中的输出中根本不添加任何标记:

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

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

The easiest thing to do is to add no markup at all to the output in your custom File Decorator:

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

now you can do whatever you want in your viewscript for this file element (output the file input field and all hidden fields you need on your own).

猫腻 2024-08-26 06:17:15

以防万一您遵循了 @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),),
));

Just in case you have followed @Shaun's answer and you are still getting the error: make sure that you've disabled default decorators for the element in question (take look at line 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),),
));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文