在 api 平台中验证嵌入式模型

validate embedded model in api platform

我有这个 Order 型号:

<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Validator\Constraints as Assert;
/**
 * @ApiResource(
 *     itemOperations={
 *          "get"={
 *              "normalization_context"={
 *                  "groups"={"order:view"}
 *              }
 *          },
 *          "patch"={
 *              "normalization_context"={
 *                  "groups"={"order:view"}
 *              },
 *              "denormalization_context"={
 *                  "groups"={"upsert"}
 *              }
 *          },
 *          "delete"
 *     },
 *     collectionOperations={
 *          "get"={
 *              "normalization_context"={
 *                  "groups"={"order:index"}
 *              }
 *          },
 *          "post"={
 *              "normalization_context"={
 *                  "groups"={"order:view"}
 *              },
 *              "denormalization_context"={
 *                  "groups"={"order:create"}
 *              }
 *          }
 *     }
 * )
 * @ORM\Entity(repositoryClass="App\Repository\OrderRepository")
 * @ORM\Table(name="`order`")
 */
class Order
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"order:index", "order:view"})
     */
    private $id;
    /**
     * @ORM\Column(type="integer")
     * @Assert\PositiveOrZero()
     * @Groups({"order:index", "order:view"})
     */
    private $amount;
    /**
     * @ORM\OneToMany(targetEntity="App\Entity\OrderItem", mappedBy="orderId")
     * @Groups({"order:index", "order:view", "order:create"})
     */
    private $orderItems;
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Merchant", inversedBy="orders")
     * @ORM\JoinColumn(nullable=false)
     * @Groups({"order:index", "order:view"})
     */
    private $merchant;
    /**
     * Order constructor.
     */
    public function __construct()
    {
        $this->orderItems = new ArrayCollection();
    }
    /**
     * @return string
     */
    public function __toString(): string {
        return $this->id;
    }
    /**
     * @return int|null
     */
    public function getId(): ?int
    {
        return $this->id;
    }
    /**
     * @return int|null
     */
    public function getAmount(): ?int
    {
        return $this->amount;
    }
    /**
     * @param int $amount
     * @return Order
     */
    public function setAmount(int $amount): self
    {
        $this->amount = $amount;
        return $this;
    }
    /**
     * @Assert\Valid
     */
    public function getOrderItems()
    {
        return $this->orderItems->getValues();
    }
    /**
     * @param OrderItem $orderItem
     * @return Order
     */
    public function addOrderItem(OrderItem $orderItem): self
    {
        if (!$this->orderItems->contains($orderItem)) {
            $this->orderItems[] = $orderItem;
            $orderItem->setOrderId($this);
        }
        return $this;
    }
    /**
     * @param OrderItem $orderItem
     * @return Order
     */
    public function removeOrderItem(OrderItem $orderItem): self
    {
        if ($this->orderItems->contains($orderItem)) {
            $this->orderItems->removeElement($orderItem);
            // set the owning side to null (unless already changed)
            if ($orderItem->getOrderId() === $this) {
                $orderItem->setOrderId(null);
            }
        }
        return $this;
    }
    /**
     * @return Merchant|null
     */
    public function getMerchant(): ?Merchant
    {
        return $this->merchant;
    }
    /**
     * @param Merchant|null $merchant
     * @return Order
     */
    public function setMerchant(?Merchant $merchant): self
    {
        $this->merchant = $merchant;
        return $this;
    }
}

和这个 OrderItem 模型

<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\SerializedName;
use Symfony\Component\Validator\Constraints as Assert;
/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass="App\Repository\OrderItemRepository")
 */
class OrderItem
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"view", "order:view", "order:index"})
     */
    private $id;
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Product")
     * @ORM\JoinColumn(nullable=false)
     * @SerializedName("product")
     * @Assert\NotBlank()
     * @Groups({"view", "order:view", "order:index", "order:create"})
     */
    private $productId;
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Order", inversedBy="orderItems")
     * @SerializedName("order")
     * @ORM\JoinColumn(nullable=false)
     */
    private $orderId;
    /**
     * @ORM\Column(type="integer")
     * @Assert\NotBlank()
     * @Assert\PositiveOrZero()
     * @Groups({"view", "order:view", "order:index", "order:create"})
     */
    private $num;
    /**
     * @ORM\Column(type="integer")
     * @SerializedName("total_amount")
     * @Assert\PositiveOrZero()
     * @Groups({"view", "order:view", "order:index"})
     */
    private $totalAmount;
    /**
     * @return string
     */
    public function __toString(): string {
        return $this->id;
    }
    /**
     * @return int|null
     */
    public function getId(): ?int
    {
        return $this->id;
    }
    /**
     * @return Product|null
     */
    public function getProductId(): ?Product
    {
        return $this->productId;
    }
    /**
     * @param Product|null $productId
     * @return OrderItem
     */
    public function setProductId(?Product $productId): self
    {
        $this->productId = $productId;
        return $this;
    }
    /**
     * @return Order|null
     */
    public function getOrderId(): ?Order
    {
        return $this->orderId;
    }
    /**
     * @param Order|null $orderId
     * @return OrderItem
     */
    public function setOrderId(?Order $orderId): self
    {
        $this->orderId = $orderId;
        return $this;
    }
    /**
     * @return int|null
     */
    public function getNum(): ?int
    {
        return $this->num;
    }
    /**
     * @param int $num
     * @return OrderItem
     */
    public function setNum(int $num): self
    {
        $this->num = $num;
        return $this;
    }
    /**
     * @return int|null
     */
    public function getItemAmount(): ?int
    {
        return $this->itemAmount;
    }
    /**
     * @param int $itemAmount
     * @return OrderItem
     */
    public function setItemAmount(int $itemAmount): self
    {
        $this->itemAmount = $itemAmount;
        return $this;
    }
    /**
     * @return int|null
     */
    public function getTotalAmount(): ?int
    {
        return $this->totalAmount;
    }
    /**
     * @param int $totalAmount
     * @return OrderItem
     */
    public function setTotalAmount(int $totalAmount): self
    {
        $this->totalAmount = $totalAmount;
        return $this;
    }
}

我正在尝试使用嵌入的嵌套项目而不是项目 IRI 在单个 post 请求中创建一个包含其项目的订单。 正如文档所建议的那样,我在 Order 模型中为 orderItems getter 方法添加了 @Assert\Valid 。但它不会在 post 请求中验证我的 orderItem 数据。

我可以解决它。
Valid 约束仅验证嵌入式模型本身,因此如果您不为嵌入式模型提供密钥或仅设置一个空数组,验证将不执行任何操作。
所以你需要为属性本身添加另一个验证。
在这种情况下,因为我希望我的 orderItems 包含至少一项,所以我为此 属性 添加了 Count 验证约束,如下所示。

/**
 * @Assert\Count(
 *      min = "1",
 *      minMessage = "You must specify at least one OrderItem"
 * )
 * @Assert\Valid()
 */
public function getOrderItems()
{
    return $this->orderItems;
}

所以它强制请求包含列表中的第一个 OrderItem