Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP: 5 layer model

I have question about how to "correctly" write my application.

I would like to follow the service/DAO/mapper/entity model and I think that I fully understand it but I found out that some things which I want to do might collide with general conception of this model.

I have these database tables: Product table and it's related table - manufacturers

I have simple ProductDbMapper which gets product's data from database and returns them as a Product entity so I can use it like this:

echo $productEntity->getName();
$productEntity->setPrice(29.9);

I think that it would be great to be able to use somethink like this:

echo $productEntity->getManufacturer()->getName();

The "getManufacturer()" method would simply query another service/DAO for the manufacturer by id. I know that the correct way how to get manufacturer of a product is:

$product = $productService->getProduct(5);
$manufacturer = $manufacturerService->getManufacturerByProduct($product);

But I think that "the fluent" solution is much simplier - it's easy to understand and fun to use. Actually it's pretty natural. I tried to implement this by callbacks. I passed the callback which calls manufacturer's service to Product entity in my ProductMapper.

Problem is that it looks like I'm trying to follow the 5 layer model and at the same time I'm trying to avoid it. So my question is: does this look like a good solution? Does it make sense? How could I achieve the same (magic) in better way?

like image 405
grongor Avatar asked Dec 11 '22 20:12

grongor


1 Answers

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.

Why? Because otherwise you would have to put some database code inside your entity, and the whole idea about those layers is decoupling.

So, what's a virtual proxy?

According to Martin Fowler (2003):

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.

Example

The interface defines the methods that will be implemented by the real entities and virtual proxies:

// The interface
interface ManufacturerInterface
{
    public function getName();
}

This is the entity, you are probably extending some generic model class too:

// The concrete Manufacturer class
class Manufacturer implements ManufacturerInterface
{
    private $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

And this is the proxy:

// The proxy
class ManufacturerProxy implements ManufacturerInterface
{
    private $dao;
    private $manufacturer;
    private $manufacturerId;

    public function __construct($dao, $manufacturerId)
    {
        $this->dao = $dao;

        // set manufacturer to NULL to indicate we haven't loaded yet
        $this->manufacturer = null;
    }

    public function getName()
    {
        if ($this->manufacturer === null)
        {
            $this->lazyLoad();
        }
        return $this->manufacturer->getName();
    }

    private function lazyLoad()
    {
        $this->manufacturer = $this->dao->getById($this->manufacturerId);
    }
}

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.

The implementation of the Data Mapper will differ, so I'll give a simple example:

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->dao->find($id);
        
        // instantiate the class with the associative array
        $obj = new Product($product);
        
        // create the virtual proxy
        $obj->manufacturer = new ManufacturerProxy($this->manufacturerDao, $product['manufacturer_id']);
        
        return $obj;
    }
}

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.

With a more complex Data Mapper, you could only have files in a language like XML or YAML, similar Doctrine or Hibernate.

Just to finish, the service:

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->productMapper->find($productId);
        
        // now the manufacturer has been loaded from the database
        $manufacturerName = $product->manufacturer->getName();
    }
}

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.

The Alternative

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 PHP-ActiveRecord and Paris. Or if you want to do it by yourself, you can look here and here. The book by Martin Fowler is also a good start.

References

FOWLER, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley, 2003. NOCK, CLIFTON. Data Access Patterns: Database Interactions in Object-Oriented Applications. Addison-Wesley, 2004.

like image 63
mayconbordin Avatar answered Dec 20 '22 20:12

mayconbordin