Roll Your Own Repository in PHP: Building a User Repository Class

In this sixth part of the series, I start using all of the sample classes defined previously to create a basic user repository class. The partial construction of this class calls for some extra work, such as defining a repository factory. The development process, as you’ll see, is pretty straightforward, and shows how useful this kind of abstraction layer can be for concentrating querying code behind an intuitive API.

One of the biggest advantages of PHP is its pragmatism (which can turn into a curse when used inappropriately), as it allows developers to quickly start building functional web programs without having to tackle a steep learning curve or deal with a complicated API. As an application becomes larger and more complex, however, it is sometimes necessary to sacrifice a little of this pragmatism and construct some additional abstraction layers that permit you to keep the application modular and scalable for further improvements.

One typical case where an extra abstraction layer can be useful is in the construction of a repository, where it can help concentrate the query logic in a program that reveals a rich domain composed of a great variety of objects. Being a fundamental piece of Domain-Driven Design (DDD), a repository can be thought of as a mediator that acts between the domain and the mapping layers of a program. This mediator allows you to easily query collections of objects ,and produces the illusion of those collections residing in memory at the same time.

As with other topics related to applying the OOP paradigm in PHP, the most effective way to grasp the logic of a repository is by example. In keeping with this idea, in previous installments of this series I started building from scratch a sample web application. This application was responsible for manipulating simple user entities. So far, I’ve created the domain, mapping and data access layers corresponding to this application, and also defined a couple of additional classes that handle collections of entities through an array-like interface.

With all of these layers already up and running, the next logical step is to build a structure that permits you to put them to work together, while maintaining their isolation from each other. You’re probably wondering what that structure is, right? Well, the “glue” for the aforementioned layers is a user repository class, and in this article I’m going to partially implement it, so that you can understand its driving logic.

Ready to learn how to create such a repository in a few simple steps? Then begin reading!             

Reviewing the collection classes created previously

As I explained in the introduction, a repository must be capable of querying collections of domain objects through a friendly API. To restrict the responsibility of the classes that compose this sample application and make them perform only a few discrete tasks, in this case I decided to create two independent classes responsible for handling collections of objects.

The first of these classes is an abstract parent tasked with manipulating generic entities. Its source code looks like this:

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

As you can see from the above code fragment, the previous “CollectionAbstract” class is an implementer of the Countable, Iterator and ArrayAccess PHP built-in interfaces. Its objective is to handle collections of generic entities through the set of methods declared by the interfaces. Its underlying logic is that simple.

With the earlier abstract parent neatly encapsulating most of the functionality required for manipulating domain objects by using an array-like approach, building a class that only works with user entities is a breeze. If you’re still not convinced of this, then focus your attention on the following “UserCollection” subclass, which does exactly that: 

(UserCollection.php)

<?php

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

Were you expecting to see a longer code snippet? I don’t think so. In either case, the above “UserCollection” class only implements a single method. This method, called “add(),” assures that every object added to its internal collection will be effectively a user entity.

So far, so good. Provided that you’ve grasped how the two collection classes do their thing, it’s time to  start building the user repository mentioned at the beginning. You’ll finally see how all of the classes previously defined fit together.

To learn more on this topic, click on the link that appears below and read the following segment.

{mospagebreak title=Querying collections of user entities: building a user repository}

To keep things understandable and easy to follow, the repository class that I plan to construct here will only be able to retrieve user collections that match some simple criteria. It’s possible to encapsulate more complex query logic, however, as the layer that does the leg work is the mapping one, which in turn uses the persistence mechanism to run the actual SQL queries behind the scenes.    

But it’s time to stop talking and show the initial definition of the repository. This one, not surprisingly, is a new class. It’s called “UserRepository” and its source code is shown below:

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

While the finders exposed by the above “UserRepository” class are pretty simplistic, they show in a nutshell the relevant role that a repository plays when you’re abstracting query logic within an application. Notice that the names assigned to these methods are very explicit, which makes it easy to guess what they do. In this case, however, all the complexity required to find users by their first and last names, or even by their email addresses (although additional finders can be added in a snap) is delegated down the line — first to the mapper (injected via the repository’s constructor), and then to the persistence layer.  

At the risk of being repetitive, this illustrates an important concept that I’ve outlined from the very beginning: a repository retrieves collections of related domain objects as if they were actually residing in memory. Obviously, this is only an illusion to the eyes of client code, which consumes the repository’s API, ignoring the complexities involved in each retrieval process.

Having the previous user repository comfortably seated between the domain and mapping layers of this sample application comes with a hidden cost: due to the heavy use of dependency injection (AKA Inversion of Control), its instantiation demands the creation, in turn, of a bunch of additional collaborators.

In general, it’s considered a good programming habit to keep object instantiation isolated as much as possible from application logic. This means that the appropriate construction of the previous user repository should be delegated to a factory class, which would encapsulate the entire construction process in a way similar to what a dependency injection container does in more complicated scenarios. Given that, in the following section I’m going to define this repository factory, so you can quickly grasp how it works.

Now, just keep reading. 

Isolating object instantiation from application logic: building a repository factory

In most use cases, the implementation of a concrete factory is a fairly straightforward process that doesn’t present major difficulties. And certainly, the one that I included below, whose responsibility is to create repositories, isn’t the exception to this rule. Take a close look at it, please:

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

Mission accomplished. As you can see above, the earlier “RepositoryFactory” class only defines a single method called “create(),” which accepts the name of the repository that will be returned to client code. In addition, the factory follows a simple convention; it assumes that the associated collaborators will be named according to the task that each of them perform (a mapper will be suffixed “Mapper,” while a collection will be suffixed “Collection”). This basic rule permits you to create different kinds of repositories, including the one defined in the preceding section. What’s more, the process would be as easy as writing the following lines:

$repositoryFactory = new RepositoryFactory;
$userRepository = $repositoryFactory->create(‘user’);

Did you get the point as far as defining a factory that builds only repositories? I guess you did. In doing so, it’s possible to keep application logic neatly separated from the instantiation of repositories (and therefore of their dependencies), since the internals of this creation process have been encapsulated behind an isolated class.

Finally, it’s worth noting that the development of the previous user repository is still unfinished. It’s mandatory to provide it with the ability to add and remove user entities from the underlying MySQL database. This will be done in the forthcoming part of the series. 

Final thoughts

In this sixth part of the series, things became more interesting, since I finally started using all of the sample classes defined previously to create a basic user repository class. Even the partial construction of this class required us to do some extra work, such as defining a repository factory. In general the entire development process was pretty straightforward, and showed how useful this kind of abstraction layer can be when concentrating querying code behind an intuitive API.

As I said before, however, this example repository is still incomplete, as can’t add and delete user entities from the underlying persistence layer. Don’t feel concerned about this issue, though, as this extended functionality will be implemented in the coming installment of the series.

Don’t miss the next tutorial! 

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort