gpt4 book ai didi

php - API 平台资源的多个 key 标识符

转载 作者:行者123 更新时间:2023-12-05 03:41:59 24 4
gpt4 key购买 nike

我有一个包含 Serie 对象集合的 Chart 对象,而这些 Serie 对象包含 Data 对象集合。我希望根据它们在各自集合中的位置,而不是要求 REST API 通过代理主键来识别 Serie 和 Data 对象。

数据库架构如下。本来想做serie的chart_id/position和data的chart_id/serie_id/position复合主键,但是Doctrine只能对one level做(即 Serie)并且为了保持一致,我对所有表都使用代理键。

chart
- id (PK autoincrement)
- name

serie
- id (PK autoincrement)
- position (int with unique constraint with chart_id)
- name
- chart_id (FK)

data
- id (PK autoincrement)
- position (int with unique constraint with serie_id)
- name
- serie_id (FK)
- value

/charts/1 的完全水合响应将返回如下所示的 JSON。例如,要定位名称为 Series1Data1 的数据对象,url 应该是 /charts/1/series/0/datas/1,或者如果需要的话 /datas/chart=1;seriePosition=0;dataPosition=1 也可以。

{
"id": 1,
"name": "chart1"
"series": [{
"chart": "/chart/1",
"position": 0,
"name": "series0.chart1",
"datas": [{
"serie": "/charts/1/series/0",
"position": 0,
"name": "datas0.series0.chart1"
}, {
"serie": "/series/chart=1;position=0",
"position": 1,
"name": "datas1.series0.chart1"
}
]
}, {
"chart": "/chart/1",
"position": 1,
"name": "series1.chart1",
"datas": []
}
]
}

为了更改标识符,我使用 @ApiProperty 将 Serie 和 Data 的主键 $ididentifier 设置为 false,并将 Serie 的 $chart$position 以及 Data 的 $serie$position 为真。

实体/Chart.php

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
//use ApiPlatform\Core\Annotation\ApiProperty;
//use ApiPlatform\Core\Annotation\ApiSubresource;
//use Symfony\Component\Serializer\Annotation\Groups;
//use Symfony\Component\Serializer\Annotation\SerializedName;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

/**
* @ORM\Entity()
* @ApiResource()
*/
class Chart
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
* @ORM\Column(type="integer")
*/
private $id;

/**
* @ORM\OneToMany(targetEntity=Serie::class, mappedBy="chart")
*/
private $series;

/**
* @ORM\Column(type="string", length=45)
*/
private $name;

public function __construct()
{
$this->series = new ArrayCollection();
}

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

public function getSeries(): Collection
{
return $this->series;
}

public function addSeries(Serie $series): self
{
exit(sprintf('Chart::addSeries() $this->series->contains($series): %s', $this->series->contains($series)?'true':'false'));
if (!$this->series->contains($series)) {
$this->series[] = $series;
$series->setChart($this);
}

return $this;
}

public function removeSeries(Serie $series): self
{
if ($this->series->removeElement($series)) {
if ($series->getChart() === $this) {
$series->setChart(null);
}
}

return $this;
}

public function setName($name)
{
$this->name = $name;

return $this;
}

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

实体/Serie.php

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
//use ApiPlatform\Core\Annotation\ApiSubresource;
//use Symfony\Component\Serializer\Annotation\Groups;
//use Symfony\Component\Serializer\Annotation\SerializedName;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

/**
* @ORM\Entity()
* @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="unique_position_serie", columns={"chart_id", "position"}), @ORM\UniqueConstraint(name="unique_name_serie", columns={"chart_id", "name"})})
* @ApiResource(
* collectionOperations={
* "get1" = {
* "method" = "get",
* },
* "get2" = {
* "method" = "get",
* "path" = "/charts/{id}/series",
* "requirements" = {
* "id" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "id",
* "in" = "path",
* "required" = true,
* "description" = "Chart ID",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "post1" = {
* "method" = "post",
* },
* "post2" = {
* "method" = "post",
* "path" = "/charts/{id}/series",
* "requirements" = {
* "id" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "id",
* "in" = "path",
* "required" = true,
* "description" = "Chart ID",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* }
* },
* itemOperations={
* "get1" = {
* "method" = "get",
* },
* "get2" = {
* "method" = "get",
* "path" = "/charts/{id}/series/{position}",
* "requirements" = {
* "id" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "put1" = {
* "method" = "put",
* },
* "put2" = {
* "method" = "put",
* "path" = "/charts/{id}/series/{position}",
* "requirements" = {
* "id" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "patch1" = {
* "method" = "patch",
* },
* "patch2" = {
* "method" = "patch",
* "path" = "/charts/{id}/series/{position}",
* "requirements" = {
* "id" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "delete1" = {
* "method" = "delete",
* },
* "delete2" = {
* "method" = "delete",
* "path" = "/charts/{id}/series/{position}",
* "requirements" = {
* "id" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* }
* }
* )
*/

class Serie
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
* @ORM\Column(type="integer")
* @ApiProperty(identifier=false)
*/
private $id;

/**
* @ORM\ManyToOne(targetEntity=Chart::class, inversedBy="series")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
* @ApiProperty(identifier=true)
* ApiProperty(push=true)
*/
private $chart;

/**
* @ORM\Column(type="integer")
* @ApiProperty(identifier=true)
* ApiProperty(push=true)
*/
private $position;

/**
* @ORM\OneToMany(targetEntity=Data::class, mappedBy="serie")
*/
private $data;

/**
* @ORM\Column(type="string", length=45)
*/
private $name;

public function __construct()
{
$this->data = new ArrayCollection();
}

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

public function getChart(): ?Chart
{
return $this->chart;
}

public function setChart(?Chart $chart): self
{
$this->chart = $chart;

return $this;
}

public function getData(): Collection
{
return $this->data;
}

public function addData(Data $data): self
{
if (!$this->data->contains($data)) {
$this->data[] = $data;
$data->setSerie($this);
}

return $this;
}

public function removeData(Data $data): self
{
if ($this->data->removeElement($data)) {
if ($data->getSerie() === $this) {
$data->setSerie(null);
}
}

return $this;
}

public function setPosition(int $position)
{
$this->position = $position;

return $this;
}

public function getPosition()
{
return $this->position;
}

public function setName($name)
{
$this->name = $name;

return $this;
}

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

实体/Data.php

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
//use ApiPlatform\Core\Annotation\ApiSubresource;
//use Symfony\Component\Serializer\Annotation\Groups;
//use Symfony\Component\Serializer\Annotation\SerializedName;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity()
* @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="unique_name_data", columns={"serie_id", "name"}), @ORM\UniqueConstraint(name="unique_position_data", columns={"serie_id", "position"})})
* @ApiResource(
* collectionOperations={
* "get1" = {
* "method" = "get",
* },
* "get2" = {
* "method" = "get",
* "path" = "/charts/{id}/series/{position}/datas",
* "requirements" = {
* "id" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "id",
* "in" = "path",
* "required" = true,
* "description" = "Chart ID",
* "schema" = {
* "type" = "integer"
* }
* },
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "post1" = {
* "method" = "post",
* },
* "post2" = {
* "method" = "post",
* "path" = "/charts/{id}/series/{position}/datas",
* "requirements" = {
* "id" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "id",
* "in" = "path",
* "required" = true,
* "description" = "Chart ID",
* "schema" = {
* "type" = "integer"
* }
* },
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* }
* },
* itemOperations={
* "get1" = {
* "method" = "get",
* },
* "get2" = {
* "method" = "get",
* "path" = "/charts/{id}/series/{series_position}/datas/{position}",
* "requirements" = {
* "id" = "\d+",
* "series_position" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "series_position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* },
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Datas position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "put1" = {
* "method" = "put",
* },
* "put2" = {
* "method" = "put",
* "path" = "/charts/{id}/series/{series_position}/datas/{position}",
* "requirements" = {
* "id" = "\d+",
* "series_position" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "series_position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* },
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Datas position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "patch1" = {
* "method" = "patch",
* },
* "patch2" = {
* "method" = "patch",
* "path" = "/charts/{id}/series/{series_position}/datas/{position}",
* "requirements" = {
* "id" = "\d+",
* "series_position" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "series_position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* },
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Datas position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* },
* "delete1" = {
* "method" = "delete",
* },
* "delete2" = {
* "method" = "delete",
* "path" = "/charts/{id}/series/{series_position}/datas/{position}",
* "requirements" = {
* "id" = "\d+",
* "series_position" = "\d+",
* "position" = "\d+",
* },
* "openapi_context" = {
* "parameters" = {
* {
* "name" = "series_position",
* "in" = "path",
* "required" = true,
* "description" = "Series position in chart",
* "schema" = {
* "type" = "integer"
* }
* },
* {
* "name" = "position",
* "in" = "path",
* "required" = true,
* "description" = "Datas position in chart",
* "schema" = {
* "type" = "integer"
* }
* }
* }
* }
* }
* }
* )
*/
class Data
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
* @ORM\Column(type="integer")
* @ApiProperty(identifier=false)
*/
private $id;

/**
* @ORM\ManyToOne(targetEntity=Serie::class, inversedBy="data")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
* @ApiProperty(identifier=true)
* ApiProperty(push=true)
*/
private $serie;

/**
* @ORM\Column(type="integer")
* @ApiProperty(identifier=true)
* ApiProperty(push=true)
*/
private $position;

/**
* @ORM\Column(type="string", length=45)
*/
private $name;

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

public function getSerie(): ?Serie
{
return $this->serie;
}

public function setSerie(?Serie $serie): self
{
$this->serie = $serie;

return $this;
}

public function setPosition(int $position)
{
$this->position = $position;

return $this;
}

public function getPosition()
{
return $this->position;
}

public function setName($name)
{
$this->name = $name;

return $this;
}

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

API 平台能够为图表和系列项目生成 IRI,但不能为数据项目生成 IRI。我怀疑发生这种情况是因为 Data 的 $serie 标识符属性需要 chartId 及其位置,但不知道如何解决它。

我调查了 subresources ,但是,它们只支持 GET 请求和 subresources will be deprecated in favor of multiple ApiResources (但是,我什至不知道“多个 ApiResources”是什么意思)。另外,可能与装饰 IriConverter 有关,但不确定。

我需要做什么才能让数据资源通过其在系列集合中的位置来识别,并让 Swagger 文档反射(reflect)这样做?

编辑 - 附加内容

我更改了实体以尝试实现两种不同的方法,但不幸的是,这两种方法都无法完全奏效。我应该只将精力集中在这两种方法中的一种上吗?

  1. 父级标识符在查询中的位置(即 /datas/chart=1;serie_position=0;data_position=0)

  2. 父级标识符在路径中的位置(即 /charts/1/series/0/datas/0)

如果我想使用占位符 chart_id 而不是 id(即 /charts/{chart_id}/series/{series_position}/datas/{position} 而不是 /charts/{id}/series/{series_position}/datas/{position},如何删除或重命名图表的 id

我是否应该以不同的方式执行此操作,这样我就不需要所有注释并可能重命名图表的 id?在某种程度上,我能够让 Swagger 提供字段,但不要认为我做得对。也许通过 decorating api_platform.openapi.factory

enter image description here

最佳答案

可悲的是,我发现 api-platform 太天真了,而且对于好的多级 api 的设计/实现非常有限(以及通常不符合存储在 ORM 中的实体的 api 资源)并且导致代码不可读/不可维护快。

对于类似的用例,我求助于具有三个必需参数的单个 api 端点(更不用说 api-platform 在一个半月后从项目中删除了)。

关于php - API 平台资源的多个 key 标识符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67473888/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com