1. Home
  2. Documentation
  3. Tutorials
  4. MVC Tutorials
  5. In-Depth Tutorial
  6. Preparing for Different Databases

In-Depth Tutorial

In This Article

Preparing for Different Databases

In the previous chapter, we created aPostRepository that returns some datafrom blog posts. While the implementation was adequate for learning purposes, itis quite impractical for real world applications; no one would want to modifythe source files each time a new post is added! Fortunately, we an always turnto databases for the actual storage of posts; all we need to learn is how tointeract with databases within our application.

There's one small catch: there are many database backend systems, includingrelational databases, documentent databases, key/value stores, and graphdatabases. You may be inclined to code directly to the solution that fits yourapplication's immediate needs, but it is a better practice to create anotherlayer in front of the actual database access that abstracts the databaseinteraction. Therepository approach we used in the previous chapter is onesuch approach, primarily geared towardsqueries. In this section, we'll expandon it to addcommand capabilities for creating, updating, and deletingrecords.

What is database abstraction?

"Database abstraction" is the act of providing a common interface for alldatabase interactions. Consider a SQL and a NoSQL database; both have methodsfor CRUD (Create, Read, Update, Delete) operations. For example, to query thedatabase against a given row in MySQL you might use

$results = mysqli_query('SELECT foo FROM bar')`;

However, for MongoDB, for example you'd use something like:

$results = $mongoDbClient->app->bar->find([], ['foo' => 1, '_id' => 0])`;

Both engines would give you the same result, but the execution is different.

So if we start using a SQL database and write those codes directly into ourPostRepository and a year later we decide to switch to a NoSQL database, theexisting implementation is useless to us. And in a few years later, when a newpersistence engine pops up, we have to start over yet again.

If we hadn't created an interface first, we'd also likely need to change ourconsuming code!

On top of that, we may find that we want to use some sort of distributed cachinglayer forread operations (fetching items), whilewrite operations will bewritten to a relational database. Most likely, we don't want our controllers toneed to worry about those implementation details, but we will want to ensurethat we account for this in our architecture.

At the code level, the interface is our abstraction layer for dealing withdifferences in implementations. However, currently, we only deal with queries.Let's expand on that.

Adding command abstraction

Let's first think a bit about what possible database interactions we can thinkof. We need to be able to:

  • find a single blog post
  • find all blog posts
  • insert new blog post
  • update existing blog posts
  • delete existing blog posts

At this time, ourPostRepositoryInterface deals with the first two.Considering this is the layer that is most likely to use different backendimplementations, we probably want to keep it separate from the operations thatcause changes.

Let's create a new interface,Blog\Model\PostCommandInterface, inmodule/Blog/src/Model/PostCommandInterface.php, and have it read as follows:

namespace Blog\Model;interface PostCommandInterface{    /**     * Persist a new post in the system.     *     * @param Post $post The post to insert; may or may not have an identifier.     * @return Post The inserted post, with identifier.     */    public function insertPost(Post $post);    /**     * Update an existing post in the system.     *     * @param Post $post The post to update; must have an identifier.     * @return Post The updated post.     */    public function updatePost(Post $post);    /**     * Delete a post from the system.     *     * @param Post $post The post to delete.     * @return bool     */    public function deletePost(Post $post);}

This new interface defines methods for eachcommand within our model. Eachexpects aPost instance, and it is up to the implementation to determine howto use that instance to issue the command. In the case of an insert operation,ourPost does not require an identifier (which is why the value is nullable inthe constructor), but will return a new instance that is guaranteed to have one.Similarly, the update operation will return the updated post (which may be thesame instance!), and a delete operation will indicate if the operation wassuccessful.

Conclusion

We're not quite ready to use the new interface; we're using it to set the stagefor the next few chapters, where we look at using zend-db to implement ourpersistence, and later creating new controllers to handle blog postmanipulation.

Found a mistake or want to contribute to the documentation? Edit this page on GitHub!