我有一個 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 的方式來執行此操作?
LE:不需要使用自定義名稱轉換器,我現在正在使用SerializedName
uj5u.com熱心網友回復:
簡短回答:我強烈建議只用內置的標準 php 替換集合array——至少在 setter 上是這樣!-,我敢打賭這會有所幫助。
長答案:
我的猜測是,由于您的 DTO 不是物體,因此序列化程式不會使用與原則相關的東西,這會告訴它將陣列轉換為集合。
恕我直言,集合是物件內部解決方案,永遠不應直接暴露給外部,與外界的介面是陣列。可能很有主見。
從非 Doctrine Serializer 的角度來看,集合是一個物件。物件具有屬性。將陣列映射到物件是使用陣列的鍵并將它們與物件的屬性匹配,使用反射:直接使用屬性,如果它是公共的,否則使用加法器/移除器,否則使用設定器/獲取器。(IIRC 按此順序)-這就是為什么將elementsid 陣列作為鍵添加到您的輸入中“有效”。
使用陣列作為 setter/getter 對中的介面將極大地幫助屬性訪問器僅設定陣列。如果您想要/需要,您仍然可以在內部將屬性作為 ArrayCollection。
這只是一個猜測:向/** @param array<int> $tagIds */您的 setter 添加 docblock 型別提示甚至可能觸發"2"從2. 用addTagId removeTagId getTagIds代替 php 型別提示也可能會起作用。
uj5u.com熱心網友回復:
我添加這個是為了幫助其他遇到同樣問題的人,但只有在@Jakumi 的回答后我才能想出它,這也是我要獎勵他們的原因。
因此,經過一些研究,似乎確實,最簡單的方法就是使用陣列作為型別提示。這樣,事情就會開箱即用,你不需要做任何其他事情,但你不會使用集合,所以:
#[Assert\Type('array')]
#[Assert\All([
new Assert\Type('numeric'),
new Assert\Positive(),
])]
#[SerializedName('tag-ids')]
private ?array $tagIds = null;
public function getTagIds(): ?array
{
return $this->tagIds;
}
public function setTagIds(?array $tagIds): self
{
$this->tagIds = $tagIds;
return $this;
}
但是,如果你想繼續使用集合,那么你需要做一些額外的作業:
#[Assert\Type(ArrayCollection::class)]
#[Assert\All([
new Assert\Type('numeric'),
new Assert\Positive(),
])]
#[SerializedName('tag-ids')]
private ?ArrayCollection $tagIds = null;
public function getTagIds(): ?ArrayCollection
{
return $this->tagIds;
}
public function setTagIds(array|ArrayCollection|null $tagIds): self
{
if (null !== $tagIds) {
$tagIds = $tagIds instanceof ArrayCollection ? $tagIds : new ArrayCollection($tagIds);
}
$this->tagIds = $tagIds;
return $this;
}
// Make sure our numeric values are treated as integers not strings
public function addTagId(int $tagId): void
{
if (null === $this->getTagIds()) {
$this->setTagIds(new ArrayCollection());
}
$this->getTagIds()?->add($tagId);
}
public function removeTagId(int $tagId): void
{
$this->getTagIds()?->remove($tagId);
}
所以除了 setter 和 getter 之外,您還需要添加一個加法器和一個移除器。
這將反序列化為整數的 ArrayCollection。
獎勵,如果您的物件屬性是 DTO 的集合,而不僅僅是整數,并且您也想對它們進行非規范化,那么您可以執行以下操作:
#[Assert\Type(TagCollection::class)]
#[Assert\All([
new Assert\Type(TagDto::class),
])]
#[Assert\Valid]
private ?TagCollection $tags = null;
private PropertyAccessorInterface $propertyAccessor;
public function __construct()
{
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->disableExceptionOnInvalidIndex()
->disableExceptionOnInvalidPropertyPath()
->getPropertyAccessor()
;
}
public function getTags(): ?TagCollection
{
return $this->tags;
}
public function setTags(array|TagCollection|null $tags): self
{
if (null !== $tags) {
$tags = $tags instanceof TagCollection ? $tags : new TagCollection($tags);
}
$this->tags = $tags;
return $this;
}
public function addTag(array $tag): void
{
if (null === $this->getTags()) {
$this->setTags(new TagCollection());
}
$tagDto = new TagDto();
foreach ($tag as $key => $value) {
$this->propertyAccessor->setValue($tagDto, $key, $value);
}
$this->getTags()?->add($tagDto);
}
public function removeTag(TagDto $tag): void
{
$this->getTags()?->remove($tag);
}
這會將 tags 屬性非規范化為 ArrayCollection 實體(TagCollection 擴展它),并且集合中的每個專案在這種情況下都是 TagDto。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/533879.html
