Home arrow PHP arrow Page 2 - Roll Your Own Repository in PHP: Data Mapper Finishing Touches

Saving and deleting entity-related table records with additional methods - PHP

In this fourth tutorial of the series, I added three methods to the data mapper class. They will be used for saving and deleting the table rows related to a specified entity. With this parent class fully implementing the methods declared by the “DataMapperInterface” interface, it will be easy to create a refined subclass that can specifically map user objects.

TABLE OF CONTENTS:
  1. Roll Your Own Repository in PHP: Data Mapper Finishing Touches
  2. Saving and deleting entity-related table records with additional methods
By: Alejandro Gervasio
Rating: starstarstarstarstar / 4
December 01, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

In reality, giving the earlier mapper the ability to save and remove the table rows related to a specified entity is a straightforward process that can be tackled in a snap. Don’t believe me? Take a peek at the following methods, which perform these tasks in an approachable way:

/**
 * 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");
}

Mission accomplished. Thanks to the functionality aggregated by the above methods, the mapper class is capable of retrieving entities, as well as saving and removing them from their associated MySQL table. But wait a minute! Showing these extra methods as if they were isolated pieces doesn’t help too much if you're trying to see the mapper as a whole. Therefore, in the coming segment I’m going to list the mapper’s full source code, so you can see how it looks in its finished state.

So just keep reading. 

The finished version of the data mapper

In consonance with the concepts deployed above, below I included the complete version of the previous data mapper. This should help you understand how it works more easily. Here it is:

(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");
    }               
}

For obvious reasons, the construction of a generic mapper like the one above is not an effective solution in all cases; this depends strongly on the type of application being developed, the domain objects involved in this process, and so forth. In this specific case, though, where the only domain objects that will be manipulated are simple user objects, the implementation of a generic mapper makes a lot of sense, trust me. And if you’re still reluctant to accept this, see for yourself how easy it is to create a mapper that only works with user entities:   

(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);
     }  
}

That was easy to code, wasn’t it? As the previous code sample shows, building a concrete user mapper is only a question of subclassing the base abstract parent and overriding its constructor. In fact, the last step is optional, but it assures that the collection injected into the mapper will be one that handles only user objects.

And now that I've managed to create a user mapper class in a few easy steps, we've gotten a bit closer to the construction of a fully-functional user repository. With the domain, data access and mapping layers of this sample application already up and running, it’s really tempting to take the plunge and start building the repository, right? Well, let's not move so fast. A repository in its simplest form should be capable of handling collections of domain objects, a process that hasn’t been demonstrated so far.

However, this is about to change. In the next tutorial I’m going to develop a couple of classes that will be tasked specifically with manipulating entities, and more specifically, user objects.  

Final thoughts

That’s it for now. In this fourth tutorial of the series, I added three methods to the previous data mapper class, which came in handy for saving and deleting the table rows related to a specified entity. With this parent class fully implementing the methods declared by the “DataMapperInterface” interface, it was really easy to create a refined subclass, capable of specifically mapping user objects. In simple terms, the whole subclassing process was reduced to overriding the parent’s constructor.

At this point, it could be said that it’s safe to use the earlier “DataMapperAbstract” class to spawn concrete mappers like the “UserMapper” shown before. But, is this completely true? Not really. As you may have noticed, there’s a small catch here. The abstract parent and its concrete child inject, through their respective constructors, a pair of collaborators that are instances of two classes called “CollectionAbstract” and “UserCollection,” which remain undefined.

As their names imply, these classes are countable and “iteratable” (if the term is applicable) structures that will allow you to handle collections of entities as if they were array elements. It’s necessary, therefore, to show the source code of these classes, to truly complete the mapping layer.

Since this will be accomplished in the forthcoming part of the series, I suggest you don’t miss it!



 
 
>>> 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: