简体   繁体   English

动态创建主义(伪)实体?

[英]Creating Doctrine (pseudo) Entities on the fly?

I've been thrown a 'can it be done in Doctrine' type question by my supervisor, and while I've been able to do some of it with external 'Mapper' objects, I'm wondering if there might be any way to fake-out the ORM to do it within Doctrine itself. 我的主管向我抛出了一个“可以在Doctrine中完成”类型的问题,尽管我已经能够使用外部“映射器”对象来完成其中的一些操作,但我想知道是否有任何方法可以伪造ORM以便在Doctrine本身内完成。

Essentially, the thing we are interested in doing is cutting down database clutter. 本质上,我们感兴趣的事情是减少数据库混乱。 We have a number of tables containing distinct properties or item sets in various categories and then a whole bunch of link-tables tying them to other properties or item sets. 我们有许多包含不同类别的属性或项目集的表,然后是一堆链接表,将它们链接到其他属性或项目集。 For example 例如

We have a couple of tables such as 'Materials' and 'PaperTypes' which describe various Material and Paper options for our products. 我们有一些表格,例如“材料”和“纸张类型”,它们描述了我们产品的各种“材料”和“纸张”选项。 We then group these into 'MaterialCollections' and 'PaperFamilies' respectively. 然后,我们将它们分别分组为“ MaterialCollections”和“ PaperFamilies”。 Then there has to be a one-to-many link table between MaterialCollection/Materials and PaperFamilies/PaperTypes respectively. 然后,分别在MaterialCollection / Materials和PaperFamilies / PaperTypes之间必须有一对多链接表。 Repeat these types of relationships a couple dozen more times and you see where our DB is starting to clutter up. 重复这些类型的关系数十次,您会看到我们的数据库开始混乱的地方。

The link tables themselves are nothing more than a number of entries including multiple records with the PrimaryId from the parent table (collections/families) and unique-per-parent PrimaryId's from the sub-table (materials/papertypes). 链接表本身就是许多条目,包括具有父表(集合/系列)的PrimaryId和子表(材料/纸张类型)的父级唯一父项的多个记录。 Link tables could be named something like MaterialCollectionsMaterials and PaperFamilyPaperTypes for example. 例如,链接表可以命名为MaterialCollectionsMaterials和PaperFamilyPaperTypes。

The idea was to get rid of this slew of link tables by using an abstract 3-table structure as follows: 这个想法是通过使用如下的抽象3表结构来摆脱大量的链接表:

Lists (consisting of a unique ListId, a ListName and a TypeId) Types (consisting of a unique TypeId, a TypeName and an EntityName) ListXref (consisting of ListIds and memberId which points to the primaryId from the Entity designated in the list type) 列表(由唯一的ListId,ListName和TypeId组成)类型(由唯一的TypeId,TypeName和EntityName组成)ListXref(由指向列表类型中指定的Entity指向primaryId的ListIds和memberId组成)

Multiple lists can exist for a given type, but the Xref pairs are unique. 给定类型可以存在多个列表,但外部参照对是唯一的。 Different Types can also point to the same Entity. 不同的类型也可以指向同一实体。 (eg there may be more than one type of Material list defined) (例如,可能定义了不止一种材料清单)

The 'ListName' would be the equivalent of the parent TableNames above and would allow for eliminating two of the tables in those relationships. “ ListName”将等同于上面的父表名,并允许消除那些关系中的两个表。 So the records in 'MaterialCollections' would now instead be records in the List table of TypeName "MaterialCollection". 因此,“ MaterialCollections”中的记录现在将改为TypeName“ MaterialCollection”的List表中的记录。 The records that would have been in a link table (such as MaterialCollectionsMaterials) would now instead be pointed to from ListXRef. 现在应该从ListXRef指向链接表中的记录(例如MaterialCollectionsMaterials)。

As stated, I've gotten a basic mapper to make this work for rather basic list creation. 如前所述,我已经有了一个基本的映射器,可以使此工作用于相当基本的列表创建。 But what I'm wondering is if there is any way to create Entities or things that behave like Doctrine Entities to establish the abstract relationships between a given list/listtype and the table referenced by EntityName and the corresponding memberIds? 但是我想知道的是,是否有任何方法可以创建实体或行为类似于Doctrine实体的东西,从而在给定的列表/列表类型与由EntityName引用的表和相应的memberIds之间建立抽象关系?

In other words, it would be really nice if I had some means by which I could produce something that had the bulk of functionality of a Doctrine Entity which could be retrieved from the Service Manager or something like it that would behave (have similar properties/functions) like a Doctrine Entity. 换句话说,如果我能以某种方式生产出具有Doctrine实体的大部分功能的东西(可以从服务管理器中检索到)或类似的行为(具有相似的属性/函数),就像教义实体。

I tried adding a wrapper object in the Entity tree that my mappers could try to retrieve which was basically an inherited version of the Xref entity with a few alias functions, but it can't be retrieved with the Entity manager. 我尝试在实体树中添加一个包装器对象,我的映射器可以尝试检索该包装器对象,该对象基本上是Xref实体的继承版本,带有一些别名函数,但是无法使用实体管理器来检索。

Any help is appreciated. 任何帮助表示赞赏。

PS While it is not a priority at the moment, longer term I also want to really throw a wrench into the works by trying to have some lists be capable of pointing back to the records produced by other lists. 附言:虽然目前这不是优先事项,但从长远来看,我也想通过尝试使某些列表能够指向其他列表产生的记录来真正投入工作。 So, for example, a List "ProductXMaterials" of type "ProductMaterials" might point to some but not all results of "MaterialCollections". 因此,例如,类型为“ ProductMaterials”的列表“ ProductXMaterials”可能指向“ MaterialCollections”的某些但不是全部结果。 But I'll worry about this one later. 但是我稍后会担心。

Well, Doctrine can't generate "pseudo entity classes" but you can. 好吧,Doctrine不能生成“伪实体类”,但是可以。

First you need to hook into the Doctrine "loadClassMetadata" event. 首先,您需要连接到Doctrine的“ loadClassMetadata”事件。 Inside, you generate your entity classes and configuration from code on the fly and dump them into a specified directory (in the cache for example) maintaining the doctrine convention naming but with a custom namespace. 在内部,您可以通过代码即时生成实体类和配置,然后将它们转储到指定的目录中(例如,在缓存中),以维护主义的惯例命名方式,但是具有自定义名称空间。 For example : "AutoGeneratedNamespace\\Entities" 例如:“ AutoGeneratedNamespace \\ Entities”

So let's say you need to have a dynamic entity generated for an entity with name: "TmpUser", and you're using "yml" instead of annotations 因此,假设您需要为名称为“ TmpUser”的实体生成一个动态实体,并且您使用的是“ yml”而不是注释

<?php
namespace Example;

use Doctrine\ORM\Event\LoadClassMetadataEventArgs;

class DoctrineEventListener
{
    /**
     * Invoked on Doctrine loadClassMetadata event
     *
     * @var Doctrine\ORM\Event\LoadClassMetadataEventArgs
     **/
    public function loadClassMetadata(LoadClassMetadataEventArgs $args)
    {
        $em             = $args->getEntityManager();
        $metadata       = $args->getClassMetadata();
        $factory        = $em->getMetadataFactory();
        $name           = 'AutoGeneratedNamespace\Entities\TmpUser';
        $tmpDirectory   = '/path/to/cache/generated-classes';

        // current class metadata is the same as the one we need. this means we already generated it
        if($metadata->getName() == $name || $factory->hasClassMetadata($name)) {
            return;
        }
        /** 
         Generate your class here and dump it into the cache directory with the name: TmpUser.php
         (If you're using "yml" instead of annotations also create the TmpUser.orm.yml configuration and dump it in the same directory)
        **/

        // create a metadata for the newly created class
        $metadata = $factory->newClassMetadataInstance($name);

        // Register metadata so doctrine knows about it
        $factory->setMetadataFor($name, $metadata);
    }

}

Finally, you tell Doctrine to look for entities inside the cache directory as well using the configuration: (This is taken from the Symfony documentation so if you're not using Symfony it may vary a bit but you can find the precise configuration in the Doctrine docs) 最后,您告诉Doctrine也使用配置在缓存目录内查找实体:(这取自Symfony文档,因此,如果您不使用Symfony,它可能会有所不同,但是您可以在Doctrine中找到确切的配置文档)

doctrine:
    orm:
        mappings:
            MyGeneratedEntitiesUniqueMappingsKey:
                type: yml
                dir: '/path/to/cache/generated-classes'
                is_bundle: false
                prefix: "AutoGeneratedNamespace\Entities"
                alias: GeneratedEntities

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM