Roll Your Own Repository in PHP: a Final Example

Welcome to the conclusion to an eight-part article series on constructing your own repository from scratch in PHP. In this article, we’ll go through an example that shows you how the repository we completed in the previous article actually performs. You’ll see all of the classes we created in action.

Constructing a repository in PHP can be an enriching and fun experience. The process not only demands that you create the class (or classes) that implements this additional abstraction layer, but requires you to build other structures closely related to Domain-Driven Design (DDD), such as entities, collections and mappers — not to mention the typical persistence layer, which in most use cases rests on a relational database.

To demonstrate that the my previous statement is not simply a biased opinion, in earlier parts of this series I went through the development of a sample web application, whose main task was to handle collections of user entities via a simple repository. This user repository defined a few straightforward methods, which permitted the concentration of query code in one single point and the retrieval of the corresponding collections of domain objects according to a certain number of basic conditions.

While speaking of a repository’s benefits can be pretty useful, at least from a theoretical point of view, the best way to understand its driving logic and illustrate its actual functionality is by example. With that in mind, in this final installment of the series I’m going to set up a concluding example. It will put all of the sample classes developed so far to work together. In doing so, you’ll realize how easy it is to manipulate collections of entities through this basic repository class.

Well, the spotlights are on, the audience is already clapping and the curtain is starting to rise, so let’s get rid of the preliminaries and start writing some functional code. It’s show time!

Summary: all of the sample classes created so far

Before I start creating the hands-on example mentioned in the introduction, it’d be helpful to list all of the sample classes developed so far. This way, you can have them available in one place for further analysis or editing. Get ready to be confronted with long chunks of code. You’ve been warned!

First, here is the autoloader, which lazy-loads source classes using the SPL stack:

(Autoloader.php)

<?php

class Autoloader
{
    private static $_instance;
   
    /**
     * Get the Singleton instance of the autoloader
     */
    public static function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new self;
        }
        return self::$_instance;
    } 
   
    /**
     * Reset the instance of the autoloader
     */
    public static function resetInstance()
    {
        self::$_instance = null;
    }
   
    /**
     * Class constructor
     */
    private function __construct()
    {
        spl_autoload_register(array(__CLASS__, ‘load’));
    }
   
    /**
     * Prevent cloning the instance of the autoloader
     */
    private function __clone(){}

    /**
     * Load a given class or interface
     */
    public static function load($class)
    {
        $file = $class . ‘.php’;
        if (!file_exists($file)) {
            throw new ClassNotFoundException(‘The file containing the requested class ‘ . $class . ‘or interface was not found.’);
        }
        require $file;
        if (!class_exists($class, false) && !interface_exists($class, false)) {
            throw new ClassNotFoundException(‘The requested class or interface ‘ . $class . ‘ was not found.’);
        }
    }  
}

 

(ClassNotFoundException.php)

<?php

class ClassNotFoundException extends Exception{}

Now that you’ve recalled how the above autoloader works, it’s time to show the classes that make up the domain layer of this sample application. As you’ll remember, these are tasked with modeling generic and user entities respectively. The definition of the first of these classes is as follows:

(EntityAbstract.php)

<?php

abstract class EntityAbstract
{
    protected $_values = array();
    protected $_allowedFields = array();
   
    /**
     * Class constructor
     */
    public function __construct(array $data)
    {
        foreach ($data as $name => $value) {
            $this->$name = $value;
        }
    }
   
    /**
     * Assign a value to the specified field via the corresponding mutator (if it exists);
     * otherwise, assign the value directly to the ‘$_values’ protected array
     */
    public function __set($name, $value)
    {  
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException(‘The field ‘ . $name . ‘ is not allowed for this entity.’); 
        }
        $mutator = ‘set’ . ucfirst($name);
        if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
            $this->$mutator($value);          
        }
        else {
            $this->_values[$name] = $value;
        }   
    }
   
    /**
     * Get the value assigned to the specified field via the corresponding getter (if it exists);
    otherwise, get the value directly from the ‘$_values’ protected array
     */
    public function __get($name)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException(‘The field ‘ . $name . ‘ is not allowed for this entity.’);   
        }
        $accessor = ‘get’ . ucfirst($name);
        if (method_exists($this, $accessor) && is_callable(array($this, $accessor))) {
            return $this->$accessor;   
        }
        if (array_key_exists($name, $this->_values)) {
            return $this->_values[$name];
        }
        throw new EntityException(‘The field ‘ . $name . ‘ has not been set for this entity yet.’);
    }
       
    /**
     * Unset the specified property from the entity
     */  
    public function __unset($name)
    {
        if (array_key_exists($name, $this->_values)) {
            unset($this->_values[$name]);
        }
    }
      
    /**
     * Get the values assigned to the fields of the entity
     */
    public function toArray()
    {
        return $this->_values;
    }             
}

 

(EntityException.php)

<?php

class EntityException extends Exception {}

Well, considering that the previous abstract parent already encapsulates most of the functionality required to create generic entities, deriving a subclass that models users specifically is this simple:

(User.php)

<?php

class User extends EntityAbstract
{  
    protected $_allowedFields = array(‘id’, ‘fname’, ‘lname’, ‘email’);
   
    /**
     * Set the user’s ID
     */
    public function setId($id)
    {
        if(!filter_var($id, FILTER_VALIDATE_INT, array(‘options’ => array(‘min_range’ => 1, ‘max_range’ => 99999)))) {
            throw new EntityException(‘The specified ID ‘ . $id . ‘ is invalid.’);
        }
        $this->_values['id'] = $id;
    }
   
    /**
     * Set the user’s first name
     */ 
    public function setFname($fname)
    {
        if (strlen($fname) < 2 || strlen($fname) > 32) {
            throw new EntityException(‘The specified first name ‘ . $fname . ‘ is invalid.’);
        }
        $this->_values['fname'] = $fname;
    }
       
    /**
     * Set the user’s last name
     */
    public function setLname($lname)
    {
        if (strlen($lname) < 2 || strlen($lname) > 32) {
            throw new EntityException(‘The specified last name ‘ . $lname . ‘ is invalid.’);
        }
        $this->_values['lname'] = $lname;
    }
   
    /**
     * Set the user’s email address
     */
    public function setEmail($email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new EntityException(‘The specified email address ‘ . $email . ‘ is invalid.’);
        }
        $this->_values['email'] = $email;
    }                   
}

Done. Having included the pair of classes that compose the domain layer, the next step is to show the ones responsible for implementing the persistence layer. In fact, this new layer is integrated by an interface and a MySQL abstraction class, and their corresponding definitions are included below: 

(DatabaseAdapterInterface.php)

<?php

interface DatabaseAdapterInterface
{
    public function connect();
   
    public function disconnect(); 
   
    public function query($query);
   
    public function fetch(); 
   
    public function select($table, $where, $fields, $order, $limit, $offset);
   
    public function insert($table, array $data);
   
    public function update($table, array $data, $where);
   
    public function delete($table, $where);
   
    public function getInsertId();
   
    public function countRows();
   
    public function getAffectedRows();
}

 

(MySQLAdapter.php)

<?php

class MySQLAdapter implements DatabaseAdapterInterface
{
    protected $_config = array();
    protected $_link;
    protected $_result;
    protected static $_instance;
   
    /**
     * Get the Singleton instance of the class
     */
    public static function getInstance(array $config = array())
    {
        if (self::$_instance === null) {
            self::$_instance = new self($config);
        }
        return self::$_instance;
    }
   
    /**
     * Class constructor
     */
    protected function __construct(array $config)
    {
        if (count($config) !== 4) {
            throw new MySQLAdapterException(‘Invalid number of connection parameters.’);  
        }
        $this->_config = $config;
    }
   
    /**
     * Prevent cloning the instance of the class
     */
    protected function __clone(){}
   
    /**
     * Connect to MySQL
     */
    public function connect()
    {
        // connect only once
        if ($this->_link === null) {
            list($host, $user, $password, $database) = $this->_config;
            if ((!$this->_link = @mysqli_connect($host, $user, $password, $database))) {
                throw new MySQLAdapterException(‘Error connecting to MySQL : ‘ . mysqli_connect_error());
            }
            unset($host, $user, $password, $database);      
        }
    }

    /**
     * Execute the specified query
     */
    public function query($query)
    {
        if (!is_string($query) || empty($query)) {
            throw new MySQLAdapterException(‘The specified query is not valid.’);  
        }
        // lazy connect to MySQL
        $this->connect();
        if (!$this->_result = mysqli_query($this->_link, $query)) {
            throw new MySQLAdapterException(‘Error executing the specified query ‘ . $query . mysqli_error($this->_link));
        }
    }
   
    /**
     * Perform a SELECT statement
     */
    public function select($table, $where = ”, $fields = ‘*’, $order = ”, $limit = null, $offset = null)
    {
        $query = ‘SELECT ‘ . $fields . ‘ FROM ‘ . $table
               . (($where) ? ‘ WHERE ‘ . $where : ”)
               . (($limit) ? ‘ LIMIT ‘ . $limit : ”)
               . (($offset && $limit) ? ‘ OFFSET ‘ . $offset : ”)
               . (($order) ? ‘ ORDER BY ‘ . $order : ”);
        $this->query($query);
        return $this->countRows();
    }
   
    /**
     * Perform an INSERT statement
     */ 
    public function insert($table, array $data)
    {
        $fields = implode(‘,’, array_keys($data));
        $values = implode(‘,’, array_map(array($this, ‘quoteValue’), array_values($data)));
        $query = ‘INSERT INTO ‘ . $table . ‘(‘ . $fields . ‘)’ . ‘ VALUES (‘ . $values . ‘)’;
        $this->query($query);
        return $this->getInsertId();
    }
   
    /**
     * Perform an UPDATE statement
     */
    public function update($table, array $data, $where = ”)
    {
        $set = array();
        foreach ($data as $field => $value) {
            $set[] = $field . ‘=’ . $this->quoteValue($value);
        }
        $set = implode(‘,’, $set);
        $query = ‘UPDATE ‘ . $table . ‘ SET ‘ . $set
               . (($where) ? ‘ WHERE ‘ . $where : ”);
        $this->query($query);
        return $this->getAffectedRows(); 
    }
   
    /**
     * Perform a DELETE statement
     */
    public function delete($table, $where = ”)
    {
        $query = ‘DELETE FROM ‘ . $table
               . (($where) ? ‘ WHERE ‘ . $where : ”);
        $this->query($query);
        return $this->getAffectedRows();
    }
   
    /**
     * Single quote the specified value
     */
    public function quoteValue($value)
    {
        if ($value === null) {
            $value = ‘NULL’;
        }
        else if (!is_numeric($value)) {
            $value = "’" . mysqli_real_escape_string($this->_link, $value) . "’";
        }
        return $value;
    }
   
    /**
     * Fetch a single row from the current result set (as an associative array)
     */
    public function fetch()
    {
        if ($this->_result !== null) {
            if ((!$row = mysqli_fetch_array($this->_result, MYSQLI_ASSOC))) {
                $this->freeResult();
                return false;
            }
            return $row;
        }
    }

    /**
     * Get the insertion ID
     */
    public function getInsertId()
    {
        return $this->_link !== null ?
               mysqli_insert_id($this->_link) :
               null; 
    }
   
    /**
     * Get the number of rows returned by the current result set
     */ 
    public function countRows()
    {
        return $this->_result !== null ?
               mysqli_num_rows($this->_result) :
               0;
    }
   
    /**
     * Get the number of affected rows
     */
    public function getAffectedRows()
    {
        return $this->_link !== null ?
               mysqli_affected_rows($this->_link) :
               0;
    }
   
    /**
     * Free up the current result set
     */
    public function freeResult()
    {
        if ($this->_result !== null) {
            mysqli_free_result($this->_result);  
        }
    }
   
    /**
     * Close explicitly the database connection
     */
    public function disconnect()
    {
        if ($this->_link !== null) {
            mysqli_close($this->_link);
            $this->_link = null;
        }
    }
   
    /**
     * Close automatically the database connection when the instance of the class is destroyed
     */
    public function __destruct()
    {
        $this->disconnect();
    }
}

 

(MySQLAdapterException.php)

<?php

class MySQLAdapterException extends Exception{}

Still with me? Great. Now, take a deep breath and look at the following code snippet, which includes the interface and the pair of mapper classes that make up the mapping layer of this sample application. Check them out:

(DataMapperInterface.php)

<?php

interface DataMapperInterface
{
    public function findById($id);
   
    public function findAll();
   
    public function search($criteria);
   
    public function insert(EntityAbstract $entity);
   
    public function update(EntityAbstract $entity);
   
    public function delete($id);         
}

 

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

 

(DataMapperException.php)

<?php

class DataMapperException extends Exception{}

 

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

Whew! That was a lot to read, wasn’t it? But we’re not done yet. It’s necessary to show the classes whose responsibility it is to handle collections of entities. So, here they are:

(CollectionAbstract.php)

<?php

abstract class CollectionAbstract implements Iterator, Countable, ArrayAccess
{
    protected $_entities = array();
   
    /**
     * Get the entities stored in the collection
     */
    public function getEntities()
    {
        return $this->_entities;
    }
   
    /**
     * Clear the collection
     */
    public function clear()
    {
        $this->_entities = array();
    }
    
    /**
     * Reset the collection (implementation required by Iterator Interface)
     */    
    public function rewind()
    {
        reset($this->_entities);
    }
   
    /**
     * Get the current entity in the collection (implementation required by Iterator Interface)
     */
    public function current()
    {
        return current($this->_entities);
    }
   
    /**
     * Move to the next entity in the collection (implementation required by Iterator Interface)
     */
    public function next()
    {
        next($this->_entities);
    }
   
    /**
     * Get the key of the current entity in the collection (implementation required by Iterator Interface)
     */
    public function key()
    {
        return key($this->_entities);
    }
   
    /**
     * Check if there are more entities in the collection (implementation required by Iterator Interface)
     */
    public function valid()
    {
        return (boolean) $this->current();
    }
   
    /**
     * Count the number of entities in the collection (implementation required by Countable Interface)
     */
    public function count()
    {
        return count($this->_entities);
    }
   
    /**
     * Add an entity to the collection (implementation required by ArrayAccess interface)
     */
    public function offsetSet($key, $entity)
    {
        if ($key === null) {
            if (!in_array($key, $this->_entities, true)) {
                $this->_entities[] = $entity;
                return;
            }
        }
        else if (!array_key_exists($key, $this->_entities)) {
            $this->_entities[$key] = $entity;
        }
    }
   
    /**
     * Remove an entity from the collection (implementation required by ArrayAccess interface)
     */
    public function offsetUnset($key)
    {
        if ($key instanceof EntityAbstract) {
            $entities = array();
            foreach ($this->_entities as $_entity) {
                if ($_entity !== $key) {
                    $entities[] = $_entity;
                }  
            }
            $this->_entities = $entities;
            return;
        }
        if (array_key_exists($key, $this->_entities)) {
            unset($this->_entities[$key]);
        }
    }
   
    /**
     * Get the specified entity in the collection (implementation required by ArrayAccess interface)
     */
    public function offsetGet($key)
    {
        if (array_key_exists($key, $this->_entities)) {
            return $this->_entities[$key];
        }
    } 
   
    /**
     * Check if the specified entity exists in the collection (implementation required by ArrayAccess interface)
     */    
    public function offsetExists($key)
    {
        return array_key_exists($key, $this->_entities);
    }
}

 

(UserCollection.php)

<?php

class UserCollection extends CollectionAbstract
{
    /**
     * Add a user to the collection
     */
    public function add($key, User $user)
    {
        $this->offsetSet($key, $user);
    }     
}

Even though a lot of code has been covered so far, don’t relax yet; there’s still one thing left to do. But what is it? Well, for obvious reasons it’s necessary to show the user repository and the factory that takes care of instantiating all of its dependencies. Below you’ll find the definitions of these classes: 

(RepositoryFactory.php)

<?php

class RepositoryFactory
{
    /**
     * Create a repository based on the given name
     */
    public function create($name, array $options = array())
    {
        $name = ucfirst(strtolower($name));
        $adapter    = MySQLAdapter::getInstance();
        $collection = $name . ‘Collection’;
        $mapper     = $name . ‘Mapper’;
        $repository = $name . ‘Repository’;
        return new $repository(
            new $mapper($adapter, new $collection, $options));
    } 
}

 

(UserRepository.php)

<?php

class UserRepository
{
    protected $_userMapper;
   
    /**
     * Class constructor
     */
    public function __construct(UserMapper $userMapper)
    {
        $this->_userMapper = $userMapper;   
    }
   
    /**
     * find users by their first names
     */
    public function findByFirstName($fname)
    {
        return $this->_userMapper->search("fname = ‘$fname’");
    }
   
    /**
     * find users by their last names
     */
    public function findByLastName($lname)
    {
        return $this->_userMapper->search("lname = ‘$lname’");
    }
   
    /**
     * find users by their email addresses
     */
    public function findByEmail($email)
    {
        return $this->_userMapper->search("email = ‘$email’");
    }
   
    /**
     * Insert a new user
     */
    public function insert(User $user)
    {
        $this->_userMapper->insert($user);
    }
   
    /**
     * Delete an existing user
     */
    public function delete($id)
    {
        $this->_userMapper->delete($id);
    }            
}

Mission accomplished, at least for now. Having made a quick summary of all the classes and interfaces that comprise this example application, it’s time to develop the example mentioned at the beginning. It will show how to put those classes to work side by side, and how to use the previous user repository in a truly useful fashion.

This new example will be created in the upcoming segment. Therefore, to learn more about it, click on the link below and keep reading.

{mospagebreak title=The user repository in action}

To demonstrate how to take advantage of the functionality offered by the user repository that you saw in the previous section, I’m going to use a simple MySQL table, which has been previously populated with data corresponding to a few trivial users. The sample table looks like this:

So far, nothing unexpected, right? As you can see, the table contains four columns that store the ID (an autonumeric, unsigned primary key) of each user, as well as their first and last names and their email addresses. Now, if I wanted to retrieve some of the stored users by specifying some basic conditions, the process would be this simple:   

<?php

// include the Autoloader
require_once ‘Autoloader.php’;
Autoloader::getInstance();

// create an instance of the Repository factory
$repositoryFactory = new RepositoryFactory;

// create a user repository
$userRepository = $repositoryFactory->create(‘user’);

// fetch users by their first names
$users = $userRepository->findByFirstName(‘Alex’);
foreach ($users as $user) {
    echo $user->fname . ‘ ‘ . $user->lname . ‘ ‘ . $user->email . ‘<br />’;
}
/* displays the following
Alex Gervasio alex@domain.com
*/

// fetch users by their last names
$users = $userRepository->findByLastName(‘Wilson’);
foreach ($users as $user) {
    echo $user->fname . ‘ ‘ . $user->lname . ‘ ‘ . $user->email . ‘<br />’;
}
/* displays the following
Sandra Wilson sandra@domain.com
Jennifer Wilson jennifer@domain.com
*/

// fetch users by their email addresses
$users = $userRepository->findByEmail(‘susan@domain.com’);
foreach ($users as $user) {
    echo $user->fname . ‘ ‘ . $user->lname . ‘ ‘ . $user->email . ‘<br />’;
}
/* displays the following
Susan Norton susan@domain.com
Susan Hamilton susan@domain.com
*/

As the above code fragment shows, putting the user repository into action and fetching different collections of user entities that match a specific condition is amazingly easy! In this case, the criteria used to retrieve the previous users are very simple. It’s feasible, however, to create a repository capable of working with more complex conditions than simple first and last names. The possibilities are endless. 

Naturally, the repository can be used to insert new users into the above table, and even to remove existing users through the same API. Therefore, in the last section I’m going to build a final example that will show how to perform these additional operations.

Now, jump ahead and keep reading.  

One step further: adding and deleting user entities

As I noted in the section that you just read, the user repository makes it really easy to add new users to the persistence layer, and to delete existing ones. If you want to see how these operations can be executed in a few steps, simply pay attention to the code snippet below, which will hopefully be self-explanatory:

<?php

// include the Autoloader
require_once ‘Autoloader.php’;
Autoloader::getInstance();

// create an instance of the Repository factory
$repositoryFactory = new RepositoryFactory;

// create a user repository
$userRepository = $repositoryFactory->create(‘user’);

// insert a new user
$user = new User(array(‘fname’ => ‘Mary’, ‘lname’ => ‘Anderson’, ‘email’ => ‘mary@domain.com’));
$userRepository->insert($user);

// delete an existing user
$userRepository->delete(1);

There you have it. Thanks to the functionality provided by the repository’s “insert()” and “delete()” methods, inserting and deleting users from the persistence layer is indeed a straightforward process that can be tackled in a snap.

Needless to say, there’s plenty of room to experiment here, and extend the current capability of the repository in such a way that it can perform more complex queries. Considering this scenario, I encourage you to tweak this class in many clever ways and add your own custom methods to it. In doing so, you’ll acquire a better background in mastering one of the big pillars of Domain-Driven Design.   

Final thoughts

Finally, we’ve come to the end of this series. From a strong hands-on standpoint, I attempted to demonstrated in a step-by-step fashion how to build a repository from scratch. Hopefully, during the development process, you learned not only how to create such an abstraction layer, but how to apply some concepts inherent to modern application design, such as building models that are loosely coupled to the persistence mechanism.

If you feel that the implementation of Active Record, widely promulgated by Rails and even other well-known PHP frameworks out there, doesn’t suit your needs any longer and want to create programs that scale up well in different conditions and with multiple infrastructures, then this series might help you start migrating to more flexible solutions.

See you in the next PHP development tutorial!

Google+ Comments

Google+ Comments