持久化插入来自OneToMany ArrayCollection变量

时间:2017-07-05 09:45:32

标签: symfony doctrine-orm symfony-3.3

当我尝试保留数据时,我有一个错误,而且我没有找到有关完全相同问题的人的帮助....

我有两个关联实体得分(ManyToOne)/ 播放器(OneToMany),当我坚持得分时,学说会插入所有得分存储在与我想要存储的分数相对应的播放器的数组集合中。 有关信息,我存储了一轮的每个分数,因此用户有多个分数(所以manyToOne / OneToMany关系)。

我有一份包含所有分数的球员名单。我只想插入最后一个,就像其他人已经在数据库中一样。但是对于我尝试的每个解决方案,学说插入所有分数......即使我只将最后一个分数放在玩家分数中。 然后,我删除每个实体上的持久级联,doctrine抛出关于实体链接的异常......

以下是分数实体

/**
 * Score
 *
 * @ORM\Table(name="score")
 * @ORM\Entity(repositoryClass="AppBundle\Entity\Repository\ScoreRepository")
 */
class Score
{
/**
 * @var int
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Player", inversedBy="scores", cascade={"persist"})
 * @ORM\JoinColumn(name="player", referencedColumnName="id")
 *
 * @var Player
 */
private $player;

/**
 * @var DateTime
 *
 * @ORM\Column(name="played_at", type="datetime")
 */
private $playedAt;

/**
 * @var int
 *
 * @ORM\Column(name="value", type="integer")
 */
private $value;

这是玩家实体:

/**
 * Player
 *
 * @ORM\Table(name="player")
 * @ORM\Entity(repositoryClass="AppBundle\Entity\Repository\PlayerRepository")
 */
class Player extends BaseUser
{
/**
 * @var int
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

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

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

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

 /**
 * @ORM\OneToMany(targetEntity="AppBundle\Entity\Score", mappedBy="player", cascade={"persist"})
 *
 * @var ArrayCollection|Score[]
 */
private $scores;

现在,我的控制器,我想要保持分数

$content = $request->getContent();
    $scoresParams = [];
    if (!empty($content))
    {
        $scoresParams = json_decode($content, true); // 2nd param to get as array
    }
    $players = array();

    $transformer = $this->get('app.transformer.score');
    foreach($scoresParams as $k=>$v) {
        $players[] = $transformer->reverseTransform($v);
    }
    foreach($players as $k=>$p) {
        $index = count($p->getScores()) - 1;
        $s = $p->getScores()[$index];
        $p->setScores(array($s));
        $this->get('app.repository.score')->persist($s);
    }

我设置的玩家得分只是我要保存的分数...因为,就像我有一个持续存在的cascad,并且所有其他用户的分数都没有从教条中检索,命中将尝试插入它们。 .. 所以,对于第一个玩家来说,好吧,但是对于第二个玩家来说,当我坚持它时,$ s已经有了一个ID,并且它会保留玩家的所有数据(不仅仅是最后一个)。

变换器是一个捆绑(League \ Fractal \ Transformer),它从一组数据中创建一个玩家阵列

public function reverseTransform(array $data)
{
    $this->checkRequiredFields($data);

    $player = $this->playerRepository->find($data[self::KEY_ID]);

    if (!$player) {
        throw new NotFoundException('User not found');
    }

    $scoresRaw = $data[self::KEY_ENC_SCORES];
    $scores = array();

    foreach($scoresRaw as $k => $s) {
        $score = new Score($player);
        if($s[self::KEY_ENC_SCORE_ID] != 0) {
            $score->setId($s[self::KEY_ENC_SCORE_ID]);
        }
        $score->setPlayedAt( ( new \DateTime() )->setTimestamp( $s[self::KEY_ENC_SCORE_DATE] ) );
        $score->setValue($s[self::KEY_ENC_SCORE_VALUE]);
        $scores[] = $score;
    }

    $player->setScores($scores);

    return $player;
}

你能看到这里有什么问题吗?

这是解决方案 - >控制器

        $content = $request->getContent();
    $scoresParams = [];
    if (!empty($content))
    {
        $scoresParams = json_decode($content, true); // 2nd param to get as array
    }
    $players = array();

    $transformer = $this->get('app.transformer.score');
    // For each players
    foreach($scoresParams as $i=>$player) {
        $scores = $player['scores'];
        foreach($scores as $j=>$s) {
            $score = $transformer->reverseTransform($s['data']);
            $this->get('app.repository.score')->merge($score);
        }
    }

    $response = new JsonResponse(array('data' => ''));
    $response->setStatusCode(201);

    return $response;

将得分数组反转为对象的功能

    $player = $this->playerRepository->find($data[self::KEY_USER]);

    if (!$player) {
        throw new NotFoundException('User not found');
    }

    $score = new Score($player);
    if($data[self::KEY_ID] != 0) {
        $score->setId($data[self::KEY_ID]);
    }
    $score->setPlayedAt( ( new \DateTime() )->setTimestamp( $data[self::KEY_DATE] ) );
    $score->setValue($data[self::KEY_VALUE]);

    return $score;

1 个答案:

答案 0 :(得分:1)

由于您的实体来自json,因此它未链接到实体管理器。

我认为你应该使用merge()方法来链接它。

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#merging-entities

    $score->setValue($s[self::KEY_ENC_SCORE_VALUE]);
    $this->get('app.repository.score')->merge($score);
    $scores[] = $score;
}
$player->setScores($scores);