Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>If you want to stick with the Data Mapper pattern, you can either load the product from the database with all its dependencies (manufacturer, stock, tax) or you can use lazy loading. The first option is not a good practice. But to use lazy loading you will need an additional layer obtained through a virtual proxy.</p> <p>Why? Because otherwise you would have to put some database code inside your entity, and the whole idea about those layers is decoupling.</p> <h1>So, what's a virtual proxy?</h1> <p>According to Martin Fowler (2003):</p> <blockquote> <p>A virtual proxy is an object that looks like the object that should be in the field, but doesn't actually contain anything. Only when one of its methods is called does it load the correct object from the database.</p> </blockquote> <h1>Example</h1> <p>The interface defines the methods that will be implemented by the real entities and virtual proxies:</p> <pre><code>// The interface interface ManufacturerInterface { public function getName(); } </code></pre> <p>This is the entity, you are probably extending some generic model class too:</p> <pre><code>// The concrete Manufacturer class class Manufacturer implements ManufacturerInterface { private $name; public function __construct($name) { $this-&gt;name = $name; } public function getName() { return $this-&gt;name; } } </code></pre> <p>And this is the proxy:</p> <pre><code>// The proxy class ManufacturerProxy implements ManufacturerInterface { private $dao; private $manufacturer; private $manufacturerId; public function __construct($dao, $manufacturerId) { $this-&gt;dao = $dao; // set manufacturer to NULL to indicate we haven't loaded yet $this-&gt;manufacturer = null; } public function getName() { if ($this-&gt;manufacturer === null) { $this-&gt;lazyLoad(); } return $this-&gt;manufacturer-&gt;getName(); } private function lazyLoad() { $this-&gt;manufacturer = $this-&gt;dao-&gt;getById($this-&gt;manufacturerId); } } </code></pre> <p>The proxy will be used by the Data Mapper when instantiating other classes with some relationship with the Manufacturer, like the Product. So, when a product is instantiated, it's fields are filled and the manufacturer field receives an instance of ManufacturerProxy instead of Manufacturer.</p> <p>The implementation of the Data Mapper will differ, so I'll give a simple example:</p> <pre><code>class ProductMapper { private $dao; private $manufacturerDao; // ....... public function find($id) { // call the DAO to fetch the product from database (or other source) // returns an associative array $product = $this-&gt;dao-&gt;find($id); // instantiate the class with the associative array $obj = new Product($product); // create the virtual proxy $obj-&gt;manufacturer = new ManufacturerProxy($this-&gt;manufacturerDao, $product['manufacturer_id']); return $obj; } } </code></pre> <p>As I said, the example above is very simple. I've included the DAO as you're using it, but authors like Martin Fowler give the task of handling SQL queries or any other underlying technology to the Data Mapper. But there's also authors like Nock (2004) that employ both the Data Mapper and the Data Accessor.</p> <p>With a more complex Data Mapper, you could only have files in a language like XML or YAML, similar Doctrine or Hibernate.</p> <p>Just to finish, the service:</p> <pre><code>class ProductService { private $productMapper; /** * Execute some service * @param {int} $productId The id of the product */ public function someService($producId) { // get the product from the database // in this case the mapper is handling the DAO // maybe the data mapper shouldn't be aware of the DAO $product = $this-&gt;productMapper-&gt;find($productId); // now the manufacturer has been loaded from the database $manufacturerName = $product-&gt;manufacturer-&gt;getName(); } } </code></pre> <p>Maybe you could make the DAO call the Data Mapper to create the entities and then use the DAO in the Service. It is actually simpler, because you don't need to repeat the methods twice, but that's up to you.</p> <h1>The Alternative</h1> <p>If you don't want to implement the virtual proxy, and still want a fluent interface, you could switch to the Active Record pattern. There are some libraries that can do the job, like <a href="https://github.com/kla/php-activerecord" rel="noreferrer">PHP-ActiveRecord</a> and <a href="https://github.com/j4mie/paris" rel="noreferrer">Paris</a>. Or if you want to do it by yourself, you can look <a href="http://derivante.com/2009/05/14/php-activerecord-with-php-53/" rel="noreferrer">here</a> and <a href="http://www.devshed.com/c/a/PHP/Using-the-Active-Record-Pattern-with-PHP-and-MySQL/" rel="noreferrer">here</a>. The book by Martin Fowler is also a good start.</p> <h3>References</h3> <p>FOWLER, Martin. <strong>Patterns of Enterprise Application Architecture</strong>. Addison-Wesley, 2003. NOCK, CLIFTON. <strong>Data Access Patterns: Database Interactions in Object-Oriented Applications</strong>. Addison-Wesley, 2004.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload