A free and open-source book on ZF3 for beginners


Для демонстрации использования выгрузки файлов на сервер в Zend Framework 3, мы создадим галерею изображений, которая будет состоять из двух веб-страниц: страницы для выгрузки изображений (рисунок 10.2); и страницы галереи, содержащей список выгруженных изображений (рисунок 10.3).

Рабочий пример такой галереи Image Gallery можно посмотреть в приложении Form Demo, которое идет вместе с этой книгой.

Рисунок 10.2. Страница выгрузки изображений Рисунок 10.2. Страница выгрузки изображений

Рисунок 10.3. Страницы галереи изображений Рисунок 10.3. Страницы галереи изображений

Для этого мы примера мы создадим следующее:

10.8.1. Добавление модели FormModel

Для этого примера нам понадобится модель формы, которая будет использоваться для выгрузки файлов изображений. Назовем класс этой модели формы ImageForm. Этот класс позволит нам выгружать на сервер файлы изображений. Форма будет иметь следующие поля:

Ниже представлен код модели формы `ImageForm. Его нужно поместить в файл ImageForm.php, хранящийся в каталоге Form под корневым каталогом модуля:

<?php
namespace Application\Form;

use Zend\Form\Form;

// Эта форма используется для выгрузки файлов изображений.
class ImageForm extends Form
{
    // Конструктор.     
    public function __construct()
    {
        // Определяем имя формы.
        parent::__construct('image-form');
     
        // Устанавливаем метод POST для этой формы.
        $this->setAttribute('method', 'post');
                
        // Задаем бинарное кодирование содержимого.
        $this->setAttribute('enctype', 'multipart/form-data');
				
        $this->addElements();        
    }
    
    // Этот метод добавляет элементы к форме.
    protected function addElements() 
    {
        // Add "file" field.
        $this->add([
            'type'  => 'file',
            'name' => 'file',
            'attributes' => [                
                'id' => 'file'
            ],
            'options' => [
                'label' => 'Image file',
            ],
        ]);        
          
        // Добавляем кнопку отправки формы..
        $this->add([
            'type'  => 'submit',
            'name' => 'submit',
            'attributes' => [                
                'value' => 'Upload',
                'id' => 'submitbutton',
            ],
        ]);               
    }
}

Мы уже рассмотрели создание модели формы, и приведенный выше код не должен вызывать каких-либо проблем в его понимании. Мы лишь хотим обратить внимание читателя на то, что в строке 19 мы задаем значение "multipart/form-data" для атрибута формы "enctype", чтобы формой использовалось бинарное кодирование ее данных.

Вообще говоря, явно задавать атрибут "enctype" в конструкторе формы необязательно, так как элемент Zend\Form\Element\File делает это автоматически, когда вы вызываете метод формы prepare().

10.8.2. Добавление правил валидации к модели ImageForm

Для демонстрации использования валидаторов и фильтров, предназначенных для работы с выгрузкой файлов на сервер, мы добавим их к классу модели формы ImageForm. Мы хотим добиться следующих целей:

Чтобы добавить правила валидации, измените код класса ImageForm следующим образом:

<?php
namespace Application\Form;

use Zend\InputFilter\InputFilter;

// Эта форма используется для выгрузки файлов изображений.
class ImageForm extends Form
{
    // Конструктор
    public function __construct()
    {
        // ...
	
        // Добавляем правила валидации
        $this->addInputFilter();          
    }
  
    // ...
	
    // Этот метод создает фильтр входных данных (используется для фильтрации/валидации формы).
    private function addInputFilter() 
    {
        $inputFilter = new InputFilter();   
        $this->setInputFilter($inputFilter);
     
        // Добавляем правила валидации для поля "file".	 
        $inputFilter->add([
                'type'     => 'Zend\InputFilter\FileInput',
                'name'     => 'file',
                'required' => true,   
                'validators' => [
                    ['name'    => 'FileUploadFile'],
                    [
                        'name'    => 'FileMimeType',                        
                        'options' => [                            
                            'mimeType'  => ['image/jpeg', 'image/png']
                        ]
                    ],
                    ['name'    => 'FileIsImage'],
                    [
                        'name'    => 'FileImageSize',
                        'options' => [
                            'minWidth'  => 128,
                            'minHeight' => 128,
                            'maxWidth'  => 4096,
                            'maxHeight' => 4096
                        ]
                    ],
                ],
                'filters'  => [                    
                    [
                        'name' => 'FileRenameUpload',
                        'options' => [  
                            'target'=>'./data/upload',
                            'useUploadName'=>true,
                            'useUploadExtension'=>true,
                            'overwrite'=>true,
                            'randomize'=>false
                        ]
                    ]
                ],   
            ]);                
    }
}

В этом фрагменте мы добавляем следующие валидаторы файлов:

В строке 52 мы добавляем фильтр RenameUpload и настраиваем его таким образом, чтобы выгруженный на сервер файл сохранялся в каталог APP_DIR/data/upload. Этот фильтр будет использовать одинаковое имя для файла назначения и исходного файла (опция useUploadName). Если файл с таким именем уже существует, фильтр его перезапишет (опция overwrite).

Для работы валидаторов MimeType и IsImage нужно подключить расширение PHP fileinfo. Это расширение уже подключено в Linux Ubuntu, но не подключено в Windows. После этого не забудьте перезапустить HTTP-сервер Apache.

10.8.3. Написание сервиса ImageManager

Так как мы стараемся писать код, соответствующий паттерну предметно-ориентированного проектирования (Domain Driven Design), мы создадим класс модели сервиса, инкапсулирующий функциональность для управления изображениями. Мы назовем этот класс ImageManager и поместим его в пространство имен Application\Service. Также мы зарегистрируем этот сервис в менеджере сервисов веб-приложения.

Класс сервиса ImageManager будет иметь следующие public-методы (перечислены в таблице 10.3):

Таблица 10.3. Public-методы класса ImageManager.
Метод Описание
getSaveToDir() Возвращаем путь к каталогу, куда мы сохраняем файлы изображений.
getSavedFiles() Возвращает массив имен сохраненных файлов.
getImagePathByName($fileName) Возвращает путь к сохраненному файлу изображения.
getImageFileInfo($filePath) Извлекает информацию о файле (размер, MIME-тип) по его пути.
getImageFileContent($filePath) Возвращает содержимое файла изображения. При ошибке возвращает булевое false.
resizeImage($filePath, $desiredWidth) Изменяет размер изображения, сохраняя соотношение сторон.

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

Добавим файл ImageManager.php в каталог Service под корневым каталогом модуля. В этот файл поместим следующий код:

<?php
namespace Application\Service;

// Сервис менеджера изображений.
class ImageManager 
{
    // Каталог, куда мы сохраняем файлы изображений.
    private $saveToDir = './data/upload/';
        
    // Возвращаем путь к каталогу, куда мы сохраняем файлы изображений.
    public function getSaveToDir() 
    {
        return $this->saveToDir;
    }  
}

Как видите из фрагмента выше, мы определяем класс ImageManager в строке 5. У него есть private-свойство $saveToDir ?, которое содержит путь к каталогу, где находятся выгруженные на сервер файлы (строка 8) (мы храним выгруженные файлы в каталоге APP_DIR/data/upload).

Public-метод getSaveToDir() (строка 11) позволяет извлечь путь к каталогу выгрузки.

44) Хотя класс ImageManager является сервисом, он может иметь свойства, предназначенные для внутреннего использования.

Далее, мы хотим добавить public-метод getSavedFiles() к классу сервиса. Этот метод будет просматривать каталог выгрузки и возвращать массив, содержащий имена выгруженных на сервер файлов. Чтобы добавить метод getSavedFiles(), измените код таким образом:

<?php
//...

// Сервис менеджера изображений.
class ImageManager 
{
    //...
  
    // Возвращает массив имен выгруженных на сервер файлов.
    public function getSavedFiles() 
    {
        // Каталог, куда мы планируем сохранять выгруженные файлы..
        
        // Проверяем, существует ли уже каталог, и, если нет, то
        // создаем его.
        if(!is_dir($this->saveToDir)) {
            if(!mkdir($this->saveToDir)) {
                throw new \Exception('Could not create directory for uploads: ' . 
                             error_get_last());
            }
        }
        
        // Просматриваем каталог и создаем список выгруженных файлов. 
        $files = [];        
        $handle  = opendir($this->saveToDir);
        while (false !== ($entry = readdir($handle))) {
            
            if($entry=='.' || $entry=='..')
                continue; // Пропускаем текущий и родительский каталоги.
            
            $files[] = $entry;
        }
        
        // Возвращаем список выгруженных файлов.
        return $files;
    }  
}

Выше, в методе getSavedFiles() мы сперва проверяем, существует ли каталог (строка 16), и, если нет, то пытаемся его создать (строка 17). Затем мы получаем список файлов в каталоге (строки 24-32) и и возвращаем его.

После этого, добавим три метода для получения информации о выгруженном на сервер файле:

Чтобы добавить эти методы, внесите следующие изменения в код:

<?php
//...

// Сервис менеджера изображений.
class ImageManager 
{
    //...  
  
    // Возвращает путь к сохраненному файлу изображения.
    public function getImagePathByName($fileName) 
    {
        // Принимаем меры предосторожности, чтобы сделать файл безопасным.
        $fileName = str_replace("/", "", $fileName);  // Убираем слеши.
        $fileName = str_replace("\\", "", $fileName); // Убираем обратные слеши.
                
        // Возвращаем сцепленные имя каталога и имя файла.
        return $this->saveToDir . $fileName;                
    }
  
    // Возвращает содержимое файла изображения. При ошибке возвращает булевое false. 
    public function getImageFileContent($filePath) 
    {
        return file_get_contents($filePath);
    }
    
    // Извлекает информацию о файле (размер, MIME-тип) по его пути.
    public function getImageFileInfo($filePath) 
    {
        // Пробуем открыть файл        
        if (!is_readable($filePath)) {            
            return false;
        }
            
        // Получаем размер файла в байтах.
        $fileSize = filesize($filePath);

        // Получаем MIME-тип файла.
        $finfo = finfo_open(FILEINFO_MIME);
        $mimeType = finfo_file($finfo, $filePath);
        if($mimeType===false)
            $mimeType = 'application/octet-stream';
    
        return [
            'size' => $fileSize,
            'type' => $mimeType 
        ];
    }  
}

И наконец, мы хотим добавить функциональность для изменения размера в класс ImageManager. Этот набор функций будет использоваться для создания миниатюр изображений. Добавим метод resizeImage() к классу ImageManager следующим образом:

<?php
//...
class ImageManager 
{
    //...    
  
    //  Изменяет размер изображения, сохраняя соотношение сторон.
    public  function resizeImage($filePath, $desiredWidth = 240) 
    {
        // Получаем исходную размерность файла.
        list($originalWidth, $originalHeight) = getimagesize($filePath);

        // Вычисляем соотношение сторон.
        $aspectRatio = $originalWidth/$originalHeight;
        // Вычисляем получившуюся высоту.
        $desiredHeight = $desiredWidth/$aspectRatio;

        // Получаем информацию об изображении
        $fileInfo = $this->getImageFileInfo($filePath);
        
        // Изменяем размер изображения.
        $resultingImage = imagecreatetruecolor($desiredWidth, $desiredHeight);
        if (substr($fileInfo['type'], 0, 9) =='image/png')
            $originalImage = imagecreatefrompng($filePath);
        else
            $originalImage = imagecreatefromjpeg($filePath);
        imagecopyresampled($resultingImage, $originalImage, 0, 0, 0, 0, 
                $desiredWidth, $desiredHeight, $originalWidth, $originalHeight);

        // Сохраняем измененное изображение во временное хранилище.
        $tmpFileName = tempnam("/tmp", "FOO");
        imagejpeg($resultingImage, $tmpFileName, 80);
        
        // Возвращаем путь к получившемуся изображению.
        return $tmpFileName;
    }
}

Метод resizeImage() принимает два аргумента: $filePath (путь к файлу изображения) и/или $desiredWidth (ширина миниатюры). Внутри метода мы сначала вычисляем подходящую высоту миниатюры (строки 11-16), сохраняя соотношение сторон. Затем мы изменяем размер исходного изображения так, как нам требуется, и сохраняем его во временный файл (строки 19-32).

Как только класс ImageManager будет готов, нужно зарегистрировать сервис ImageManager в менеджере сервисов веб-приложения, добавив следующие строки в файл конфигурации module.config.php.

<?php
return [
    // ...    
    'service_manager' => [
        // ...
        'factories' => [
            // Регистрируем сервис ImageManager
            Service\ImageManager::class => InvokableFactory::class,            
        ],
    ],    
    // ...
];

10.8.4. Добавление ImageController

Теперь создадим класс контроллера ImageController для нашего примера Image Gallery. У контроллера будут следующие методы действия (перечислены в таблице 10.4):

Таблица 10.4. Методы действия класса ImageController.
Метод действия Описание
__construct() Позволит внедрить в контроллер зависимость ImageManager.
uploadAction() Показывает страницу выгрузки изображения, позволяющую выгрузить на сервер одно изображение.
indexAction() Отображает страницу галереи изображений со списком выгруженных изображений.
fileAction() Предоставляет возможность скачать изображение в полном размере либо его миниатюру.

Для начала создадим файл ImageController.php в каталоге Application/Controller под корневым каталогом модуля. Добавим в этот файл следующий код заглушки:

<?php
namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Application\Form\ImageForm;

// Этот контроллер предназначен для управления выгрузками файлов изображений на сервер.
class ImageController extends AbstractActionController 
{
    // Менеджер изображений.
    private $imageManager;
  
    // Метод конструктора используется для внедрения зависимостей
    // в контроллер.
    public function __construct($imageManager)
    {
        $this->imageManager = $imageManager;
    }
  
    // Это действие контроллера "index" (действие по умолчанию). Оно отображает
    // страницу Image Gallery, которая содержит список выгруженных изображений.
    public function indexAction() 
    {                
    }
    
    // Это действие показывает форму выгрузки изображений. Эта страница позволяет
    // выгрузить один файл на сервер.
    public function uploadAction() 
    {
    }
    
    // Это действие 'file', которое вызывается, когда пользователь хочет
    // открыть файл изображения в своем браузере или сгенерировать миниатюру.     
    public function fileAction() 
    {        
    }    
}

В этом фрагменте мы определили класс ImageController, который "живет" в пространстве имен Application\Controller, и добавили в этот класс метод конструктора и три заглушки метода действия: indexAction(), uploadAction() и fileAction(). Далее мы заполним кодом эти методы действия.

10.8.4.1. Добавление действия выгрузки и соответствующего шаблона представления

Первым делом мы заполним метод uploadAction() нашего контроллера. Этот метод действия будет обрабатывать страницу Upload a New Image, содержащую форму выгрузки на сервер. Форма будет предоставлять возможность выгружать файл изображения в галерею.

Измените файл ImageController.php таким образом:

<?php
//...
class ImageController extends AbstractActionController 
{
    //...
    public function uploadAction() 
    {
        // Создаем модель формы.
        $form = new ImageForm();
        
        // Проверяем, отправил ли пользователь форму.
        if($this->getRequest()->isPost()) {
            
            // Обязательно объедините информацию о файлах!
            $request = $this->getRequest();
            $data = array_merge_recursive(
                $request->getPost()->toArray(),
                $request->getFiles()->toArray()
            );
            
            // Передаем данные форме.
            $form->setData($data);
            
            // Валидируем форму.
            if($form->isValid()) {
                
                // Перемещаем выгруженный файл в его каталог назначения.
                $data = $form->getData();
                
                // Перенаправляем пользователя на страницу "Image Gallery".
                return $this->redirect()->toRoute('images');
            }                        
        } 
        
        // Визуализируем страницу.
        return new ViewModel([
                     'form' => $form
                 ]);
    }
}

В описанном выше методе uploadAction() мы далем следующее.

В строке 9 мы создаем экземпляр модели формы ImageForm с помощью оператора new.

В строке 12 мы проверяем, является ли запрос HTTP-запросом методом POST. Если это так, мы получаем данные из суперглобальных массивов $_POST и $_FILESи объединяем их в один массив (строки 15-19). Это необходимо для корректной обработки выгруженных на сервер файлов, если таковые имеются. Затем мы передаем этот массив модели формы с помощью метода `setData() (строка 22).

В строке 25 мы вызываем метод модели формы isValid(). Этот метод запускает фильтр входных данных, присоединенный к модели формы. Так как в этом фильтре у нас только один файловый вход, запустится только три валидатора файлов: UploadFile, IsImage и ImageSize.

Если данные действительны, мы вызываем метод getData() (строка 28). Для нашего поля файла, он запустит фильтр RenameUpload, который перемещает выгруженный на сервер файл в его постоянный каталог.

После этого, в строке 31, мы перенаправляем пользователя к "index"-действию контроллера. (мы заполним этот метод действия немного позже).

Теперь пора добавить шаблон представления для действия "upload". Добавьте шаблон представления upload.phtml под каталогом application/image под каталогом модуля view.

<?php
$form = $this->form;
$form->get('submit')->setAttributes(['class'=>'btn btn-primary']);
$form->prepare();
?>

<h1>Upload a New Image</h1>

<p>
    Пожалуйста, заполните следующую форму и нажмите кнопку <i>Upload</i>.
</p>

<div class="row">
    <div class="col-md-6">
        <?= $this->form()->openTag($form); ?>
        
        <div class="form-group">
            <?= $this->formLabel($form->get('file')); ?>
            <?= $this->formElement($form->get('file')); ?>
            <?= $this->formElementErrors($form->get('file')); ?> 
            <div class="hint">(PNG and JPG formats are allowed)</div>
        </div>
                
        <?= $this->formElement($form->get('submit')); ?>
        
        <?= $this->form()->closeTag(); ?>
    </div>    
</div>    

В коде шаблона представления мы сперва задаем атрибут "class" (строка 3). Это нужно, чтобы применить красивые стили Twitter Bootstrap к кнопке отправки формы Submit.

Затем мы визуализируем форму с помощью общих помощников видов, которые мы обсудили в Cбор пользовательских данных с помощью форм. Для визуализации поля "file" мы используем общий помощник вида FormElement.

Как правило, помощник вида FormElement используется для визуализации поля файла. FormElement внутренне вызывает помощник вида FormFile, который выполняет фактическую визуализацию.

10.8.4.2. Добавление index действия и соответствующего шаблона представления

Следующий метод действия, который мы заполним - indexAction(). Это действие будет обрабатывать страницу Image Gallery, содержащую список выгруженных на сервер файлов и их миниатюры. Для каждого изображения будет кнопка "Show In Natural Size" (показать в исходном размере) для открытия изображения в новой вкладке браузера.

Измените файл ImageController.php, как показано ниже:

<?php
//...
class ImageController extends AbstractActionController 
{
    //...
    public function indexAction() 
    {
        // Получаем список уже сохраненных файлов.
        $files = $this->imageManager->getSavedFiles();
        
        // Визуализируем шаблон представления.
        return new ViewModel([
            'files'=>$files
        ]);
    }
}

Во фрагменте выше мы используем метод getSavedFiles() класса ImageManager для извлечения списка выгруженных на сервер файлов и передаем их представлению для визуализации.

Заметьте, насколько это действие контроллера "тонкое" и понятное! Мы добились этого, переместив набор функций для управления изображениями в модель сервиса ImageManager.

Добавьте шаблон представления index.phtml в каталог application/image под каталогом модуля view. Содержимое файла показано ниже:

<h1>Image Gallery</h1>
 
<p>
    Эта страница отображает список выгруженных изображений.
</p>

<p>
    <a href="<?= $this->url('images', ['action'=>'upload']); ?>" 
        class="btn btn-primary" role="button">Upload More</a>
</p>

<hr/>

<?php if(count($files)==0): ?>

<p>
  <i>Нет файлов для отображения.</i>
</p>

<?php else: ?>

<div class="row">
    <div class="col-sm-6 col-md-12">

        <?php foreach($files as $file): ?>  

        <div class="img-thumbnail">
                
            <img src="<?= $this->url('images', ['action'=>'file'], 
                ['query'=>['name'=>$file, 'thumbnail'=>true]]); ?>">
                
            <div class="caption">
                <h3><?php echo $file; ?></h3>                    
                <p>
                <a target="_blank" href="<?= $this->url('images', ['action'=>'file'], 
                    ['query'=>['name'=>$file]]); ?>" 
                    class="btn btn-default" role="button">Show in Natural Size</a>
                </p>
            </div>
        </div>

        <?php endforeach; ?>
    </div>
</div>

<?php endif; ?>

<hr/>

В этом фрагменте кода мы создаем HTML-разметку для кнопки Upload More.

Далее мы проверяем, является ли массив $files пустым. Если он пустой, мы выводим сообщение "Нет файлов для отображения"; иначе мы проходим по файлам и выводим миниатюры всех выгруженных на сервер изображений.

Для визуализации миниатюры мы используем тег <img>. Мы задаем его атрибут src с помощью URL, указывающего на действие "file" нашего контроллера ImageController. Через часть запроса URL действию передаются два параметра: имя изображения и флаг миниатюры.

Для стилизации миниатюр мы используем предоставляемый Twitter Bootstrap CSS-класс ".img-thumbnail".

За дополнительной информацией о стилях Twitter Bootstrap, пожалуйста, обратитесь к официальной дкоументации Bootstrap.

Под каждой миниатюрой мы размещаем ссылку "Show in Natural Size" (показать в исходном размере), которая направляет к действию "file" контроллера ImageController Когда посетитель сайта нажимает на ссылку, ему показывается изображение в исходном размере, и это изображение будет открыто в другой вкладке (обратите внимание на атрибут ссылки `target="_blank").

10.8.4.3. Добавление действия file

Последнее действие, которое мы заполним - это метод ImageController::fileAction(). Этот метод позволит предварительно просматривать выгруженное на сервер изображение или генерировать его миниатюру. Метод действия будет принимать два GET-параметра:

Измените файл ImageController.php следующим образом:

 <?php
//...
class ImageController extends AbstractActionController 
{
    //...
    public function fileAction() 
    {
        // Получаем имя файла из GET-переменной.
        $fileName = $this->params()->fromQuery('name', '');

        // Проверяем, что нужно пользователю: изображение в полном размере или миниатюра.
        $isThumbnail = (bool)$this->params()->fromQuery('thumbnail', false);
    
        // Получаем путь к файлу изображения.
        $fileName = $this->imageManager->getImagePathByName($fileName);
        
        if($isThumbnail) {
        
            // Изменяем размер изображения.
            $fileName = $this->imageManager->resizeImage($fileName);
        }
                
        // Получаем информацию файла изображения (размер и MIME-тип).
        $fileInfo = $this->imageManager->getImageFileInfo($fileName);        
        if ($fileInfo===false) {
            // Устанавливаем код состояния 404 Not Found
            $this->getResponse()->setStatusCode(404);            
            return;
        }
                
        // Запсиываем HTTP-заголовки.
        $response = $this->getResponse();
        $headers = $response->getHeaders();
        $headers->addHeaderLine("Content-type: " . $fileInfo['type']);        
        $headers->addHeaderLine("Content-length: " . $fileInfo['size']);
            
        // Записываем содержимое файла.
        $fileContent = $this->imageManager->getImageFileContent($fileName);
        if($fileContent!==false) {                
            $response->setContent($fileContent);
        } else {        
            // Устанавливаем код состояния 500 Server Error.
            $this->getResponse()->setStatusCode(500);
            return;
        }
        
        if($isThumbnail) {
            // Удаляем временный файл миниатюры изображения.
            unlink($fileName);
        }
        
        // Возвращаем экземпляр Response, чтобы избежать визуализации представления по умолчанию.
        return $this->getResponse();
    }    
}

В этом фрагменте мы первым делом получаем параметры "name" и "thumbnail" из суперглобального массива $_GET (строки 9, 12). Если параметры отсутствуют, вместо них используются значения по умолчанию.

В строке 15 мы используем метод getImagePathByName(), предоставляемый сервисом ImageManager, чтобы получить абсолютный путь к изображению по его имени.

Если запрашивается миниатюра, мы изменяем размер изображения с помощью метода ImageManager resizeImage() (строка 20). Этот метод возвращает путь к временному файлу, содержащему миниатюру.

Затем мы получаем информацию о файле изображения (его MIME-тип и размер файла) с помощью метода ImageManager getImageFileInfo() (строка 24).

И наконец, мы создаем объект Response, заполняем его заголовки информацией об изображении, задаем его содержимое данными файла изображения (строки 32-45) и возвращаем объект Response из действия контроллера (строка 53).

Обратите внимание, что возврат объекта Response отключает визуализацию шаблона представления по умолчанию для этого метода действия. По этой причине мы не создаем файл шаблона представления file.phtml.

10.8.4.4. Создание фабрики для контроллера

Так как ImageController использует сервис ImageManager, нам нужно как-то передать ему экземпляр ImageManager (для внедрения зависимости в конструктор контроллера). Мы сделаем это с помощью фабрики.

Создайте файл ImageControllerFactory.php под подкаталогом Controller/Factory под корневым каталогом модуля. Поместите в этот файл следующий код:

<?php
namespace Application\Controller\Factory;

use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use Application\Service\ImageManager;
use Application\Controller\ImageController;

/**
 * Это фабрика для ImageController. Ее целью является инстанцирование
 * контроллера.
 */
class ImageControllerFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, 
                        $requestedName, array $options = null)
    {
        $imageManager = $container->get(ImageManager::class);
        
        // Инстанцируем контроллер и внедряем зависимости
        return new ImageController($imageManager);
    }
}

10.8.4.5. Регистрация ImageController

Чтобы дать ZF3 знать о нашем контроллере, зарегистрируем ImageController в файле конфигурации module.config.php:

<?php
return [
    //...
    'controllers' => [
        'factories' => [
            Controller\ImageController::class => 
                    Controller\Factory\ImageControllerFactory::class,
            //...
        ],
    ],    
    //...
];

10.8.4.6. Создание маршрута

Также нам нужно добавить маршрут для контроллера ImageController. Для этого просто измените файл module.config.php следующим образом:

<?php
return [
    //...
    'router' => [
        'routes' => [
            'images' => [
                'type'    => Segment::class,
                'options' => [
                    'route'    => '/images[/:action]',
                    'constraints' => [
                        'action' => '[a-zA-Z][a-zA-Z0-9_-]*'
                    ],
                    'defaults' => [
                        'controller'    => Controller\ImageController::class,
                        'action'        => 'index',
                    ],
                ],
            ],
        ],
    ],    
    //...
];

После этого вы сможете обратиться к нашей галереи изображений по URL "http://localhost/images", "http://localhost/images/upload" или "http://localhost/images/file".

10.8.5. Результаты

В заключение, отрегулируем права доступа к каталогу, чтобы он был доступным для записи для веб-сервера Apache. В Linux Ubuntu это, как правило, совершается следующими командами оболочки (замените плейсхолдер APP_DIR именем каталога вашего веб-приложения):

chown -R www-data:www-data APP_DIR/data

chmod -R 775 APP_DIR/data

Приведенные выше команды соответственно назначают пользователя Apache владельцем каталога и позволяют веб-серверу записывать данные в каталог.

Теперь, если вы введете URL http://localhost/images в адресную строку браузера, вы увидите страницу галереи изображений, как показано на рисунке 10.4.

Рисунок 10.4. Страница галереи изображений Рисунок 10.4. Страница галереи изображений

Нажатие на кнопку Upload More откроет страницу Upload a New Image, где вы можете выбрать файл изображения для выгрузки на сервер. Если вы выберете недопустимый файл (не являющийся изображением, либо слишком большое изображение), вы увидите ошибки валидации (см. рисунок 10.5 ниже).

Рисунок 10.5. Ошибки валидации файла Рисунок 10.5. Ошибки валидации файла

Если выгрузка файла на сервер завершилась удачно, вы будете перенаправлены обратно на страницу Image Gallery и увидите выгруженное изображение в списке миниатюр. Нажатие на кнопку View Full Size откроет изображение в новой вкладке браузера (см. рисунок 10.6 ниже).

Figure 10.6. Opening an Image in Natural Size Figure 10.6. Opening an Image in Natural Size

Пример галереи изображений Image Gallery можно найти в приложении Form Demo, которое идет вместе с этой книгой.


Top