From e5bac27fcc3e517dd1a307b7f1fc622c135d80e6 Mon Sep 17 00:00:00 2001 From: Tim Nagel Date: Sat, 16 Jun 2012 17:36:40 +1000 Subject: [PATCH] Initial ResolveTargetEntityListener cookbook entry --- .../resolve-target-entity-listener.rst | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 en/cookbook/resolve-target-entity-listener.rst diff --git a/en/cookbook/resolve-target-entity-listener.rst b/en/cookbook/resolve-target-entity-listener.rst new file mode 100644 index 000000000..0e9947755 --- /dev/null +++ b/en/cookbook/resolve-target-entity-listener.rst @@ -0,0 +1,136 @@ +Keeping your Modules independent +================================= + +One of the goals of using modules is to create discreet units of functionality +that do not have many (if any) dependencies, allowing you to use that +functionality in other applications without including unnecessary items. + +Doctrine 2.2 includes a new utility called the ``ResolveTargetEntityListener``, +that functions by intercepting certain calls inside Doctrine and rewrite +targetEntity parameters in your metadata mapping at runtime. It means that +in your bundle you are able to use an interface or abstract class in your +mappings and expect correct mapping to a concrete entity at runtime. + +This functionality allows you to define relationships between different entities +but not making them hard dependencies. + +Background +---------- + +In the following example, the situation is we have an `InvoiceModule` +which provides invoicing functionality, and a `CustomerModule` that +contains customer management tools. We want to keep these separated, +because they can be used in other systems without each other, but for +our application we want to use them together. + +In this case, we have an ``Invoice`` entity with a relationship to a +non-existant object, an ``InvoiceSubjectInterface``. The goal is to get +the ``ResolveTargetEntityListener`` to replace any mention of the interface +with a real object that implements that interface. + +Set up +------ + +We're going to use the following basic entities (which are incomplete +for brevity) to explain how to set up and use the RTEL. + +A Customer entity + +.. code-block:: php + + // src/Acme/AppModule/Entity/Customer.php + + namespace Acme\AppModule\Entity; + + use Doctrine\ORM\Mapping as ORM; + use Acme\CustomerModule\Entity\Customer as BaseCustomer; + use Acme\InvoiceModule\Model\InvoiceSubjectInterface; + + /** + * @ORM\Entity + * @ORM\Table(name="customer") + */ + class Customer extends BaseCustomer implements InvoiceSubjectInterface + { + // In our example, any methods defined in the InvoiceSubjectInterface + // are already implemented in the BaseCustomer + } + +An Invoice entity + +.. code-block:: php + + // src/Acme/InvoiceModule/Entity/Invoice.php + + namespace Acme\InvoiceModule\Entity; + + use Doctrine\ORM\Mapping AS ORM; + use Acme\InvoiceModule\Model\InvoiceSubjectInterface; + + /** + * Represents an Invoice. + * + * @ORM\Entity + * @ORM\Table(name="invoice") + */ + class Invoice + { + /** + * @ORM\ManyToOne(targetEntity="Acme\InvoiceModule\Model\InvoiceSubjectInterface") + * @var InvoiceSubjectInterface + */ + protected $subject; + } + +An InvoiceSubjectInterface + +.. code-block:: php + + // src/Acme/InvoiceModule/Model/InvoiceSubjectInterface.php + + namespace Acme\InvoiceModule\Model; + + /** + * An interface that the invoice Subject object should implement. + * In most circumstances, only a single object should implement + * this interface as the ResolveTargetEntityListener can only + * change the target to a single object. + */ + interface InvoiceSubjectInterface + { + // List any additional methods that your InvoiceModule + // will need to access on the subject so that you can + // be sure that you have access to those methods. + + /** + * @return string + */ + public function getName(); + } + +Next, we need to configure the listener. Add this to the area you set up Doctrine. You +must set this up in the way outlined below, otherwise you can not be guaranteed that +the targetEntity resolution will occur reliably:: + +.. code-block:: php + + $evm = new \Doctrine\Common\EventManager; + + $rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener; + $rtel->addResolveTargetEntity('Acme\\InvoiceModule\\Model\\InvoiceSubjectInterface', + 'Acme\\CustomerModule\\Entity\\Customer', array()); + + // Add the ResolveTargetEntityListener + $evm->addEventSubscriber($rtel); + + $em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm); + +Final Thoughts +-------------- + +With the ``ResolveTargetEntityListener``, we are able to decouple our +bundles, keeping them usable by themselves, but still being able to +define relationships between different objects. By using this method, +I've found my bundles end up being easier to maintain independently. + +