Login

Roll Your Own Repository in PHP: Defining a Generic Data Mapper

In this third part of the series, I add a simple mapping layer to the sample application. This layer will be composed of a single interface and a generic mapper class.

Understanding repositories may sound difficult, but they’re actually pretty straightforward, especially if you have already ventured into the terrain of Domain-Driven Design (DDD). A repository is an additional abstraction layer placed between the domain and mapping layers. It is used in applications with a rich domain for handling collections of domain objects that satisfy a specified criteria. In a typical implementation, a repository concentrates, behind an intuitive API, all of the logic required to query object collections. This significantly reduces code replication.

Naturally, the most effective way to understand the functionality of a repository in PHP (or in any other programming language, of course) is by means of a concrete example that justifies the upfront work required to construct this extra abstraction layer. In keeping with this idea, in the two previous tutorials of this series I went through the development of the domain and data access layers of a sample web application. The application’s main objective is to manipulate a bunch of simple user objects that persist through a MySQL database.

Now that we have two layers existing independently of each other, the next step in the implementation of a functional user repository is to create yet another layer. It will act like a mediator between the previous ones without introducing an unnecessary, undesired coupling effect. As you may have already guessed from the article’s title, this layer will be comprised of a set of mapping classes, which can perform CRUD operations on domain objects, and handle collections of them in a fairly simple fashion.

Now that the subject of this third installment of the series has been clearly outlined, it’s time to start creating the mapping classes. Let’s get going!

Review: the data access layer

As always, before I start defining the mapping classes of this sample application, I’d like to spend a few moments reintroducing the source code corresponding to the interface and the implementing class created in the preceding tutorial. These were the building blocks of the application’s data access layer.

First, here’s the interface, which defines a contract that must be fulfilled by any class that interacts with a specific database server. Pay attention to it, please:

With the small number of methods it declares, we can’t say much about the “DatabaseAdapterInterface” interface, except that it establishes a contract that must be agreed to by all of the database adapters created from this point onward. In consonance with this, below is the definition of a concrete class which not only implements the interface, but abstracts common operations that are performed with MySQL:

/**
* 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{}

Even though the source code is somewhat extensive, the above “MySQLAdapter” class is a simple wrapper for the “mysqli” PHP extension that allows it to execute typical operations on MySQL. These operations include fetching rows, handling result sets, and running queries. You’ve probably seen many classes similar to this one, so I’m not going to spend a long time explaining how it does its business.

Instead, it’s time to summarize what we’ve achieved so far. On one hand, this sample application contains a domain layer responsible for creating generic entities, and more specifically user objects; on the other hand, there’s a data access layer capable of dealing directly with the underlying persistence mechanism, which in this case turns out to be a MySQL database. The question is, how can these layers be bridged while preserving their mutual independence?

Well, as I said before, the implementation of a mapping layer will do the trick nicely. Thus, in the next section I’m going to start defining the structure and further behavior of this new layer.

To see how this will be done, jump ahead and read the lines to come.

{mospagebreak title=The mapping layer}

Constructing the mapping layer is a two-steps process. First, it’s necessary to define a contract (pretty similar to the one used with the database adapters discussed previously), which must be satisfied by all of the mapper classes created afterward. And second, the generation of these classes will be achieved by subclassing a generic mapper, even though this last step is optional.

With that said, here’s the interface that defines the structure of the aforementioned contract:

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

Although the definition of the above “DataMapperInterface” interface is pretty short, it’s clear to see that it declares a bunch of methods that can be used for performing CRUD operations against the data access layer. Apart from the classic finders that can be found in most mappers, like “findById()” and “findAll(),” there are a few additional methods that allow you to save and delete records associated with a specified entity. Since the role of an interface is to define a contract, and nothing else, it’s up to the mappers to concretely implement these entity-related operations. Got that? Great.

Now that you understand the purpose of defining the earlier interface, the next thing that we need to do is create a class that implements the interface in question, and also encapsulates common functionality shared by concrete mapping classes. This will be done in the section below, so keep reading.

Implementing the previous interface: defining a generic mapper

In keeping with the concepts deployed above, the last step we must take to get the mapping layer of this sample application finished is to build a generic mapper class. For obvious reasons, this class will be declared abstract, and its initial definition will be as follows:

While it’s fair to admit that, at a glance, the source code of the above “DataMapperAbstract” class looks somewhat complex, this is a misleading impression. In fact, the class is a generic mapper that concentrates most of the functionality required to perform CRUD operations in a MySQL database table. It’s that simple, really.

In addition, you should notice that its constructors inject two collaborators. The first one is an instance of the previous MySQL adapter, which is utilized internally for executing the aforementioned operations. The second one is an abstract collection object, which is used within the “findAll()” and “search()” methods. You’re wondering where the latter comes from, right? This object is an instance of a countable, iteratable class responsible for handling collections of entities. But leave your troubles behind, at least for the moment, and don’t feel concerned about the implementation of this class; it’ll be covered in detail in upcoming tutorials in this series.

Needless to say, if you test the “DataMapperAbstract” class in its current state, you’ll get an ugly fatal error; it doesn’t fully implement all of the methods declared by the “DataMapperInterface” interface. Again, fear not, as these pending methods will be added to the class and discussed in depth in the following installment.

It’s safe to say, however, that the mapping layer of this sample application is near completion. It now includes a generic data mapper which can be easily subclassed to create concrete mappers, including the one responsible for handling user objects. This is a remarkable advance toward the implementation of a user repository.

Final thoughts

In this third episode of the series, I added to this sample application a simple mapping layer composed of a single interface and a generic mapper class. Even though the development of the latter is still incomplete, as it’s necessary to add the methods that save and delete entities, it can already be used effectively as a mediator between the domain and data access layers defined previously.

As I said before, the implementation of these missing methods will be covered in the next tutorial, so if you’re interested in learning how this will be done, don’t miss the upcoming part!