是否可以在每个实体的基础上在条令2中实现自定义水合和持久性?
原则2对价值对象(例如藏品和身份证(有一些主要限制。我想知道是否可以使用自定义机制(或实现(将对象属性映射到数据库(加载和持久性(。
我知道有一些可能性;解决";这个问题,但我不喜欢它们:
- 伪造实体需要在将持久层泄漏到域对象的实体中进行正确处理
- 真实实体需要更多的持久性工作(更多的存储库和更复杂的处理(
- 路堤具有上述限制
- 带有序列化的自定义DBAL类型使查询某些值变得不可能,或者至少非常缓慢
我知道条令中有一些生命周期事件是可用的。我不知道postLoad事件是否携带了一个已经构建的实体对象(带有所有的VO(?因为那样的话对我来说就没用了。
致以最良好的问候,spigandromeda
是的,您可以在config/packages/doctrine.yaml
中注册新的Hydrator,如下所示:
doctrine:
dbal: ...
orm:
hydrators:
CustomEntityHydrator: 'AppORMHydratorCustomEntityHydrator'
...
mapping: ...
...
然后,您可以在查询中使用它,如下所示:
public function findCustomEntities(): array
{
return $this->createQueryBuilder('c')
...your query logic...
->getResult('CustomEntityHydrator');
}
请注意,您只能指定要用于根实体的水合器。如果您获取相关联的实体,您可能会得到一个更复杂的设置,很难调试。
相反,您可以考虑只在实体的接口中处理值对象(VO(。换句话说,字段是标量值,但方法参数和返回值是VO。
以下是一个实体的示例,该实体的id类型为Uuid、位置(某个数字标识符(、状态(例如三进制true/false/null(。这些只是为了展示如何处理不同类型的价值对象:
/**
* @ORMEntity()
*/
class CustomEntity
{
/**
* @ORMId()
* @ORMColumn(type="string", length=64)
*/
private string $id;
/**
* @ORMColumn(type="int")
*/
private int $location;
/**
* @ORMColumn(type="bool, nullable=true)
*/
private bool $status;
private function __construct(Uuid $id, Location $location, Status $status)
{
$this->id = (string) $id;
$this->location = $location->getValue();
$this->status = $status->get();
}
public static function new(Location $location, Status $status): self
{
return new self(Uuid::v4(), $location, $status);
}
public function getId(): Uuid
{
return Uuid::fromString($this->id);
}
public function getLocation(): Location
{
return new Location($this->location);
}
public function activate(): void
{
$this->status = true;
}
public function deactivate(): void
{
$this->status = false;
}
public function isActive(): bool
{
$this->status === true;
}
public function isInactive(): bool
{
$this->status === false;
}
public function isUninitialized(): bool
{
$this->status === null;
}
public function getStatus(): Status
{
if ($this->status === null) {
return new NullStatus();
}
if ($this->status === true) {
return new ActiveStatus();
}
return new InactiveStatus();
}
}
正如您所看到的,您可以用公共构造函数替换new()
。它的作用与二传手类似。我有时甚至在构造函数中为此使用(私有(setter。在状态的情况下,如果您使用多个内部设置值的方法,您甚至不需要setter。类似地,在某些情况下,您可能希望返回标量值而不是VO(或者以其他方式返回,如状态getter和issers所示(。
关键是,你的实体从外部看起来似乎会使用你的VO,但在内部,它已经切换到了一种更适合ORM原则的表示。您甚至可以将其与使用VO和自定义类型(例如UUID(混合使用。你只需要小心,当你的VO需要更多的信息来构建,而不是存储在数据库中时,例如,如果我们的例子中的数字位置在创建过程中也会使用区域设置,那么我们需要存储它(这是有意义的,因为它似乎与数字id有关(,或者我们必须在实体中硬编码它,或者在上面添加抽象,有权访问区域设置的,在这种情况下,您的实体可能不会返回Location,或者至少不会返回LocalizedLocation。
您可能还想考虑不为实体中的每个属性都设置VO。虽然这肯定会很有帮助,例如,将电子邮件包装到自定义VO中以确保有效性,而不仅仅是字符串的类型提示,但对于像(用户(名称这样的通用名称来说,这可能不太有用,因为它接受的字符串种类繁多,所以应该非常宽松。使用上面的方法,您可以很容易地在以后引入VO,方法是为VO添加一个新的getter,更改new()
或任何其他更改属性的方法,然后不必更改下面的数据模型中的任何内容(除非对值的表示方式有更大的更改(。