更新API Platform 3.0相关实体

问题描述 投票:0回答:2

我正在尝试通过主实体更新相关实体。例如,我有产品和优惠。我做一个

POST
/products

{
    "name": "sample product",
    "offers": [
        {
            "description": "sample offer"
        }
    ]
}

我得到了这个回应

{
    "@context": "/contexts/Product",
    "@id": "/products/1",
    "@type": "Product",
    "name": "sample product",
    "offers": [
        {
            "@id": "/offers/1",
            "@type": "https://schema.org/Offer",
            "description": "sample offer"
        }
    ]
}

到目前为止,一切都很好。现在,我想在

PUT
 执行 
/products/1

操作
{
    "name": "product updated",
    "offers": [
        {
            "@id": "/offers/1",
            "description": "offer updated"
        }
    ]
}

并收到此错误

执行查询时发生异常:SQLSTATE[23000]: 违反完整性约束:1048 列“product_id”不能 空

如果我从 Offer.php 中删除

#[ORM\JoinColumn(nullable:false)]
,它可以工作,但是
product_id
设置为
null
(行的其余部分保留)并插入新行。

我想要的是更新行(只需更新描述字段)。这可能吗?

我正在使用 https://api-platform.com/docs/core/getting-started/#mapping-the-entities 给出的示例,并进行一些小的修改(请参阅评论)

我的代码是:

产品.php

<?php
// api/src/Entity/Product.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Serializer\Annotation\Groups; //ADDED THIS
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity]
#[ApiResource(
    normalizationContext: ["groups" => ["products"]],
    denormalizationContext: ["groups" => ["products"]],
)]
class Product // The class name will be used to name exposed resources
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;

    /**
     * A name property - this description will be available in the API documentation too.
     *
     */
    #[ORM\Column]
    #[Assert\NotBlank]
    #[Groups(["products"])] //ADDED THIS
    public string $name = '';

    // Notice the "cascade" option below, this is mandatory if you want Doctrine to automatically persist the related entity
    /**
     * @var Offer[]|ArrayCollection
     *
     */
    #[ORM\OneToMany(targetEntity: Offer::class, mappedBy: 'product', cascade: ['persist'])]
    #[Groups(["products"])] //ADDED THIS
    public iterable $offers;

    public function __construct()
    {
        $this->offers = new ArrayCollection(); // Initialize $offers as a Doctrine collection
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    // Adding both an adder and a remover as well as updating the reverse relation is mandatory
    // if you want Doctrine to automatically update and persist (thanks to the "cascade" option) the related entity
    public function addOffer(Offer $offer): void
    {
        $offer->product = $this;
        $this->offers->add($offer);
    }

    public function removeOffer(Offer $offer): void
    {
        $offer->product = null;
        $this->offers->removeElement($offer);
    }

    // ...

    //ADDED THIS SET AND GET
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    public function getName()
    {
        return $this->name;
    }
}

Offer.php

<?php
// api/src/Entity/Offer.php
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups; //ADDED THIS
use Symfony\Component\Validator\Constraints as Assert;

/**
 * An offer from my shop - this description will be automatically extracted from the PHPDoc to document the API.
 *
 */
#[ORM\Entity]
#[ApiResource(types: ['https://schema.org/Offer'])]
class Offer
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    private ?int $id = null;

    #[ORM\Column(type: 'text')]
    #[Groups(["products"])] //ADDED THIS
    public string $description = '';

    #[ORM\Column]
    #[Assert\Range(minMessage: 'The price must be superior to 0.', min: 0)]
    public float $price = 1.0; //MODIFIED THIS

    #[ORM\ManyToOne(targetEntity: Product::class, inversedBy: 'offers')]
    #[ORM\JoinColumn(nullable:false)] //ADDED THIS
    public ?Product $product = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    //ADDED THIS SET AND GET
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    public function getDescription()
    {
        return $this->description;
    }
}
symfony doctrine-orm api-platform.com
2个回答
0
投票

尝试使用

PATCH
方法而不是
PUT
并将 Content-Type 标头设置为
application/merge-patch+json

PUT
方法要求填写每个已知属性,否则会认为它已被删除,因此将其设置为
null
。这就是为什么您的
id
设置为
null

PATCH
方法仅编辑您的
json
内的属性。

不要忘记这样做:https://api-platform.com/docs/core/content-negotiation/#configuring-patch-formats

您可以阅读:https://rapidapi.com/blog/put-vs-patch/ 以获得正确的解释。这是我们都犯过的错误哈哈


-1
投票

嗯,显然这就像做一样简单

{
    "name": "product updated",
    "offers": [
        {
            "id": "/offers/1",
            "description": "offer updated"
        }
    ]
}

注意

id
没有
@
!!!

更新 2024:在版本 3.2.10 中,这不起作用

© www.soinside.com 2019 - 2024. All rights reserved.