与api平台嵌入关系

时间:2018-10-14 19:00:42

标签: symfony symfony4 api-platform.com

我对Api平台有疑问。 (https://api-platform.com) 我有两个实体。问题和答案。我想进行POST呼叫以创建一个答案的问题。我展示我的实体。

namespace App\Entity;

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

/**
 * @ApiResource(
 *     normalizationContext={"groups"={"question"}},
 *     denormalizationContext={"groups"={"question"}})
 * @ORM\Entity
 */
class Question
{
    /**
     * @Groups({"question"})
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @Groups({"question"})
     * @ORM\Column
     * @Assert\NotBlank
     */
    public $name = '';

    /**
     * @Groups({"question"})
     * @ORM\OneToMany(targetEntity="Answer", mappedBy="question", cascade={"persist"})
     */
    private $answers;

    public function getAnswers()
    {
        return $this->answers;
    }

    public function setAnswers($answers): void
    {
        $this->answers = $answers;
    }


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

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

    public function setName(string $name): void
    {
        $this->name = $name;
    }

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

和答案实体

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 *
 * @ApiResource
 * @ORM\Entity
 */
class Answer
{
    /**
     * @Groups({"question"})
     * @ORM\Id
     * @ORM\Column(type="guid")
     */
    public $id;

    /**
     * @Groups({"question"})
     * @ORM\Column
     * @Assert\NotBlank
     */
    public $name = '';

    /**
     * @ORM\ManyToOne(targetEntity="Question", inversedBy="answers")
     * @ORM\JoinColumn(name="question_id", referencedColumnName="id")
     */
    public $question;

    public function getQuestion()
    {
        return $this->question;
    }

    public function setQuestion($question): void
    {
        $this->question = $question;
    }

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

    public function setName(string $name): void
    {
        $this->name = $name;
    }

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

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

现在,我可以从nelmio的仪表板中创建一个问题并添加一个答案。但是在数据库中,我的答案没有保存与问题的关系。

{
  "name": "my new question number 1",
  "answers": [
    {
          "id": "ddb66b71-5523-4158-9aa3-2691cae9d473",
          "name": "my answer 1 to question number 1"
    }
  ]
}

另一个问题是...我已经用GUID更改了我的答案ID,因为在创建并回答没有ID的问题时会出错。我可以创建一个问题,然后回答而无需指定ID吗?

预先感谢

2 个答案:

答案 0 :(得分:0)

首先,它应该可以持久地存在数据库中。无论如何,您可以为Question实体创建PostValidateSubscriber并检查该关系是否存在。

<?php /** @noinspection PhpUnhandledExceptionInspection */

namespace App\EventSubscriber;

use ApiPlatform\Core\EventListener\EventPriorities;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

final class QuestionPostValidateSubscriber implements EventSubscriberInterface
{
    private $tokenStorage;

    public function __construct(
        TokenStorageInterface $tokenStorage
    ) {
        $this->tokenStorage = $tokenStorage;
    }
    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::VIEW => ['checkQuestionData', EventPriorities::POST_VALIDATE]
        ];
    }

    /**
     * @param GetResponseForControllerResultEvent $event
     */
    public function checkQuestionData(GetResponseForControllerResultEvent $event)
    {
        $bid = $event->getControllerResult();
        $method = $event->getRequest()->getMethod();

        if (!$question instanceof Question || (Request::METHOD_POST !== $method && Request::METHOD_PUT !== $method))
            return;

        $currentUser = $this->tokenStorage->getToken()->getUser();
        if (!$currentUser instanceof User)
            return;
    }
}

进行回显或使用xdebug进行检查。

第二点,您可以为实体的id添加这些注释,以便id可以自己生成。

  • @ORM \ GeneratedValue()
  • @ORM \ Column(type =“ integer”)

答案 1 :(得分:0)

好吧,我看到您的实体中存在一些错误...首先,由于 Question 和 Answer 实体之间的关系是 OneToMany,您的 Qestion 实体应该实现此:

proc_1 = Process(target=my_func_1)
proc_2 = Process(target=my_func_2)

for proc in [proc_1, proc_2]:
    proc.start()

# They are doing their thing and when they've completed they will join the 
# main process and your main program can resume
for proc in [proc_1, proc_2]:
    proc.join()

命令

import codecs, hashlib
pubkey = "AAAA...."  #Put you public key string here
fingerprint = hashlib.md5(codecs.decode(bytes(pubkey,'utf-8'), 'base64')).hexdigest()
print(fingerprint)

允许您在关系类型的实体中创建一个字段,它会为您创建这些方法,只需按照说明操作(在创建两个实体后,使用命令更新问题实体...)

readableLink ApiProperty 注释用于查看 GET 请求上的嵌入对象,如果您使用序列化组,则相同,如果将其设置为 false,则响应将如下所示:

cs_fingerprint = ":".join([ fingerprint[pair:pair+2] for pair in range(0, len(fingerprint)-1, 2) ])
print(cs_fingerprint)

它用于使响应更小(除其他外)...而 writableLink 用于允许这样的 POST 请求(有关详细信息,请参阅此示例 here):

use ApiPlatform\Core\Annotation\ApiProperty;

//..... the rest of your code

/**
 * @ApiProperty(
 *    readableLink=true
 *    writableLink=true
 * )
 * @Groups({"question"})
 * @ORM\OneToMany(targetEntity="Answer", mappedBy="question", cascade={"persist"})
 */
private $answers;


public function __construct()
{
     //....

      $this->answers = new ArrayCollection();

     //...
}

public function addAnswer(Answer $answer): self
{
     if (!$this->answers->contains($answer)) {
           $this->answers[] = $answer;
           $answer->setQuestion($this) 
     }

     return $this;
}

public function removeAnswer(Answer $answer): self
{
    if ($this->answers->contains($answer)) {
        $this->answers->removeElement($answer);
    }
    return $this;
}

当然,在每个实体中使用相应的序列化组... 在 ApiPlatform 中,嵌入的对象通过 setter 和 getter 方法持久化,但也为 OneToMany 关系添加和删除方法,其余的工作由 ORM 完成。 如果这有帮助,请告诉我。干杯!