EasyExtends - Documentation
3. Make your Symfony2 bundle extendable¶
3.1. Let’s have a quick Symfony2 and Doctrine tour¶
A quick Doctrine tour:
- 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 post` property and not a post_id property.
- 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.
- 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.
- Each entity is linked to a ClassMetadata information which contains all the mapping information and the final class name.
- An entity can extend a SuperClass. A SuperClass is just a mapping definition, a SuperClass cannot be persisted.
A quick Symfony2 bundle tour:
- There are two types of bundles: Application Bundle (AB) and Vendor Bundle (VB), the latter should not be modified inside a project.
- The AB directory is where developers implement the project requirements.
- An AB can overwrite almost everything from a VB, example: you can redefine a VB template at the AB level.
A namespace tour:
- “a namespace is an abstract container providing context for the items” (http://en.wikipedia.org/wiki/Namespace)
- An entity is defined by a namespace
- A bundle is defined by a namespace
- A VB and AB are defined with two different namespaces
3.2. Let’s start to mix these points together¶
- If an AB bundle A wants to use an entity from a VB bundle B, the fully qualify namespace must be used.
- 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.
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.
3.3. Can this problem be solved with the Alternate syntax?¶
There is actually a start of a solution, the DoctrineBundle allows us to use an alternate syntax, ie (‘BlogBundle:Blog’ instead of ‘BundleBlogBundleEntityBlog’). As you can guess this syntax only works for string, inside a query for instance.
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.
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)
3.4. A pragmatic way to solve this issue¶
The easiest way to solve this problem is to use global namespace inside your VB, the global namespace is the only namespace allowed ApplicationYourBundleEntity.
So inside your mapping definition or inside your VB code, you will use one final namespace: problem solved. How to achieve this
- Declare only SuperClass inside a VB, don’t use final entity
- Call your entity BaseXXXX and make it abstract, change the properties from private to protected.
- The same goes for a repository
- Always use Application\YourBundle\Entity\XXXX inside your code
Of course, you need to create for each VB bundle
- a valid structure inside the Application directory
- 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.
3.5. How to make your bundle easy extendable¶
Mainly all you need is to follow instructions in previous paragraph.
- Declare you entity/epository as described above
- Use your entity/repostiory as described above.
- Before generation you also need “skeleton” file that will describe AB entity. For fully working example see SonataMediaBundle.
At last you can run php app/console sonata:easy-extends:generate YourVBBundleName and that’s it!