Pull to refresh

Использование Zend_Form_Element_File в CRUD

Reading time 10 min
Views 2.4K
image

При проектировании приложений на Zend Framework использование компонента Zend_Form существенно облегчает работу с сущностями – единожды созданная форма, с настроенными валидаторами, фильтрами и прочим, используется как при создании, так и при редактировании данных в БД.
Очень часто сущности содержат элементы типа File – будь то картинка-превью, имя файла для скачивания или фотография в галерее. Вот только если Вы захотите изменить сущность в форме которой есть элемент File, становится ясно что стандартный декоратор Zend_Form_Element_File не подходит для формы изменения сущности – т.к. он не позволяет отобразить наличие загруженного файла, не дает возможности удалить этот файл и т.п.

Иными словами, когда вы открываете сущность для изменения, все остальные элементы формы заполняются значениями из БД – при этом Zend_Form_Element_File этого делать не имеет.
В это статье хочу поделиться своей реализацией работы с Zend_Form_Element_File в рамках создания CRUD (create-read-update-delete) на ZendFramework 1.11

Под хабракатом вас ожидает подробное описание как создать декоратор, как прицепить его к форме и инструкции по работе с контроллером.



Zend_Form_Element_File – общая информация, особенности


При добавлении новой сущности в БД Zend_Form_Element_File выглядит следующим (стандартным) образом:
image

При изменении сущности, Zend_Form_Element_File по-умолчанию выглядит аналогично. Наша задача привести вид этого элемента к такому:
image
Соответственно, отображается текущее загруженное имя файла, сам элемент для указания нового файла (старый будет им заменен) и чекбокс для удаления уже загруженного файла и обнуления столбца с фотографией для редактируемой сущности.

Создаем собственный декоратор для Zend_Form_Element_File

Для этого в папке application/forms/Decorators создаем файл File.php со следующим классом

  1. <?php
  2. class Application_Form_Decorators_File extends Zend_Form_Decorator_Abstract
  3. {
  4.     public function buildLabel()
  5.     {
  6.         $element = $this->getElement();
  7.         $label = $element->getLabel();
  8.         if ($translator = $element->getTranslator()) {
  9.             $label = $translator->translate($label);
  10.         }
  11.         if ($element->isRequired()) {
  12.             $label .= '*';
  13.         }
  14.         $label .= ':';
  15.         return $element->getView()
  16.                        ->formLabel($element->getName(), $label);
  17.     }
  18.  
  19.     public function buildInput()
  20.     {
  21.         $element = $this->getElement();
  22.         $helper  = $element->helper;
  23.         return $element->getView()->$helper(
  24.             $element->getName(),
  25.             $element->getValue(),
  26.             $element->getAttribs(),
  27.             $element->options
  28.         );
  29.     }
  30.  
  31.     public function buildErrors()
  32.     {
  33.         $element  = $this->getElement();
  34.         $messages = $element->getMessages();
  35.         if (empty($messages)) {
  36.             return '';
  37.         }
  38.         return '<div class="errors">' .
  39.                $element->getView()->formErrors($messages) . '</div>';
  40.     }
  41.  
  42.     public function buildDescription()
  43.     {
  44.         $element = $this->getElement();
  45.         $desc    = $element->getDescription();
  46.         if (empty($desc)) {
  47.             return '';
  48.         }
  49.         return '<div class="description">' . $desc . '</div>';
  50.     }
  51.  
  52.     public function render($content)
  53.     {
  54.         $element = $this->getElement();
  55.         if (!$element instanceof Zend_Form_Element) {
  56.             return $content;
  57.         }
  58.         if (null === $element->getView()) {
  59.             return $content;
  60.         }
  61.  
  62.         $separator = $this->getSeparator();
  63.         $placement = $this->getPlacement();
  64.         $label     = $this->buildLabel();
  65.         $input     = $this->buildInput();
  66.         $errors    = $this->buildErrors();
  67.         $desc      = $this->buildDescription();
  68.  
  69.         $renderedContent = $element->getView()->partial(
  70.         'decorators/file.phtml',
  71.         array('element'=>$element));
  72.  
  73.  
  74.         $output = '<tr><td>'
  75.                 . $label .'</td><td>' . $renderedContent
  76.                 . $input
  77.                 . $errors
  78.                 . $desc
  79.                 . '</td></tr>';
  80.  
  81.         switch ($placement) {
  82.             case (self::PREPEND):
  83.                 return $output . $separator . $content;
  84.             case (self::APPEND):
  85.             default:
  86.                 return $content . $separator . $output;
  87.         }
  88.     }
  89. }


Таким образом мы можем собрать внешний вид элемента «по-частям», добавив нужную информацию для вывода. С помощью инструкции

  1.         $renderedContent = $element->getView()->partial(
  2.         'decorators/file.phtml',
  3.         array('element'=>$element));


Попутно передаем информацию об текущем элементе (Zend_Form_Element_File) как $element (нужно для и
рендерим файл application/views/scripts/decorators/file.phtml со следующим содержимым:

  1. Вы загрузили фото '<?php echo $this->element->getDescription(); ?>'.<br>
  2. Вы можете изменить фото, указав новый файл. Если изменения фотографии не требуется, оставьте данное поле пустым. <br>
  3. Для того, чтобы удалить фото, поставьте отметку:
  4. <input name="<?php echo $this->element->getName(); ?>_checkbox" type="checkbox" value="on" /><br />


Применение декораторов в форме


Поскольку кастомный декоратор нужен только при изменении сущности, то при добавлении сущности оставляем стандартный декоратор:
  1.     if ($this->_options['type'] == 'edit' && !is_null($this->_options['photo_file'])) {
  2.     $photo->setDescription($this->_options['photo_file']);
  3.     $photo->setDecorators(array(
  4.      array('ViewScript', array('viewScript' => 'decorators/file.phtml')),
  5.          'File',
  6.          'Errors',
  7.          array(array('data' => 'HtmlTag'), array('tag' => 'td', 'class' => 'element')),
  8.          array('Label', array('tag' => 'td')),
  9.          array(array('row' => 'HtmlTag'), array('tag' => 'tr'))
  10.      ));
  11.     } else {
  12.     $photo->setDecorators(array(
  13.          'File',
  14.          'Errors',
  15.          array(array('data' => 'HtmlTag'), array('tag' => 'td', 'class' => 'element')),
  16.          array('Label', array('tag' => 'td')),
  17.          array(array('row' => 'HtmlTag'), array('tag' => 'tr'))
  18.      ));
  19.     }


Использование декоратора в контроллере:


Для каждой загруженной картинки создается превью с помощью фильтра — соответственно при удалении картинки надо удалять и превью.

  1. // если поставлена галочка на удаление - то удаляем файл и пищем в БД null
  2.  
  3. if (!is_null($this->getRequest()->getParam('photo_checkbox')) && $this->getRequest()->getParam('photo_checkbox') == 'on') {
  4. if (file_exists(PUBLIC_PATH . '/userfiles/images/full/' . $item->photo))
  5. unlink(PUBLIC_PATH . '/userfiles/images/full/' . $item->photo);
  6.  
  7. if (file_exists(PUBLIC_PATH . '/userfiles/images/thumb/' . $item->photo))
  8. unlink(PUBLIC_PATH . '/userfiles/images/thumb/' . $item->photo);
  9.  
  10. $item->photo = null;
  11. }
  12.  
  13. // если указан новый файл и он загружен, то удаляем старый файл и пишем в БД новый.
  14. // в случае, если элемент File не заполнен, $form->photo->getFileName() == array(null) ;
  15. if (!is_null($form->photo->getFileName()) && count($form->photo->getFileName()) != 0) {
  16.  
  17. if (file_exists(PUBLIC_PATH . '/userfiles/images/full/' . $item->photo)) {
  18. unlink(PUBLIC_PATH . '/userfiles/images/full/' . $item->photo);
  19. }
  20. if (file_exists(PUBLIC_PATH . '/userfiles/images/thumb/' . $item->photo)) {
  21. unlink(PUBLIC_PATH . '/userfiles/images/thumb/' . $item->photo);
  22. }
  23. $item->photo = $form->getValue('photo');
  24. }


Исходные коды:


  1. Application_Form_Decorators_File
  2. Application_Form_Photogallery
  3. PhotogalleryController


Если тема актуальна, то в есть еще материал по поводу написания фильтров для обработки изображений, загружаемых через Zend_Form_Element_File (gdlib и imagic).

Спасибо за внимание, принимаются комментарии и предложения. Не так давно начал изучать Zend Framework, решения подобной задачи не нашел — пришлось разбираться самому. Если что-то сделано не совсем корректно и (или) не в стиле ZF — прошу отписать в комментариях, поправлю.

Текущая реализация успешно работает.
Tags:
Hubs:
+5
Comments 13
Comments Comments 13

Articles