我们正在使用JMS进行实体(de)序列化,使用Doctrine和FOSRestBundle开发基于Symfony 3构建的REST API。
在POST操作期间出现问题,我们使用一组新的子实体(玩家)创建一个新实体(Team)。正确反序列化JSON数据 - 创建一个Team对象,并将所有子实体反序列化为Team对象中的players-collection中的Player对象。
反序列化后,Player对象没有Team-reference。因此,我们无法存储Player对象,因为所需的Team FK为空。
我们目前在PrePersist事件中“手动”设置了Player - > Team关系:
/**
* @ORM\PrePersist
*/
public function doSetParentRelation()
{
foreach ($this->getPlayers() as $player) {
$player->setTeam($this);
}
}
在Team.php中:
/**
* @var \AppBundle\Entity\Player[]
*
* @ORM\OneToMany(targetEntity="Player", mappedBy="team", cascade={"persist"})
* @Type("array<AppBundle\Entity\Player>")
* @Groups({"create-version"})
* @Assert\Valid(traverse="true")
*/
private $players;
在Player.php中:
/**
* @var \AppBundle\Entity\Team
*
* @ORM\ManyToOne(targetEntity="Team", inversedBy="players", fetch="EAGER")
* @ORM\JoinColumn(nullable=false)
* @Exclude
*/
private $team;
我想知道是否有更优雅的方法。我们不需要将自定义PrePersist事件添加到通过API调用创建的OneToMany关系的每个实体,而是需要更全面的解决方法。
我们找到了一个适合我们的解决方案,并且不依赖于自定义的PrePersist事件。我们已经设置JMS来使用getter和setter而不是反射(de)序列化:
use JMS\Serializer\Annotation\AccessType;
/**
* [...]
* @AccessType("public_method")
*/
class Team
{
[...]
}
然后我们添加了一个setPlayers()方法并更改了addPlayer()方法,以便在每个Player中设置team-reference。
/**
* Set players
*
* @param Player[] $players
*/
public function setPlayers(array $players)
{
foreach ($players as $player) {
$this->addPlayer($player);
}
}
/**
* Add player
*
* @param Player $player
*/
public function addPlayer(Player $player)
{
$this->players[] = $player;
$player->setTeam($this);
}
对于API的POST请求,我们使用表单来获取请求参数并创建实体或执行操作。 (注意,您总是必须在后端提交表单,因为用户没有。请参阅下面的submit
方法调用。)
$yourEntity = new YourEntity();
$form = $this->createForm(YourFormType::class, $yourEntity)
->submit($request->request->all())
;
if ($form->isValid()) {
// your entity logic
}
或者,根据您的帖子的内容,您可能需要先解码它。
$yourEntity = new YourEntity();
$form = $this->createForm(YourFormType::class, $yourEntity);
$data = json_decode($request->getContent(), true);
$form->submit($data);
if ($form->isValid()) {
// your entity logic
}
与此以及常规形式的唯一区别在于,您永远不会使用$form->createView();
在前端(通过树枝模板)创建表单。您只是始终捕获POST请求并将表单用作实体转换器。
希望这可以帮助。