1
0
mirror of synced 2024-12-14 15:16:04 +03:00
doctrine2/docs/en/cookbook/decorator-pattern.rst

274 lines
6.6 KiB
ReStructuredText
Raw Normal View History

2011-05-04 06:48:40 +04:00
Persisting the Decorator Pattern
================================
.. sectionauthor:: Chris Woodford <chris.woodford@gmail.com>
2011-05-06 05:36:18 +04:00
This recipe will show you a simple example of how you can use
2012-12-02 21:58:15 +04:00
Doctrine 2 to persist an implementation of the
2011-05-06 05:36:18 +04:00
`Decorator Pattern <http://en.wikipedia.org/wiki/Decorator_pattern>`_
2011-05-04 06:48:40 +04:00
2011-05-04 07:04:41 +04:00
Component
---------
2011-05-04 06:48:40 +04:00
2011-05-06 05:36:18 +04:00
The ``Component`` class needs to be persisted, so it's going to
2011-05-06 05:16:40 +04:00
be an ``Entity``. As the top of the inheritance hierarchy, it's going
2011-05-04 06:48:40 +04:00
to have to define the persistent inheritance. For this example, we
2011-05-06 05:16:40 +04:00
will use Single Table Inheritance, but Class Table Inheritance
2011-05-04 07:04:41 +04:00
would work as well. In the discriminator map, we will define two
2011-05-06 05:16:40 +04:00
concrete subclasses, ``ConcreteComponent`` and ``ConcreteDecorator``.
2011-05-04 06:48:40 +04:00
.. code-block:: php
<?php
namespace Test;
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
2011-05-06 05:16:40 +04:00
* @DiscriminatorMap({"cc" = "Test\Component\ConcreteComponent",
"cd" = "Test\Decorator\ConcreteDecorator"})
2011-05-04 06:48:40 +04:00
*/
abstract class Component
{
/**
* @Id @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
protected $id;
/** @Column(type="string", nullable=true) */
2011-05-04 06:48:40 +04:00
protected $name;
/**
* Get id
* @return integer $id
*/
public function getId()
{
return $this->id;
}
/**
* Set name
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
* @return string $name
*/
public function getName()
{
return $this->name;
}
}
ConcreteComponent
-----------------
2011-05-06 05:16:40 +04:00
The ``ConcreteComponent`` class is pretty simple and doesn't do much
more than extend the abstract ``Component`` class (only for the
purpose of keeping this example simple).
2011-05-04 06:48:40 +04:00
.. code-block:: php
<?php
namespace Test\Component;
use Test\Component;
/** @Entity */
class ConcreteComponent extends Component
{}
Decorator
---------
2011-05-06 05:16:40 +04:00
The ``Decorator`` class doesn't need to be persisted, but it does
need to define an association with a persisted ``Entity``. We can
use a ``MappedSuperclass`` for this.
2011-05-04 06:48:40 +04:00
.. code-block:: php
<?php
namespace Test;
/** @MappedSuperclass */
abstract class Decorator extends Component
{
/**
2011-05-06 05:16:40 +04:00
* @OneToOne(targetEntity="Test\Component", cascade={"all"})
2011-05-04 06:48:40 +04:00
* @JoinColumn(name="decorates", referencedColumnName="id")
*/
protected $decorates;
/**
2012-12-02 21:58:15 +04:00
* initialize the decorator
2011-05-04 06:48:40 +04:00
* @param Component $c
*/
public function __construct(Component $c)
{
$this->setDecorates($c);
2011-05-04 06:48:40 +04:00
}
/**
* (non-PHPdoc)
2011-05-06 05:16:40 +04:00
* @see Test.Component::getName()
2011-05-04 06:48:40 +04:00
*/
public function getName()
{
return 'Decorated ' . $this->getDecorates()->getName();
}
/**
* the component being decorated
* @return Component
*/
protected function getDecorates()
{
return $this->decorates;
}
/**
* sets the component being decorated
* @param Component $c
*/
protected function setDecorates(Component $c)
{
$this->decorates = $c;
}
}
2011-05-06 05:16:40 +04:00
All operations on the ``Decorator`` (i.e. persist, remove, etc) will
cascade from the ``Decorator`` to the ``Component``. This means that
when we persist a ``Decorator``, Doctrine will take care of
persisting the chain of decorated objects for us. A ``Decorator`` can
be treated exactly as a ``Component`` when it comes time to
persisting it.
The ``Decorator's`` constructor accepts an instance of a
``Component``, as defined by the ``Decorator`` pattern. The
setDecorates/getDecorates methods have been defined as protected to
hide the fact that a ``Decorator`` is decorating a ``Component`` and
keeps the ``Component`` interface and the ``Decorator`` interface
identical.
To illustrate the intended result of the ``Decorator`` pattern, the
getName() method has been overridden to append a string to the
``Component's`` getName() method.
2011-05-04 06:48:40 +04:00
ConcreteDecorator
-----------------
2011-05-04 07:04:41 +04:00
The final class required to complete a simple implementation of the
2011-05-06 05:16:40 +04:00
Decorator pattern is the ``ConcreteDecorator``. In order to further
illustrate how the ``Decorator`` can alter data as it moves through
the chain of decoration, a new field, "special", has been added to
this class. The getName() has been overridden and appends the value
of the getSpecial() method to its return value.
2011-05-04 07:04:41 +04:00
2011-05-04 06:48:40 +04:00
.. code-block:: php
<?php
namespace Test\Decorator;
use Test\Decorator;
/** @Entity */
class ConcreteDecorator extends Decorator
{
/** @Column(type="string", nullable=true) */
2011-05-04 06:48:40 +04:00
protected $special;
/**
* Set special
* @param string $special
*/
public function setSpecial($special)
{
$this->special = $special;
}
/**
* Get special
* @return string $special
*/
public function getSpecial()
{
return $this->special;
}
/**
* (non-PHPdoc)
2011-05-06 05:16:40 +04:00
* @see Test.Component::getName()
2011-05-04 06:48:40 +04:00
*/
public function getName()
{
return '[' . $this->getSpecial()
. '] ' . parent::getName();
}
}
2011-05-04 07:04:41 +04:00
Examples
--------
Here is an example of how to persist and retrieve your decorated
objects
2011-05-04 06:48:40 +04:00
.. code-block:: php
<?php
2011-05-06 05:16:40 +04:00
use Test\Component\ConcreteComponent,
Test\Decorator\ConcreteDecorator;
2011-05-04 06:48:40 +04:00
// assumes Doctrine 2 is configured and an instance of
// an EntityManager is available as $em
// create a new concrete component
$c = new ConcreteComponent();
$c->setName('Test Component 1');
$em->persist($c); // assigned unique ID = 1
// create a new concrete decorator
$c = new ConcreteComponent();
$c->setName('Test Component 2');
$d = new ConcreteDecorator($c);
$d->setSpecial('Really');
$em->persist($d);
// assigns c as unique ID = 2, and d as unique ID = 3
$em->flush();
$c = $em->find('Test\Component', 1);
$d = $em->find('Test\Component', 3);
echo get_class($c);
2011-05-06 05:16:40 +04:00
// prints: Test\Component\ConcreteComponent
2011-05-04 06:48:40 +04:00
echo $c->getName();
// prints: Test Component 1
echo get_class($d)
2011-05-06 05:16:40 +04:00
// prints: Test\Component\ConcreteDecorator
2011-05-04 06:48:40 +04:00
echo $d->getName();
// prints: [Really] Decorated Test Component 2