FormHydrator: гидрация UploadedFileInterface из POST+FILES (включая nested forms)
Nobody has claimed this yet.
- Dominant language
- PHP
- Stars
- 17
- Forks
- 7
- Avg merge
- 58m
- Merged PRs (30d)
- 1
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in FormHydrator::populateFromPost() and populateFromPostAndValidate(), tracing how getParsedBody() is used and where getUploadedFiles() can be combined. Verify the result with the nested form and collection structures described in the issue; done means UploadedFileInterface fields hydrate from POST plus FILES at every shown nesting level while UPLOAD_ERR_NO_FILE entries are excluded.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100