Proposed new feature or change
FormHydrator::populateFromPost() и populateFromPostAndValidate() сейчас берут только $request->getParsedBody(), поэтому UploadedFileInterface-поля (включая nested/collection формы) не гидрируются без ручного merge POST + FILES.
Нужна доработка “из коробки” для файловой гидрации (в т.ч. вложенных структур), с корректной фильтрацией UPLOAD_ERR_NO_FILE (иначе в модель попадают “пустые” файлы).
Сейчас обходной путь такой:
$post = array_merge_recursive(
$request->getParsedBody(),
UploadedFilesFilter::filter($request->getUploadedFiles())
);
$this->formHydrator->populateAndValidate($form, $post);
<?php
declare(strict_types=1);
namespace App;
use Psr\Http\Message\UploadedFileInterface;
final class UploadedFilesFilter
{
/**
* @param array<array-key, mixed> $uploadedFiles
*
* @return array<array-key, mixed>
*/
public static function filter(array $uploadedFiles): array
{
$result = [];
foreach ($uploadedFiles as $key => $value) {
if ($value instanceof UploadedFileInterface) {
if ($value->getError() === UPLOAD_ERR_NO_FILE) {
continue;
}
$result[$key] = $value;
continue;
}
if (is_array($value)) {
$filtered = self::filter($value);
if ($filtered !== []) {
$result[$key] = $filtered;
}
continue;
}
$result[$key] = $value;
}
return $result;
}
}
Пример формы с вложенными изображениями
use Psr\Http\Message\UploadedFileInterface;
use Yiisoft\FormModel\FormModel;
use Yiisoft\Hydrator\Attribute\Parameter\Collection;
use Yiisoft\Validator\Label;
use Yiisoft\Validator\Rule\Each;
use Yiisoft\Validator\Rule\Image\Image;
use Yiisoft\Validator\Rule\Nested;
use Yiisoft\Validator\Rule\Length;
final class Form extends FormModel
{
#[Label('Title')]
#[Length(min: 2)]
public string $title = '';
#[Label('Picture')]
#[Image(skipOnEmpty: true)]
public ?UploadedFileInterface $picture = null;
#[Label('Nested picture')]
#[Nested(PictureForm::class)]
public ?PictureForm $nestedPicture = null;
/** @var array<UploadedFileInterface> */
#[Label('Gallery')]
#[Each([new Image(skipOnEmpty: true)])]
public array $pictures = [];
/** @var array<PictureForm> */
#[Collection(PictureForm::class)]
#[Label('Nested gallery')]
#[Each([new Nested(PictureForm::class)])]
public array $nestedPictures = [];
}
final class PictureForm extends FormModel
{
#[Label('Description')]
#[Length(min: 0)]
public string $description = '';
#[Label('Picture')]
#[Image(skipOnEmpty: true)]
public ?UploadedFileInterface $picture = null;
/** @var array<NestedPictureForm> */
#[Collection(NestedPictureForm::class)]
#[Label('Nested gallery level 2')]
#[Each([new Nested(NestedPictureForm::class)])]
public array $nestedPictures = [];
}
final class NestedPictureForm extends FormModel
{
#[Label('Description')]
#[Length(min: 0)]
public string $description = '';
#[Label('Picture')]
#[Image(skipOnEmpty: true)]
public ?UploadedFileInterface $picture = null;
}
Input names
Form[title]
Form[picture]
Form[pictures][]
Form[nestedPicture][description]
Form[nestedPicture][picture]
Form[nestedPictures][0][description]
Form[nestedPictures][0][picture]
Form[nestedPictures][0][nestedPictures][0][description]
Form[nestedPictures][0][nestedPictures][0][picture]
Ожидание
Встроенный механизм в FormHydrator (новые методы или флаг в populateFromPost*), чтобы ручной merge/фильтр не дублировались в каждом action.
Proposed new feature or change
FormHydrator::populateFromPost()иpopulateFromPostAndValidate()сейчас берут только$request->getParsedBody(), поэтомуUploadedFileInterface-поля (включая nested/collection формы) не гидрируются без ручного mergePOST + FILES.Нужна доработка “из коробки” для файловой гидрации (в т.ч. вложенных структур), с корректной фильтрацией
UPLOAD_ERR_NO_FILE(иначе в модель попадают “пустые” файлы).Сейчас обходной путь такой:
Пример формы с вложенными изображениями
Input names
Form[title]Form[picture]Form[pictures][]Form[nestedPicture][description]Form[nestedPicture][picture]Form[nestedPictures][0][description]Form[nestedPictures][0][picture]Form[nestedPictures][0][nestedPictures][0][description]Form[nestedPictures][0][nestedPictures][0][picture]Ожидание
Встроенный механизм в
FormHydrator(новые методы или флаг вpopulateFromPost*), чтобы ручной merge/фильтр не дублировались в каждом action.