Как я могу использовать ViewScripts на элементах файла Zend_Form?
Я использую этот 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'))
)
Теперь он визуализирует элемент file дважды, один раз в моем скрипте вида, и дополнительные элементы с элементом file вне моего скрипта вида:
<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>
7 ответов
Ответ относительно прост, как это происходит. Все, что вам нужно сделать, это сначала указать декоратор файла, создать специальный скрипт представления для ввода файла и указать false для размещения в аргументах декоратора viewScript, это эффективно внедрит выходные данные декоратора файла в декоратор 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>
Это не простое и не идеальное решение, потому что оно требует расширения декоратора файлов... но довольно обидно, что они не приложили усилий, чтобы отделить логику генерации скрытых элементов от логики генерации входных файлов. Я не уверен, что помощник представления файла обрабатывает проблему элемента, являющегося массивом (это, кажется, причина, почему они сделали это таким образом.)
Расширение File Decorator:(закомментированная часть - это то, что вызывает дополнительный ввод.)
<?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>
Проблема с этим решением состоит в том, что скрытые элементы не отображаются в пределах viewScript; это может быть проблемой, если вы используете div в качестве селектора в скрипте на стороне клиента...
Я понял, что пользовательский класс декоратора будет обрабатывать большинство полей, кроме полей File. Убедитесь, что ваш класс реализует интерфейс следующим образом:
class CC_Form_Decorator_Pattern
extends Zend_Form_Decorator_Abstract
implements Zend_Form_Decorator_Marker_File_Interface
Это сработало для меня.
Это помогло мне решить мою проблему. Я изменил код, чтобы обернуть элемент файла внутри таблицы. Чтобы заставить его работать, просто удалите метку из 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), ),
));
Я нашел обходной путь, который полностью избегает 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);
Я думаю, что я должен смотреть на декораторы на основе классов. Может быть, там больше гибкости?
Самое простое, что нужно сделать, это вообще не добавлять разметку к выводу в вашем пользовательском File Decorator:
Класс Custom_Form_Decorator_File extends Zend_Form_Decorator_File { публичная функция render($content) { вернуть $content; } }
теперь вы можете делать все, что вы хотите в своем скрипте для этого элемента файла (вывести поле ввода файла и все скрытые поля, которые вам нужны) самостоятельно.
На всякий случай, если вы следовали ответу @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),),
));