Php 条令-模型与实体的解耦

Php 条令-模型与实体的解耦,php,orm,doctrine-orm,Php,Orm,Doctrine Orm,这是交易。 使用PHP的ORM,我需要将模型与实体持久层“解耦”。 假设我们有UserEntity,它保存了数据库映射的所有漂亮东西, as:注释、属性、setter/getter等。 另一方面,我希望有一个单独的用户类,它只保存与业务相关的逻辑,例如:User::getFullName()。 此外,我希望用户扩展UserEntity,以便用户继承所有访问方法 我检查过的可能解决方案不适用于我: 仅仅从实体扩展模型,然后在DQL中指定模型是行不通的 make UserEntity/**@Map

这是交易。 使用PHP的ORM,我需要将模型与实体持久层“解耦”。 假设我们有UserEntity,它保存了数据库映射的所有漂亮东西, as:注释、属性、setter/getter等。 另一方面,我希望有一个单独的用户类,它只保存与业务相关的逻辑,例如:User::getFullName()。 此外,我希望用户扩展UserEntity,以便用户继承所有访问方法

我检查过的可能解决方案不适用于我:

  • 仅仅从实体扩展模型,然后在DQL中指定模型是行不通的
  • make UserEntity/**@MappedSuperclass*/不起作用,因为在本例中,UserEntity“本身不是实体”
  • InheritanceType/DiscriminatorColumn/DiscriminatorMap也不起作用,因为模型不是实体
有什么想法吗?

嘿,孩子们想买些魔术吗? 您可以使用PHP中的魔术方法转发所有访问器方法调用,例如:

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。

关于自定义的应用程序。 简而言之,这个解决方案对我来说也不是一个选择(尽管我可能遗漏了任何东西)。这里有两个陷阱:

  • 不方便从原始数据库数据重新映射到自定义数据库中的模型对象。问题是db表中的字段是“单字”(如:firstname/lastname),而在对象中,我手动将它们修改为驼峰大小写(为了可读性)。这当然可以通过从实体的注释中读取元信息来解决,但这将涉及魔法和额外的时间资源。因此,这个解决方案将相当“肮脏”
  • 我将如何在“查找”等功能中使用自定义搜索器?比如:$em->find('My\Model\User',1)?因为我希望我的模型在每个实体使用的地方都能百分之百的可用
  • 因此,Mihai Stancu提出的任何解决方案都不适合我

    • 模型类内部的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并不肮脏,它只是与众不同,天主教徒都不喜欢它。告诉你——我会给你几天时间在教义文档中查找它,并做出自己的尝试