使用相关/嵌入式实体

Posted

技术标签:

【中文标题】使用相关/嵌入式实体【英文标题】:Working with related/embedded entities 【发布时间】:2015-07-19 14:53:10 【问题描述】:

Github 原版:https://github.com/zfcampus/zf-apigility-doctrine/issues/215

在代码连接 API 中,三个实体及其各自的服务:地点、产品和位置。

Venue 与 Location 具有 ManyToOne 关系

   /**
     * @ORM\ManyToOne(targetEntity="Db\Entity\Location", cascade="persist")
     * @ORM\JoinColumn(name="location_id", referencedColumnName="id")
     *
     * @var Db\Entity\Location $location
     */
    protected $location;
    public function getLocation() ...
    public function setLocation($location) ...

Venue 与 Product 有 ManyToMany 关系

/**
 * @ORM\ManyToMany(targetEntity="Db\Entity\Product", inversedBy="venues", cascade="persist")
 * @ORM\JoinTable(name="products_venues")
 **/
protected $products;
public function getProducts() ...
public function setProducts($products) ...
public function addProduct(Product $product)

    $product->addVenue($this);
    $this->products[] = $product;

    return $this;

并且 Product 与 Venue 具有多对多关系

/**
 * @ORM\ManyToMany(targetEntity="Db\Entity\Venue", mappedBy="products", cascade="persist")
 **/
protected $venues;
public function getVenues() ...
public function setVenues($venues) ...
public function addVenues($venue)

    $this->venues[] = $venue;

    return $this;

现在,我有两个问题: 1)我可以创建一个新的场地和这样的位置信息:

POST /venues

  .
  .
  .
  "location":
      "address":"123 Fake Street",
      "city":"cityTest",
      "country":"CountryTest",
      "postalCode":"12345"
  

并且使用新的位置注册表创建了一个新的场地,当我获取刚刚创建的场地时,可以看到两者正确链接。 当我获得刚刚创建的 Venue Id 时,我在 _embedded 对象中看到了具有其 id 的位置对象,但是如果我需要编辑该地址并将 Venue 1 与位置 2 而不是最初创建的位置 1 链接怎么办?

其次,我无法在创建最后一个的同一 POST 调用中通过产品创建并将产品与场所链接来重现此过程。 Nether 想出了如何将现有产品添加到特定 Venue 的 Products 集合中。 那么,哪种方式更合适呢? (“1 个调用创建并将它们全部链接”与“多个创建调用 + 补丁调用将它们链接在一起”)我需要什么才能让它们在 Apigility + Doctrine 中发生?

7 月 20 日更新:

哇!!这是一个有趣的发现。为了更新特定场地的位置,我必须在场地服务中添加一个新的“位置”字段。 有了这个,我可以将位置对象作为额外属性与 /Venues 所需的属性一起发布。或者通过添加 location: "id" : 123 来指定 id(这也适用于 PATCH,这是我一直在寻找的。太棒了!)。但是我意识到没有实际的验证来检查具有指定 ID 的位置是否确实存在。如果没有找到,则新场地将链接到“空”位置。 :皱眉:

因此,在那之后,我看到了其中包含的 Apigility Doctrine 验证器。 :smile: ZF\Apigility\Doctrine\Server\Validator\ObjectExists 验证我发送的对象确实存在于数据库中。耶耶耶!!我所要做的就是将其添加为“位置”字段的验证器,并根据https://github.com/doctrine/DoctrineModule/blob/master/docs/validator.md 指定两个选项 entity_class = Db\Entity\Location 所以验证器知道我要验证的实体是什么,fields = id 就像一个验证标准。

现在,当我尝试发布场地时,我需要使用"location": "id":123 指定位置。这也适用于 PATCH。当找不到具有指定 ID 的位置时,我会收到一条很好的错误消息,这很好。如果没有提供 ID,则为 500(我猜有时会出现一个问题......)。

现在,我一直在尝试寻找如何将对象添加到实体集合中。 (例如,将产品添加到特定场所)。我尝试将“产品”字段添加到场地服务,但没有奏效。我一直在没有任何产品的情况下获得场地。 这就是我试图修补的方式:

PATCH /venues/1

...
"products":[
  "id": 5
]
...

对此有什么想法吗?

【问题讨论】:

【参考方案1】:

在 RESTful API 中进行原子 CRUD 操作通常是一个好主意。

在您的情况下,这意味着最好通过名为 POST|GET|DELETE|PUT /location 的单独 API 资源对您的 Location 实体进行 CRUD。

然后 API 客户端必须首先检索或创建一个位置,然后才发布地点。

示例:

第 1 步。 创建或检索位置。

POST /location
  
    "address":"123 Fake Street",
    "city":"cityTest",
    "country":"CountryTest",
    "postalCode":"12345"
  

GET /location/somefilter

第 2 步。 创建场地

POST /venues
  
    ...
    "location": 2 //the location id
    ...
  

【讨论】:

是的,REST API 像这样工作很有意义,但在这种情况下,我在我的项目中使用 Apigility 和 Doctrine 模块 (zf-apigility-doctrine),我可以找不到像那样实现它的方法。我可以同时创建地点和地址,还可以通过使用地点字段 + 位置执行 POST /venues 来链接它们。我只是找不到执行 PATCH /venues/2 "location":123 的方法有道理吗? 您的PATCH /venues/id 端点是否有location 字段? 在我看来,一旦你在服务中创建了一个/location 端点,然后将一个location 字段添加到你的/venues 端点,一切都应该工作。 注意:不要忘记删除旧的 location 字段,因为它需要 array 而不是整数, 我找到了如何使它适用于location(一对一关系)并更新了原始问题。现在我正在处理特定场地的products 集合。 @TomHAnderson 在我创建的回购问题中评论:github.com/zfcampus/zf-apigility-doctrine/issues/215

以上是关于使用相关/嵌入式实体的主要内容,如果未能解决你的问题,请参考以下文章

可嵌入类的映射实体

从一个视图映射具有嵌入式List的实体

JPA - 更新嵌入式实体会生成无效的 SQL

嵌入式密钥的 JPA 实体映射

JPA:如果孩子有嵌入式ID,如何级联保存实体?

Android Room Database:如何嵌入多个实体