问题描述
我有一个称为Submission
的实体类型。 Submission
与SurveyData
实体类型具有OnetoOne关系。
SurveyData实体实际上是一个映射的超类。最终,该实体将有几十个子类,用于存储来自不同调查的数据。
根据文档,我创建了一个自定义的normalizer,用于基于type
键处理反规范化:
public function denormalize($data,string $type,string $format = null,array $context = [])
{
if ($type === 'App\Entity\SurveyData\SurveyData') {
$class = 'App\Entity\SurveyData\\' . $data['type'];
$context['resource_class'] = $class;
}
$context[self::ALREADY_CALLED] = true;
return $this->denormalizer->denormalize($data,'App\Entity\SurveyData\\' . $data['type'],$format,$context);
}
有了这个,我可以完美地创建一个带有嵌入式SurveyData的新提交。这是我发送给POST请求的JSON的示例:
{
"facility": "/api/facilities/1","survey": "/api/surveys/monthly_totals","dateDetail": "Q1 2020","surveyData": {
"type": "MonthlyTotals","num_deliveries": 50,"num_cesarean": 30,"num_epidural_anesthesia": 15
},"created": "2020-08-14T18:59:49.218Z","updated": "2020-08-14T18:59:49.218Z","user": "brian","status": "complete"
}
但是,当我通过GET获取集合或单个提交实体时,API平台返回的响应忽略了将@id
属性添加到嵌入式调查响应中。我不确定这是否是因为它是一个不能为空的OnetoOne,所以它是内部跟踪的:
{
"@id": "/api/submissions/2","@type": "Submission","id": 2,"facility": "/api/facilities/1","created": "2020-08-14T18:59:49+00:00","updated": "2020-08-14T18:59:49+00:00","status": "complete","surveyData": {
"num_deliveries": 50,"num_epidural_anesthesia": 15
}
}
真正的问题是PUT
和PATCH
请求失败。
对于PATCH
请求,我可以更新父提交实体中的字段。但是,如果我发送以下请求,则会从数据库中删除Submission和SurveyData实体,并且会从API中收到以下错误:
"Entity App\\Entity\\Submission@000000002116ebc30000000012ca4827 is not managed. An entity is managed if its fetched from the database or registered as new through EntityManager#persist",
要点包括整个响应,包括跟踪:https://gist.github.com/brianV/c32661186c91b49b013017dde77d5d4a
以下是触发错误的PATCH
请求的示例:
{
"user": "brian","surveyData": {
"type": "MonthlyTotals","num_deliveries": 100
}
}
这也发生在每个PUT请求中(其中包括整个替换Submission实体)。
在简单的Symfony&Doctrine中,此解决方案会很好用,但似乎破坏了API平台。
根据评论请求,以下是Submission
实体注释:
/**
* @ApiResource(
* normalizationContext={"groups"={"submission"}},* denormalizationContext={"groups"={"submission"}},* itemOperations={
* "get"={
* "method"="GET",* "access_control"="is_granted('view',object)",* },* "put","patch","delete",* },* )
* @ORM\Entity(repositoryClass="App\Repository\SubmissionRepository")
* @CustomAssert\SubmissionDataIsValid
*/
class Submission
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Groups({"submission"})
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Facility")
* @ORM\JoinColumn(nullable=false)
* @Groups({"submission"})
*/
private $facility;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Patient",inversedBy="submissions")
* @Groups({"submission"})
*/
private $patient;
/**
* @ORM\Column(type="string",length=255)
* @Groups({"submission"})
*/
private $survey;
/**
* @ORM\Column(type="string",length=255,nullable=true)
* @Groups({"submission"})
*/
private $dateDetail;
/**
* @ORM\Column(type="datetime")
* @Assert\Type("\DateTimeInterface")
* @Groups({"submission"})
*/
private $created;
/**
* @ORM\Column(type="datetime")
* @Assert\Type("\DateTimeInterface")
* @Groups({"submission"})
*/
private $updated;
/**
* @ORM\Column(type="string",length=255)
* @Groups({"submission"})
*/
private $user;
/**
* @ORM\Column(type="string",length=255)
* @Groups({"submission"})
*/
private $status;
/**
* @ORM\OnetoOne(targetEntity="App\Entity\SurveyData\SurveyData",inversedBy="submission",cascade={"persist","remove"},orphanRemoval=true,fetch="EAGER")
* @Groups({"submission"})
*/
private $surveyData;
在此先感谢您的帮助!
解决方法
在使用// validator.ts
export const Validate = () => {
// some code
}
URL时,请确保您的SurveyData
属性已通过api_platform.jsonld.normalizer.item
服务进行了规范化。
我假设您已遵循those steps嵌入对象?好吧,正如here所述,由于您未在嵌入式对象中提供Submission
属性,因此Api-Platform认为您正在推送 new 对象,而不是编辑当前版本,这就是为什么准则哭泣(您的错误消息)的原因:此新对象未注册。
自动注册此新对象的最简单方法是在您的@id
属性上添加一个cascade={"persist"}
批注属性:
Submission::$surveyData
但是您也可以使用class Submission
{
/**
* @OneToOne(targetEntity="App\Entity\SurveyData",cascade={"persist"})
*/
private $surveyData,}
方法。
注意:我不确定EntityManagerInterface::persist()
方法是否与嵌入式对象完全兼容,我还记得github上有关此问题。