学说 - 从实体中解耦模型

时间:2014-09-24 15:59:13

标签: php orm doctrine-orm

这是合约。 使用Doctrine ORM for PHP,我需要"解耦"来自实体持久层的模型。 假设我们有UserEntity,其中包含db映射的所有漂亮内容, as:注释,属性,setter / getters等。 另一方面,我希望有一个单独的User类,它只保存与业务相关的逻辑,例如:User :: getFullName()。 此外,我希望用户扩展UserEntity,以便用户继承所有访问方法。

我可以通过不为我工作来检查可能的解决方案:

  • 只是从实体扩展模型,然后在DQL中指定模型不起作用
  • 使UserEntity / ** @MappedSuperclass * /不起作用,因为在这种情况下,UserEntity"本身并不是一个实体"
  • InheritanceType / DiscriminatorColumn / DiscriminatorMap不起作用因为模型不是实体

任何想法?

4 个答案:

答案 0 :(得分:3)

得了! (> = PHP 5.4解决方案)

简短:使实体成为特征,在模型类中使用实体特征。 查看此文档页面:http://doctrine-orm.readthedocs.org/en/latest/tutorials/override-field-association-mappings-in-subclasses.html

实施例: 假设我们有用户模型。首先创建用户实体:

/**
 * @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;
    }
}

在用户模型中注意@Entity注释。这是直接使用模型所必需的 在实体经理。

现在假设我们需要一个扩展User one的Admin模型。这有点棘手。 我们必须将@Entity更改为User中的@MappedSuperclass,以便进行扩展。 然后创建Admin模型,将其声明为@Entity,并在其上重新声明表名 使用@Table注释(否则,由于某些原因,Doctrine会因为要获取哪个表而混淆)。 看起来像这样:

/**
 * @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)';
    }
}

这样,用户和管理模型(=实体)都可以在Doctrine中使用。

我们不能做任何我们通常对实体做的事情: 通过实体管理器查找()它们,直接在查询中使用模型等。

答案 1 :(得分:0)

嘿孩子们想买些魔法吗?

您可以在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类,该类扩展Doctrine\ORM\EntityRepository。这样,您可以确保findfindByfindOneBy方法将创建模型的新实例,在模型中设置$ entity对象并返回新的Model实例而不是实体。

想要与Doctrine结果输出更清晰地集成吗?

您可以编写一个模型类(不将其注册到Doctrine2中),它扩展实体类并编写您自己的自定义Hydrator类(并将其注册到Doctrine2中),以确保您的结果将作为Model类的数组返回不是Entity类的数组。

想按照Symfony方式做吗?

您可以定义所有这些"与数据库相关的业务逻辑"在您的实体存储库类和所有这些"非数据库相关的业务逻辑"在服务类中(如果您愿意,可以创建专用于一个实体的服务)。然后使用db-logic参数(实体)调用业务逻辑层(服务,存储库)。

另一方面,Symfony方式非常灵活,因为只要它们是PSR,你就可以在预先建立的Symfony结构之外构建任何功能。

答案 2 :(得分:0)

关于定制保湿器。 简而言之,这个解决方案也不适合我(我可能会遗漏任何东西)。这里有两个陷阱:

  1. 不方便从原始db数据重新映射到自定义水合器内的模型对象。问题是db表中的字段是单个单词' (比如:firstname / lastname)而在对象中我手动修改它们是为了骆驼(为了便于阅读)。这当然可以解决从实体的注释中读取元信息,但这将涉及魔术和额外的时间资源。所以这个解决方案会非常糟糕'
  2. 我如何使用自定义水合物'发现'功能 ?喜欢:$ em-> find('我的\ Model \ User',1)?因为我希望我的模型可以在每个地方百分之百地使用实体。
  3. 因此,Mihai Stancu建议的任何解决方案都不适合我。

    • 模型类中的魔法:不是一个干净的解决方案,因为它涉及将实体传递给构造函数并且不会给你类型提示(魔法__call的原因)
    • 自定义保温器:似乎更干净但不能在所有情况下使用,并且从db原始结果重新映射到模型对象是复杂的(=耗时)
    • Symfony的服务方式:也不是干净的解决方案,因为我需要100%分离模型逻辑和数据库相关的逻辑(所以我的模型中没有单一的映射信息/注释,只是一个纯商业逻辑)

答案 3 :(得分:0)

好吧,我最近一直问自己这个问题,而我得到的简单答案是将所有的anotations从实体中移到yaml或xml中,以便整理我的“模型”/实体。

然后我的所有属性都在我的用户类中,但是所有的持久性配置都已移出,那么你也可以用getter和setter做任何你想做的事情(在我看来尽可能多地删除)。