Загрузка файлов
Загрузка файла — это поле формы, через которое посетитель отправляет картинку или документ. 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.