本文共 6537 字,大约阅读时间需要 21 分钟。
this post is not part of the Symfony2 documentation; it is just a state about how things work now (end of 2010) between Doctrine2 and Symfony2. It is not a complaint about the architecture, it just exposes how I solve a recurrent problem I have.
注意:本文档并非Symfony2的官方文档,它只是表现Symfony2和Doctrine2之间如何工作的状态(2010年底)。它并非是关于结构方面的抱怨,它只是表明了我是如何解决这一常见问题的。
A quick Doctrine tour:
Doctrine的简要说明:
Doctrine2 entities are plain PHP objects; there is no database layer information. An Comment::post_id property is part of the database layer and not part of the domain layer. So a comment entity will have a postproperty and not a post_id property.
Doctrine2实体是一个简单PHP对象;它没有数据库层信息。Comment::post_id属性是属于数据库层而非域层,因此Comment实体有一个post属性而非post_id属性。
Doctrine2 entities are mapped through mapping information: yaml, xml, annotation or php code. There is one mapping per class. So if Blog extends SuperBlog, which extends SuperEntity, you will have 3 classes and so 3 information mappings and you will be able to use 3 different tables to save these entities.
Doctrine2实体通过yaml、xml、annotation或PHP代码等映射信息来进行映射,它是基于每个类来做映射的。因此如果Blog从SuperBlog(SuperBlog又是从SuperEntity扩展出来的)扩展出来的话,您将有三个类和三个映射信息,并且您将使用3个不同的数据表来保存这些实体。
A mapped entity is final (from doctrine2 point of view), it cannot be extended unless you create a new mapping definition for the new child class.
如果一个被映射的实体是最终实体(从Doctine2的角度来看)的话,那么它不能被扩展,除非您为新的子类定义一个新的映射。
Each entity is linked to a ClassMetadata information which contains all the mapping information and the final class name.
每个实体都被链到一个ClassMetadata信息,该信息包括所有的映射信息和最终类名。
An entity can extend a SuperClass. A SuperClass is just a mapping definition, a SuperClass cannot be persisted.
实体可以从SuperClass扩展,SuperClass只是个映射定义,它是不能被持久化的。
A quick Symfony2 bundle tour:
Symfony2功能包的简要说明:
There are two types of bundles: Application Bundle (AB) and Vendor Bundle (VB), the latter should not be modified inside a project.
功能包有两类:应用程序功能包(AB)和供应商提供的功能包(VB),后者不能在项目中修改。
The AB directory is where developers implement the project requirements.
AB的目录位置可以由开发者在根据项目要求来实现
An AB can overwrite almost everything from a VB, example: you can redefine a VB template at the AB level.
AB几乎可以覆写VB中的任何东西,如您可以在AB层次重定义VB的模板
名称空间的简要说明:
“a namespace is an abstract container providing context for the items” ()
“命名空间是一个提供上下文的抽象容器
An entity is defined by a namespace
实体通过命名空间定义
A bundle is defined by a namespace
功能包通过命名空间定义
A VB and AB are defined with two different namespaces
VB和AB通过两个不同的命名空间定义
If an AB bundle A wants to use an entity from a VB bundle B, the fully qualify namespace must be used.
如果AB功能包A想使用来自VB功能包B的实体,那么必须使用一个完整的命名空间
If a developer wants to add a new property into a VB entity, the developer needs to create a new child entity with a custom mapping.
如果开发者想添加一个新属性到一个VB实体,那么他需要通过一个自定义的映射去创建一个新的子实体
At this point you have 2 entities with 2 different namespace. The VB bundle’s code refers to its own namespace to instantiate the model, BUT ... how ... you just create a new entity. Your VB will be unable to use this new model ... too bad.
此时,您有两个不同命名空间的两个实体。VB功能包代码指向它自己的命名空间用来对模型进行实例化,但...您刚才创建了一个新的实体。您的VB将不能使用这个新模型...太糟了
There is actually a start of a solution, the DoctrineBundle allows us to use an alternate syntax, ie (BlogBundle:Blog instead of Bundle\BlogBundle\Entity\Blog). As you can guess this syntax only works for string, inside a query for instance.
有个解决方案:DoctrineBundle允许我们使用一个替代语法,如使用BlogBundle:Blog来替代Bundle\BlogBundle\Entity\Blog,因此您可以猜到该语法仅适用于字符串,以便放置在实例的查询中。
So if you want to instantiate a new model, you need first to get the ClassMetadata instance, retrieve the class name and create the model. It’s not really nice and creates a dependency to the class metadata.
因此如果您想实例化一个新模型,您首先需要获得ClassMetadata实例,检索类名并创建模型,然后创建类元数据依赖,这并不是太好。
Last issue, the entity’s mapping association required fully qualifies namespace: no alternate syntax. (I suppose, this last point can be fixed).
最后,实体映射关联要求完整的命名空间:没有替代语法(我假定最后一点没有变化)
At this point, we are stuck with no solution to fully extend a bundle. (Don’t take this for granted; this might change in a near future, as Symfony2 is not complete yet)
基于上述考虑,我们认为目前没有一个完全扩展功能包的解决方案。(不要想当然,在将来这些也许会被改变,因为Symfony2并未完成)
The easiest way to solve this problem is to use global namespace inside your VB, the global namespace is the only namespace allowed Application\YourBundle\Entity.
最容易解决这一问题的方法在您的VB中使用全局命名空间,全局的命名空间只允许Application\您的功能包\Entity
So inside your mapping definition or inside your VB code, you will use one final namespace: problem solved. How to achieve this
因此在您的映射定义或VB代码中,您将使用一个最终命名空间:问题解决。如何来实现呢?
Declare only SuperClass inside a VB, don’t use final entity
在VB中只声明SuperClass,并不使用最终实体
Call your entity BaseXXXX and make it abstract, change the properties from private to protected.
调用您的BaseXXXX并将其抽象,并将其属性由私有(private)改为保护(protected)。
The same goes for a repository
同样对repository中也作相同操作
Always use Application\YourBundle\Entity\XXXX inside your code
总在在您的代码中使用Application\您的功能包\Entity\XXXX
Of course, you need to create for each VB bundle
当然,您需要为每个VB功能包创建:
a valid structure inside the Application directory
Application目录下的合法结构
a valid entity mapping definition
合法的实体映射定义
a model inside the entity folder
实体目录中的模型
The last part is quite inefficient without an efficient tool to generate for you this structure: EasyExtendsBundle to the rescue.
最后这部分如果没有一个有效的工具来帮您生成这一结构将会十分繁琐:EasyExtendsBundle将能帮到您。
Mainly all you need is to follow instructions in previous paragraph.
综上所述,您主要需要:
Declare you entity/repository as described above
根据上节的说明重新声明您的entity/repository
Use your entity/repository as described above.
根据上节的说明使用您的entity/repository
Before generation you also need “skeleton” file that will describe AB entity. Skeleton file can either xml or yml. For fully working example see SonataMediaBundle.
在生成之前,您还需要“skeleton”文件来描述AB实体,该文件可以是xml或yml格式,完整示例请参见SonataMediaBundle
At last you can run php app/console sonata:easy-extends:generate YourVBBundleName. Note that the –dest option allows you to choose the target directory, such as src. Default destination is app/
最后您可以运行 php app/console sonata:easy-extends:generate 您的VB功能包名。注意--dest选项允许您选择目的目录,如src等,该目录缺省为app/