Php 条令-模型与实体的解耦
这是交易。 使用PHP的ORM,我需要将模型与实体持久层“解耦”。 假设我们有UserEntity,它保存了数据库映射的所有漂亮东西, as:注释、属性、setter/getter等。 另一方面,我希望有一个单独的用户类,它只保存与业务相关的逻辑,例如:User::getFullName()。 此外,我希望用户扩展UserEntity,以便用户继承所有访问方法 我检查过的可能解决方案不适用于我:Php 条令-模型与实体的解耦,php,orm,doctrine-orm,Php,Orm,Doctrine Orm,这是交易。 使用PHP的ORM,我需要将模型与实体持久层“解耦”。 假设我们有UserEntity,它保存了数据库映射的所有漂亮东西, as:注释、属性、setter/getter等。 另一方面,我希望有一个单独的用户类,它只保存与业务相关的逻辑,例如:User::getFullName()。 此外,我希望用户扩展UserEntity,以便用户继承所有访问方法 我检查过的可能解决方案不适用于我: 仅仅从实体扩展模型,然后在DQL中指定模型是行不通的 make UserEntity/**@Map
- 仅仅从实体扩展模型,然后在DQL中指定模型是行不通的
- make UserEntity/**@MappedSuperclass*/不起作用,因为在本例中,UserEntity“本身不是实体”
- InheritanceType/DiscriminatorColumn/DiscriminatorMap也不起作用,因为模型不是实体
class UserModel
{
protected $userEntity;
public function __construct($entity)
{
$this->userEntity = $entity;
}
public function __call($name, $arguments)
{
if (!method_exists($this, $name) AND method_exists($this->userEntity, $name)) {
return call_user_func_array(array($this, $name), $arguments);
}
}
}
您可以定义自己的本地EntityRepository类,该类扩展了原则\ORM\EntityRepository
。通过这种方式,您可以确保find
、findBy
、findOneBy
方法将创建模型的新实例,在模型内设置$entity对象,并返回新模型实例而不是实体
想要与条令结果输出进行更干净的集成?
您可以编写一个扩展实体类的模型类(无需将其注册到Doctrine2),并编写您自己的自定义类(并将其注册到Doctrine2),以确保结果将作为模型类的数组而不是实体类的数组返回
想用Symfony的方式吗?
您可以在entities repository类中定义所有这些“与db相关的业务逻辑”,在服务类中定义所有这些“与db无关的业务逻辑”(如果需要,您可以创建一个专用于一个实体的服务)。然后使用db逻辑参数(实体)调用业务逻辑层(服务、存储库)
另一方面,Symfony方式非常灵活,因为您可以在预先建立的Symfony结构之外构建任何功能,只要它们是PSR。关于自定义的应用程序。
简而言之,这个解决方案对我来说也不是一个选择(尽管我可能遗漏了任何东西)。这里有两个陷阱:
- 模型类内部的Magic:不是一个干净的解决方案,因为它涉及将实体传递给构造函数,并且不会给您类型提示(Magic调用的原因)
- 自定义对象:看起来更干净,但不能在所有情况下都使用,从db原始结果到模型对象的重新映射很复杂(=耗时)
- Symfony的服务方式:也不是干净的解决方案,因为我需要100%分离模型逻辑和数据库相关逻辑(因此我的模型中没有一个映射信息/注释,只是一个纯业务逻辑)
- 明白了!(>=PHP5.4解决方案)
简短:使实体成为特征,在模型类中使用实体特征。
签出此文档页:
例如:
假设我们有用户模型。首先创建用户实体:
/**
* @ORM\Table(name="user")
*/
trait UserEntity {
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @var string
*
* @ORM\Column(name="first_name", type="string", length=100, nullable=true)
*/
protected $firstName;
/**
* @var string
*
* @ORM\Column(name="last_name", type="string", length=100, nullable=true)
*/
protected $lastName;
// other mapping here...
}
然后创建模型并在其中使用实体特征:
/**
* @ORM\Entity
*/
class User {
use UserEntity;
public function getFullName() {
return $this->firstName . ' ' . $this->lastName;
}
}
用户模型中的Mind@Entity注释。这是直接使用模型所必需的
在实体管理器中
现在假设我们需要一个管理模型来扩展用户模型。这有点棘手。
我们必须在User中将@Entity更改为@MappedSuperclass,以便对其进行扩展。
然后创建管理模型,将其声明为@Entity,并在其上重新声明一个表名
使用@Table注释(否则,由于某种原因,将混淆从哪个表中获取)。
像这样:
/**
* @ORM\MappedSuperclass
*/
class User {
use UserEntity;
public function getFullName() {
return $this->firstName . ' ' . $this->lastName;
}
}
/**
* @ORM\Entity
* @ORM\Table(name="user")
*/
class Admin extends User {
public function getFullName() {
return parent::getFullName() . ' (admin)';
}
}
这样,用户和管理模型(=实体)都可以在条令中使用
不,我们可以对实体做我们通常会做的任何事情:
通过实体管理器查找它们,在查询中直接使用模型,等等。好吧,我最近一直在问自己这个问题,我得到的简单答案是将实体中的所有变量移到yaml或xml中,以分离我的“模型”/“实体”
然后,我的用户类中有我的所有属性,但所有持久性配置都已移出,然后您还可以使用getter和setter执行任何操作(在我看来,删除尽可能多的属性)。相反。UserEntity扩展了用户。用户不需要也不希望访问所有UserEntity访问属性。相反,它应该只有您的业务方法,如changeName.no,用户必须有权访问UserEntity。e、 g.User::getFullName()是
return$this->firstName$此->lastName
。在这种情况下,firstName/lastName需要从用户级别进行访问。是的,MappedSuperclass
和InheritaceType
注释都是与数据库相关的行为,它们不会帮助您实现所需的功能。好吧,魔法是肮脏的东西。第二个选择似乎是我需要的。你能用“自定义水合器”写一个例子吗?thanksMagic并不肮脏,它只是与众不同,天主教徒都不喜欢它。告诉你——我会给你几天时间在教义文档中查找它,并做出自己的尝试