21
votes

I want to create some Symfony2 bundles which are reusable accross different projects, but where the entities also can be easily extended if required.

An example could be a reusable UserBundle, which contains a User entity with all the ORM mappings defined. In my application however, I might want to extend this entity and add extra columns, associations or override some of the parent's mappings.

The closest solution I could find are Doctrine2's mapped superclasses, but then I'd lose the plug-and-playness of my reusable bundle, I'd always have to extend the mapped superclass in my application even if I don't wish to modify the mappings.

The other documented inheritance schemes require modifying the parent's mappings, and then my UserBundle wouldn't be portable anymore accross projects.

Is there a way to define a fully-working entity in one bundle, and still extend that in another bundle?

1
+1 I have drawn the same conclusions as you, did you ever come up with a solution?Steve
Nope, it seems it's not possible with the current inheritance models in Doctrine.Gerry
Any news on this problem ? I've been struggling with this limitation so many times that I wonder if a real fix will be issued at all. Forking bundles just to add a field in the entity mapping is getting old.Hubert Perron

1 Answers

13
votes

For future reference, this can be solved using target entity resolution.

You can find extra information in Symfony docs.

The steps are pretty straighforward:

  1. Create an interface in your bundle for the User entity

    namespace Acme/UserBundle/Model;
    interface UserInterface
    {
        // public functions expected for entity User
    }
    
  2. Make your base User entity implement the interface

    namespace Acme/UserBundle/Entity;
    /**
     * @ORM\Entity
     */
    class User implements UserInterface
    {
        // implement public functions
    }
    
  3. Create relationships as usual, but using the interface

    namespace Acme/InvoiceBundle/Entity;
    /**
     * @ORM\Entity
     */
    class Invoice
    {
        /**
         * @ORM\ManyToOne(targetEntity="Acme\UserBundle\Model\UserInterface")
         */
        protected $user;
    }
    
  4. Configure the listener by adding the following to config.yml

    doctrine:
        # ....
        orm:
            # ....
            resolve_target_entities:
                Acme\UserBundle\Model\UserInterface: Acme\UserBundle\Entity\User
    

If you want to customize User entity for your current application

  1. Extend from the User class or implement UserInterface

    namespace Acme/WebBundle/Entity;
    use Acme/UserBundle/Entity/User as BaseUser;
    /**
     * @ORM\Entity
     */
    class User extends BaseUser
    {
        // Add new fields and functions
    }
    
  2. Configure the listener accordingly

    doctrine:
        # ....
        orm:
            # ....
            resolve_target_entities:
                Acme\UserBundle\Model\UserInterface: Acme\WebBundle\Entity\User