在Doctrine实体中设置/更新关联查找字段的最佳方法是什么

时间:2016-05-21 08:52:11

标签: php doctrine-orm dependency-injection domain-driven-design

我有主要实体

/**
 * @ORM\Entity()
 */
class Document
{
    /**
     * @var int
     * @ORM\Id()
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @var DocumentStatus
     * @ORM\ManyToOne(targetEntity="DocumentStatus")
     */
    private $status;
    /**
     * @var string
     * @ORM\Column(type="text")
     */
    private $text;
}

和查找"枚举"实体(在应用程序部署上播种)

/**
 * @ORM\Entity(repositoryClass="DocumentStatusRepository");
 */

class DocumentStatus
{
    const DRAFT = 'draft';
    const PENDING = 'pending';
    const APPROVED = 'approved';
    const DECLINED = 'declined';
    /**
     * @var int Surrogate primary key
     * @ORM\Id()
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string  Natural primary key (name for developers)
     * @ORM\Column(type="string", unique=true)
     */
    private $key;
    /**
     * @var string Short name for users
     * @ORM\Column(type="string", unique=true)
     */
    private $name;
    /**
     * @var string Full decription for users
     * @ORM\Column(type="string", nullable=true, unique=true)
     */
    private $description;
}

使用简单的存储库

class DocumentStatusRepository extends EntityRepository
{
    public function findOneByKey($key)
    {
        return parent::findOneBy(['key' => $key]);
    }
}

我想通过引入类似

的方法来封装文档生命周期的域逻辑
public function __construct($text)
{
   $this->text = $text;
   $this->status = $something->getByKey(DocumentStatus::DRAFT);
}

public function approve()
{
    try {
        $this->doSomeDomainActions();
        $this->status = $something->getByKey(DocumentSatus::DRAFT);
    } catch (SomeDomainException($e)) {
        throw new DocumentApproveException($e);
    }
}
...

public function __construct($text)
{
   $this->text = $text;
   $this->status = $something->getDraftDocumentStatus()
}

public function approve()
{
    $this->status = $something->getApprovedDocumentStatus()
}
...

没有公共制定者。另外,我希望保持文档松散耦合和可测试。

我看到了下一个方法:

  • 永久注入DocumentStatusRepository(或将其封装的服务)通过构造函数创建实体并在postLoad订阅者中使用public setter
  • 通过应用程序引导程序或loadClassMetadata上的静态Document方法永久注入DocumentStatusRepository
  • 在Document :: approve
  • 等构造函数和方法中临时注入DocumentStatusRepository
  • 使用基于$ status->键值
  • 的复杂逻辑的setStatus()方法
  • 在一些DocumentManager中封装文档域逻辑,并使用Document实体,如简单数据storgae或DTO :(

还有其他方法吗?从长远来看,哪种方式更容易使用?

1 个答案:

答案 0 :(得分:1)

  1. 使用生成的文件身份。
    现在,您将在数据库一侧生成标识。所以你save从域角度看文档处于不一致状态。实体/汇总should be identified,如果它没有ID,则它不应该存在。
    如果您真的想要保留数据库连续出版物,请将方法添加到存储库,该方法将为您生成id 更好的方法是使用uuid生成器,例如ramsey / uuid。 inject身份to the constructor

  2. DocumentStatus作为价值对象
    为什么文档状态是实体?它看起来像一个简单的值对象。 然后你可以使用Embeddable注释。因此它将留在数据库中的同一个表中,不需要进行内部连接 DocumentStatus获取行为,例如 - > draftDocumentStatus(),returns NEW DocumentStatus具有草稿状态,因此您可以使用新实例切换旧实例。 ORM将完成剩下的工作。

  3. DocumentStatusRepository
    如果你真的想把DocumentStatus保留为实体,我认为is wrong你不应该有DocumentStatusRepository。
    Document是您的聚合根,而DocumentStatus的only entrance应该是聚合根 因此,您将只拥有DocumentRepository,它将负责rebuilding整个聚合和saving它。 你也应该改变映射 它应该具有FETCH=EAGER类型,因此它将与Document一起检索DocumentStatus 其次,您应该使用CASCADE=ALLORPHANREMOVAL=TRUE进行制图 否则,如果您remove文档,DocumentStatus will stay in the database