Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Загрузка файлов

Загрузка файла — это поле формы, через которое посетитель отправляет картинку или документ. Laminas принимает файл во временную папку, проверяет его по вашим правилам и переносит туда, куда вы укажете. Добавим к статье картинку.

Подготовка

Создайте папку public/uploads — в неё будут попадать файлы. Добавьте в таблицу столбец для имени файла:

ALTER TABLE posts ADD image VARCHAR(255) NULL;

В классе Post добавьте свойство public ?string $image = null; и строку $this->image = $data['image'] ?? null; в exchangeArray(), а в методе PostTable::save() — ключ 'image' => $post->image в массив $data.

Поле формы

Добавьте элемент в конструктор PostForm:

// module/Blog/src/Form/PostForm.php
$this->add([
    'name' => 'image',
    'type' => Element\File::class,
    'options' => ['label' => 'Картинка'],
]);

В шаблоне add.phtml выведите его строкой <?= $this->formRow($form->get('image')) ?>. Атрибут enctype="multipart/form-data", без которого браузер не отправит файл, форма добавит сама при вызове prepare().

Правила для файла

В getInputFilterSpecification() у файла особый тип ввода — FileInput:

// module/Blog/src/Form/PostForm.php
use Laminas\Filter\File\RenameUpload;
use Laminas\InputFilter\FileInput;
use Laminas\Validator\File\Extension;
use Laminas\Validator\File\Size;
 
'image' => [
    'type' => FileInput::class,
    'required' => false,
    'validators' => [
        ['name' => Size::class, 'options' => ['max' => '2MB']],
        ['name' => Extension::class, 'options' => ['extension' => ['jpg', 'jpeg', 'png']]],
    ],
    'filters' => [
        [
            'name' => RenameUpload::class,
            'options' => [
                'target' => './public/uploads/post',
                'randomize' => true,
                'use_upload_extension' => true,
            ],
        ],
    ],
],

Для FileInput порядок обратный: сначала валидаторы, затем фильтры. Фильтр RenameUpload переносит файл из временной папки в public/uploads. С randomize имя получает случайную добавку — например, post_64a1f3c2.jpg, — поэтому два файла с одинаковым именем не затрут друг друга.

Обработка в контроллере

Файлы приходят отдельно от остальных полей, поэтому перед setData() их объединяют:

// module/Blog/src/Controller/PostController.php
if ($request->isPost()) {
    $form->setData(array_merge_recursive(
        $request->getPost()->toArray(),
        $request->getFiles()->toArray(),
    ));
 
    if ($form->isValid()) {
        $data = $form->getData();
 
        $post = new Post();
        $post->exchangeArray($data);
        $post->image = empty($data['image']['tmp_name']) ? null : basename($data['image']['tmp_name']);
 
        $this->posts->save($post);
 
        return $this->redirect()->toRoute('blog/view', ['id' => $post->id]);
    }
}

После проверки $data['image'] — массив с описанием файла, а tmp_name в нём — уже новый путь в public/uploads. В базу сохраняется только имя файла.

Показ картинки

<!-- module/Blog/view/blog/post/view.phtml -->
<h1><?= $this->escapeHtml($this->post->title) ?></h1>
 
<?php if ($this->post->image !== null): ?>
    <img src="<?= $this->basePath('uploads/' . $this->post->image) ?>" alt="">
<?php endif; ?>

Добавьте статью с картинкой через /blog/add — на странице статьи она появится под заголовком.

Частая ошибка

Нельзя сохранять файл под именем, которое прислал посетитель, и без проверки расширения: так на сервер попадёт файл shell.php, который затем откроют по ссылке и выполнят. Всегда ограничивайте расширения валидатором Extension и давайте файлам свои имена через RenameUpload с randomize.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.