Doctrine2でValueObject


#はじめに
実は結構前からDoctrine2のAnnocationでValueObjectを表現する事が出来ます。
しかし、まだ正式版のリリースはありません。。。
でも知りたい方への紹介、代替案の紹介、自身の暇つぶしも兼ねて記したいと思います。

#準備
Symfony2のプロジェクト内（じゃなくてもいいですがDoctrine2を動かしている環境で）
composer.jsonへ何も考えずに以下の定義を追加してcomposer updateしましょう。

```json

  "require": {
    "doctrine/orm": "2.5.*@dev",
    "doctrine/dbal": "v2.5.0-RC2",
  }

```

これで準備はできました。


#実装方法
http://doctrine-orm.readthedocs.org/en/latest/tutorials/embeddables.html
をみればわかります。
が、暇なので実装例について。コメントも合わせて書いていきます。

```php

/**
 * Class User
 * @package RSchedule\Bundle\RScheduleBundle\Entity\User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="RSchedule\Bundle\RScheduleBundle\Entity\User\UserRepository")
 */
class User extends Entity
{
    /**
     * @param UserAuthority $userAuthority
     *
     * //これが重要、UserへUserAuthorityをEbeddedする
     * //DoctrineによってUserを実態化した際に、UserAuthorityが組み込まれる
     * @ORM\Embedded(class = "UserAuthority", columnPrefix = false))
     */
    private $userAuthority;

}

/**
 * Class UserAuthority
 * @package RSchedule\Bundle\RScheduleBundle\Entity\User
 *
 * //これが重要、何かのクラスへ組み込まれる事を宣言している
 * @ORM\Embeddable
 */
final class UserAuthority extends Enum
{
    /**
     * 管理者権限
     */
    const ADMIN = 0;
    /**
     * 管理者権限なし
     */
    const OTHER = 1;

    /**
　　　* //通常通りAnnotationでカラムの属性を定義可能
     * @ORM\Column(name="authority", columnDefinition="TINYINT(1) DEFAULT 1 NOT NULL", options={"comment" = "権限"})
     */
    private $authority;


    /**
     * @param $value
     */
    public function __construct($value)
    {
        $this->authority = parent::__construct($value);
    }

    /**
     * @return integer
     */
    public function valueOf()
    {
        return $this->authority;
    }

}

//これは色々やり方あるけどEnumっぽくしたかった(mysqlのじゃないよ)
abstract class Enum
{

    public function __construct($value)
    {
        $ref = new \ReflectionClass(get_class($this));
        $constants = $ref->getConstants();
        if (!in_array($value, $constants, true)) {
            throw new \InvalidArgumentException("does not definition:{$value}");
        }

        return $value;
    }

    abstract public function valueOf();

}

```

#代替案
つかいたい、でもRC2は流石に、、、じゃあせめて正式サポート時にリファクタしやすいようにしよう、
そんな方法のご紹介です。

```php


/**
 * Class User
 * @package RSchedule\Bundle\RScheduleBundle\Entity\User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="RSchedule\Bundle\RScheduleBundle\Entity\User\UserRepository")
 */
class User extends Entity
{
    /**
     * @param UserAuthority $userAuthority
     *
     * @ORM\Column(name="authority", columnDefinition="TINYINT(1) DEFAULT 1 NOT NULL", options={"comment" = "権限"})
     */
    private $userAuthority;

    // こんな感じでクラスにインジェクションする
    public setUserAuthority(UserAuthority $userAuthority)
    {
          $this->userAuthority = $userAuthority->valueOf();
    }

    // 取り出すときもインスタンス化したクラスを返す
    public getUserAuthority()
    {
          return new UserAuthority($this->userAuthority);
    }

}

/**
 * Class UserAuthority
 * @package RSchedule\Bundle\RScheduleBundle\Entity\User
 *
 */
final class UserAuthority extends Enum
{
    /**
     * 管理者権限
     */
    const ADMIN = 0;
    /**
     * 管理者権限なし
     */
    const OTHER = 1;

    private $authority;

    /**
     * @param $value
     */
    public function __construct($value)
    {
        $this->authority = parent::__construct($value);
    }

    /**
     * @return integer
     */
    public function valueOf()
    {
        return $this->authority;
    }

}

abstract class Enum
{

    public function __construct($value)
    {
        $ref = new \ReflectionClass(get_class($this));
        $constants = $ref->getConstants();
        if (!in_array($value, $constants, true)) {
            throw new \InvalidArgumentException("does not definition:{$value}");
        }

        return $value;
    }

    abstract public function valueOf();

}

// 新規のインスタンス化はFactoryへ任せてこんな感じにすると実装がシンプルになるかも
class UserFactory
{

    public function newUser()
    {
         $user = new User();
         $user->setUserAuthority(new UserAuthority(UserAuthority::ADMIN));
         return $user;
    }

}


参考：

http://doctrine-orm.readthedocs.org/en/latest/tutorials/embeddables.html

[http://welcometothebundle.com/persist-the-money-doctrine-value-object/]

[https://packagist.org/packages/doctrine/orm]


```