Edited at

Doctrine2でValueObject

More than 3 years have passed since last update.


はじめに

実は結構前からDoctrine2のAnnocationでValueObjectを表現する事が出来ます。

しかし、まだ正式版のリリースはありません。。。

でも知りたい方への紹介、代替案の紹介、自身の暇つぶしも兼ねて記したいと思います。


準備

Symfony2のプロジェクト内(じゃなくてもいいですがDoctrine2を動かしている環境で)

composer.jsonへ何も考えずに以下の定義を追加してcomposer updateしましょう。



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

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


実装方法

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

をみればわかります。

が、暇なので実装例について。コメントも合わせて書いていきます。


/**
* 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は流石に、、、じゃあせめて正式サポート時にリファクタしやすいようにしよう、

そんな方法のご紹介です。

/**
* 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]