APIプラットフォーム:継承マップされたDoctrineエンティティの正規化と非正規化

Aug 22 2020

と呼ばれるエンティティタイプがありSubmissionます。ASubmissionは、SurveyDataエンティティタイプに対してOneToOne関係を持っています。

SurveyDataエンティティは、実際にはマップされたスーパークラスです。最終的には、さまざまな調査からのデータを格納するエンティティのサブクラスが数十個あります。

ドキュメントに従って、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を介して単一のSubmissionエンティティをフェッチすると、API Platformによって返される応答は@id、埋め込まれた調査応答にプロパティを追加することを怠ります。これが空白にできないOneToOneであるためかどうかはわかりません。そのため、内部で追跡されます。

{
            "@id": "/api/submissions/2",
            "@type": "Submission",
            "id": 2,
            "facility": "/api/facilities/1",
            "survey": "/api/surveys/monthly_totals",
            "dateDetail": "Q1 2020",
            "created": "2020-08-14T18:59:49+00:00",
            "updated": "2020-08-14T18:59:49+00:00",
            "user": "brian",
            "status": "complete",
            "surveyData": {
                "num_deliveries": 50,
                "num_cesarean": 30,
                "num_epidural_anesthesia": 15
            }
        }

本当の問題PUTPATCHリクエストが失敗することです。

以下のため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リクエストでも発生します(置換送信エンティティ全体が含まれます)。

プレーンな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;

助けてくれてありがとう!

回答

1 rugolinifr Aug 22 2020 at 07:10

URLを操作するときSurveyDataは、プロパティがapi_platform.jsonld.normalizer.itemサービスで正規化されていることを確認してくださいSubmission

私はあなたが続いていると仮定し、これらのステップをあなたのオブジェクトをembeddしますか?ここで説明するように、@id埋め込みオブジェクト内にプロパティを指定しないため、Api-Platformは、現在のオブジェクトを編集するのではなく、新しいオブジェクトをプッシュしていると見なします。そのため、教義が叫びます(エラーメッセージ):この新しいオブジェクトが登録されていません。

この新しいオブジェクトを自動的に登録する最も簡単な方法は、cascade={"persist"}プロパティに注釈プロパティを追加することSubmission::$surveyDataです。

class Submission
{
    /**
     * @OneToOne(targetEntity="App\Entity\SurveyData", cascade={"persist"})
     */
    private $surveyData,
}

ただし、このEntityManagerInterface::persist()方法を使用することもできます。

PATCHメソッドが埋め込みオブジェクトと完全に互換性があるかどうかはわかりません。githubでいくつかの問題を覚えています。