我有一个DTO类,如下所示:
class ParamsDto
{
#[Assert\Type(ArrayCollection::class)]
#[Assert\All([
new Assert\Type('digit'),
new Assert\Positive(),
])]
private ?ArrayCollection $tagIds = null;
public function getTagIds(): ?ArrayCollection
{
return $this->tagIds;
}
public function setTagIds(?ArrayCollection $tagIds): self
{
$this->tagIds = $tagIds;
return $this;
}
}
给定一个对URL(如https://domain.php?tag-ids[]=2
)的请求,我希望将tag-ids
请求参数解析为该DTO的tagIds
属性。
第一步,我创建了一个名称转换器,这样我就可以在tag-ids
和tagIds
之间进行转换,所以我的序列化器示例化如下所示:
$nameConverter = new EducationEntrySearchParamNameConverter();
$serializer = new Serializer([
new ArrayDenormalizer(),
new ObjectNormalizer(null, $nameConverter, null, new ReflectionExtractor()),
], [new JsonEncoder()]);
$params = $serializer->denormalize($requestParams, ParamsDto::class);
其中$params
显示为:
^ App\DataTransferObject\ParamsDto {#749
-tagIds: Doctrine\Common\Collections\ArrayCollection {#753
-elements: []
}
}
因此,它已示例化,但为空。
很可能是因为我的请求中没有包含elements
键。如果我做一些预处理,比如:
$requestParams = [];
foreach ($request->query->all() as $key => $value) {
if (in_array($key, ['tag-ids'])) {
$requestParams[$key] = ['elements' => $value];
} else {
$requestParams[$key] = $value;
}
}
$params = $serializer->denormalize($requestParams, ParamsDto::class);
然后我得到正确的输出:
^ App\DataTransferObject\ParamsDto {#749
-tagIds: Doctrine\Common\Collections\ArrayCollection {#757
-elements: array:1 [
0 => "2"
]
}
}
如何才能让序列化程序将请求转换为DTO,而不必执行此预处理?
L.E:不需要使用自定义名称转换器,我现在使用SerializedName
2条答案
按热度按时间yyhrrdl81#
简短回答:我强烈建议用一个标准的php内置
array
来代替这个集合--至少在setter上是这样!--我打赌这会有帮助。道长回答:
我的猜测是,由于您的DTO不是一个实体,序列化程序不会使用一些与理论相关的东西,这些东西会告诉它将数组转换为集合。
恕我直言,集合是对象内部的解决方案,不应该直接暴露给外部,与外部世界的接口是数组。可能非常固执己见。
从非原则序列化程序的Angular 来看,集合是一个对象。对象具有属性。要将数组Map到对象上,需要使用数组的键,并使用反射将它们与对象的属性相匹配:直接使用属性,如果它是公共的,则使用加法器/移除器,否则使用setter/getter。(按IIRC顺序)-这就是为什么将
elements
作为键添加到带有id数组的输入中“工作”的原因。使用数组作为setter/getter对中的接口将极大地帮助property accessor只设置数组。如果你想要/需要的话,你仍然可以在内部将该属性作为ArrayCollection。
这只是一种猜测:在setter中添加docblock类型提示
/** @param array<int> $tagIds */
甚至可能触发从"2"
到2
的转换。用php类型提示代替addTagId
+removeTagId
+getTagIds
也可能起作用。axr492tv2#
我会加上这一点,以帮助其他人挣扎与同样的问题,但我能够想出它后,@雅库米的答案,这也是为什么我要奖励他们的赏金。
因此,经过一些研究后,似乎确实最简单的方法是使用array作为类型提示。这样,事情就可以开箱即用,而不需要做任何其他事情,但您不会使用集合,因此:
但是,如果您希望继续使用集合,则需要做一些额外的工作:
因此,除了setter和getter之外,还需要添加加法器和移除器。
这将反序列化为整数的ArrayCollection。
另外,如果您的对象属性是DTO的集合,而不仅仅是整数,并且您还希望对这些属性进行反规范化,则可以执行以下操作:
这会将tags属性反规范化为ArrayCollection示例(TagCollection扩展了它),在这种情况下,集合中的每一项都将是TagDto。