Home arrow PHP arrow Roll Your Own Repository in PHP: Working with Collections of Entities

Roll Your Own Repository in PHP: Working with Collections of Entities

In this fifth part of the series, I add two additional classes to our sample PHP application. They will be responsible for handling collections of generic entities, and more specifically, collections of user objects. In truth, these new classes will be simple countable iterators, capable of accessing the entities as if they were array elements.

TABLE OF CONTENTS:
  1. Roll Your Own Repository in PHP: Working with Collections of Entities
  2. Manipulating collections of entities with an abstract collection class
By: Alejandro Gervasio
Rating: starstarstarstarstar / 3
December 07, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

Being a fundamental pillar of Domain-driven Design (DDD), a repository is an abstraction layer placed between the domain and mapping layers of an application. It permits you to query collections of domain objects as if all of them were residing in memory at the same time. There are many benefits to having such a level of abstraction, especially for applications that expose a rich domain integrated by numerous objects; however, the implementation of a repository demands some extra work, which can be overkill to the eyes of the average PHP developer.

Contrary to this opinion, I must say that constructing a repository from scratch is instructive. It requires first that you define a few domain classes and mappers, then at least a simple access data layer, and finally that you put all of these pieces to work together. To demonstrate how useful this experience can be, in previous parts of this series I started creating a sample web program whose main objective is to handle collections of user objects through a basic repository.

So far, I've demonstrated how to construct the domain, mapping and data access layers of this sample program, and it has been a fairly straightforward process. As I stated above, though, a repository bases most of its functionality on working with collections of domain objects, a process that we have yet to discuss.

To tackle that issue, in this fifth tutorial of the series I'm going to define a couple of classes that will be responsible for treating collections of entities as if they were array elements. Needless to say, the creation of these classes will require the use of the Countable, Iterator and ArrayAccess native PHP interfaces. If you're interested in learning the full details of this creation process, start reading right now!

The generic mapper previously developed

As usual, before I show you the definitions of the collection classes mentioned in the introduction, we'll  take a quick look at the finished version of the data mappers developed in the last tutorial.

With that said, here's the abstract mapper that permits you to work with generic entities. Its source code is as follows:

(DataMapperAbstract.php)

<?php

abstract class DataMapperAbstract implements DataMapperInterface
{
    protected $_adapter;
    protected $_collection;
    protected $_entityClass;
    protected $_entityTable; 
        
    /**
     * Class constructor
     */
    public function __construct(MySQLAdapter $adapter, CollectionAbstract $collection, array $entityOptions = array())
    {
        $this->_adapter = $adapter;
        $this->_collection = $collection;
        if (isset($entityOptions['entityClass'])) {
            $this->setEntityClass($entityOptions['entityClass']);
        }
        if (isset($entityOptions['entityTable'])) {
            $this->setEntityTable($entityOptions['entityTable']);
        }
        $this->init();
    }
   
    /**
     * Initialize the data mapper here
     */
    public function init(){}
   
    /**
     * Get the instance of the database adapter
     */
    public function getAdapter()
    {
        return $this->_adapter;
    }
   
    /**
     * Get the collection the mapper uses
     */
    public function getCollection()
    {
        return $this->_collection;
    }
   
    /**
     * Set the class for reconstructing entities
     */
    public function setEntityClass($entityClass)
    {
        if (!class_exists($entityClass, false)) {
            throw new DataMapperException('The specified entity class ' . $entityClass . ' does not exist.');
        }
        $this->_entityClass = $entityClass;
    }
   
    /**
     * Get the class for reconstructing entities
     */
    public function getEntityClass()
    {
        return $this->_entityClass;
    }
   
    /**
     * Set the entity database table the mapper works with
     */
    public function setEntityTable($entityTable)
    {
        if (!is_string($entityTable) || empty($entityTable)) {
            throw new DataMapperException('The specified entity table ' . $entityTable . ' is invalid.');
        }
        $this->_entityTable = $entityTable;
    }
   
    /**
     * Get the entity database table the mapper works with
     */
    public function getEntityTable()
    {
        return $this->_entityTable;
    }
   
    /**
     * Find an entity by its ID
     */
    public function findById($id)
    {
        $id = (int) $id;
        $this->_adapter->select($this->_entityTable, "id = $id");
        if ($data = $this->_adapter->fetch()) {
            return new $this->_entityClass($data);
        }
        return null;
    }
   
    /**
     * Find all the entities
     */
    public function findAll()
    {
        $this->_collection->clear();
        $this->_adapter->select($this->_entityTable);
        while ($data = $this->_adapter->fetch()) {
            $this->_collection->add($data['id'], new $this->_entityClass($data));   
        }
        return $this->_collection->count() !== 0 ?
               $this->_collection :
               null;
    }
   
    /**
     * Find all the entities that match the specified criteria
     */
    public function search($criteria)
    {
        $this->_collection->clear();
        $this->_adapter->select($this->_entityTable, $criteria);
        while ($data = $this->_adapter->fetch()) {
            $this->_collection->add($data['id'], new $this->_entityClass($data));   
        }
        return $this->_collection->count() !== 0 ?
               $this->_collection :
               null;
    }
     
    /**
     * Insert a new row in the table corresponding to the specified entity
     */
    public function insert(EntityAbstract $entity)
    {
        $data = $entity->toArray();
        return $this->_adapter->insert($this->_entityTable, $data);
    }
   
    /**
     * update the row in the table corresponding to the specified entity
     */
    public function update(EntityAbstract $entity)
    {
        $id = (int) $entity->id;
        $data = $entity->toArray();
        unset($data['id']);
        return $this->_adapter->update($this->_entityTable, $data, "id = $id");
    }
    
    /**
     * Delete the row in the table corresponding to the specified entity or ID
     */
    public function delete($id)
    {
        if ($id instanceof EntityAbstract) {
            $id = (int) $id->id;
        }
        return $this->_adapter->delete($this->_entityTable, "id = $id");
    }               
}

While the logic implemented by the above generic mapper seems hard to grasp, it's really quite simple to understand. All that this class does is perform CRUD operations using generic entities (please, notice the injection into the constructor of an object of type CollectionAbstract). With this abstract parent comfortably seated on top of the hierarchy, building a concrete mapper that handles user entities is ridiculously easy. What's more, the following snippet shows how to accomplish this in a nutshell. Check it out: 

(UserMapper.php)

<?php

class UserMapper extends DataMapperAbstract
{
    protected $_entityClass = 'User';
    protected $_entityTable = 'users'; 
   
    /**
     * Class constructor
     */
     public function __construct(MySQLAdapter $adapter, UserCollection $collection, array $entityOptions = array())
     {
         parent::__construct($adapter, $collection, $entityOptions);
     }  
}

There you have it. Thanks to all the functionality encapsulated by the earlier abstract class, the implementation of a user mapper is indeed a breeze. But before you start playing with this subclass, take a closer look at the constructors of both classes. Yes, apart from injecting an instance of the already familiar MySQL adapter, they accept the two collaborators mentioned at the beginning, which are responsible for handling collections of entities.

For obvious reasons, these classes must be properly defined. That's exactly what I plan to do in the following section. Therefore, to learn more about this process, click on the link below and read the lines to come.



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: