Symfony2でセッションに保存していたエンティティをDBに登録しようとするエンティティのプロパティにセットしてflushしたときにエラーになるのを回避する方法

ググってもそれっぽい情報がなかったので、
誰かの役に立てば幸いです。

## 0. 環境

* CentOS 7.1
* PHP 7.0.9
* Symfony 2.8.9
* Doctrine\ORM 2.5.4

## 1. 現象

セッションに格納したエンティティ（DBに登録済み）を
登録対象のエンティティのプロパティにセットして
`$em->flush()` すると下記のエラーになった。

```
[2016-09-06 12:25:09] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Debug\Exception\ContextErrorException: "Notice: Undefined index: 0000000041d0498f0000000066042c62" at /home/homepage/api/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 2917 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\ContextErrorException(code: 0): Notice: Undefined index: 0000000041d0498f0000000066042c62 at /home/homepage/api/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:2917)"} []
```

`Notice: Undefined index: 0000000041d0498f0000000066042c62` とエラーになる。
そして再度実行すると
`Notice: Undefined index: 000000007785750d0000000017c0921d` と値が変わっている。

なんだこのエラー？

実行したコードは以下のとおり

```HogeController.php
public function orderAction(Request $request)
{
    $orderTmp = $request->getSession()->get('order_tmp');
    if (is_null($orderTmp)) {
        throw new HttpException(Response::HTTP_BAD_REQUEST, 'エラー');
    }

    $em = $this->getDoctrine()->getManager();
    $package = $orderTmp->getPackage();    // ★セッションから取得したものをそのまま使う

    $order = (new Order())
        ->setPackage($package)
    ;

    $this->tryValidate($this->get('validator'), $order);
    $em->persist($order);
    $em->flush();

    $request->getSession()->remove('order_tmp');
    return $order;
}
```

## 2. マッピングに不一致があるからエラーになっているのでは？

DoctrineのUnitOfWorkがエラーメッセージに出ているので、
DBスキーマとエンティティの定義情報に不一致があるからエラーが出ているのではないかと疑い
マッピングのValidateをコンソールから行った。

```
$ php app/console doctrine:schema:validate
[Mapping]  OK - The mapping files are correct.
[Database] OK - The database schema is in sync with the mapping files.
```

問題なし...

## 3. そもそもEntityManagerで管理されている？

UnitOfWorkでエラーになっていたので、
そもそもEntityManagerで管理しているのかダメ元で確認してみた。

```
$uow = $em->getUnitOfWork();
$package = $orderTmp->getPackage();
$this->get('logger')->error($uow->getEntityState($package));
```

するとログには `3` と出力された。

```Doctrine\ORM\UnitOfWork.php
    /**
     * A detached entity is an instance with persistent state and identity that is not
     * (or no longer) associated with an EntityManager (and a UnitOfWork).
     */
    const STATE_DETACHED = 3;
```

つまり、セッションに保存されたエンティティを取得すると
EntityManagerの管理外になっている。

このせいでエラーになっていた模様。

## 4. 解決方法

データをDBから再取得すると `flush()` でエラーにならなくなった。

```HogeController.php
public function orderAction(Request $request)
{
    $orderTmp = $request->getSession()->get('order_tmp');
    if (is_null($orderTmp)) {
        throw new HttpException(Response::HTTP_BAD_REQUEST, '注文中のデータがありません。');
    }

    $em = $this->getDoctrine()->getManager();
    //$package = $orderTmp->getPackage();
    $package = $em->getRepository(Package::class)->find($orderTmp->getPackage()->getId());    // ★ここを修正した

    $order = (new Order())
        ->setPackage($package)
    ;

    $this->tryValidate($this->get('validator'), $order);
    $em->persist($order);
    $em->flush();

    $request->getSession()->remove('order_tmp');
    return $order;
}
```

エラーは解決しDBにデータは登録されたが、
このやり方でいいのか疑問は残る...

## 5. 結論

Symfony2でセッションに保存したエンティティをそのまま使ってDBに登録しようとするとよくわからんエラーになってハマる可能性があります。