PHP Service Layers: User Services

In this fifth installment of the series, I will finally implement a fully functional user service. This additional layer will use the functionality of a mapper to run CRUD functions on user entities, as well as retrieve these domain objects in XML format.

If you ever need to implement a pluggable structure that allow your PHP applications to interact easily with different client layers, such as several front-ends or multiple external APIs, then you might want to consider a service layer (http://martinfowler.com/eaaCatalog/serviceLayer.html). A service layer is an enterprise-level pattern that will let you achieve this kind of interaction by encapsulating application logic behind a single interface (AKA a boundary), which can be directly consumed by distinct clients.

Of course, highlighting the benefits that a service offers without adding some functional code is pretty pointless. In previous parts of this series I went through the development of a sample application, whose main responsibility is to perform CRUD operations on a few user entities that reside in a domain model completely decoupled from the storage mechanism (in this case, a MySQL database).

In its current state, the application is entirety functional. What’s more, with a little bit of effort from us, it’s perfectly feasible to fetch, save and remove user entities from the pertinent database simply by setting up a mapping layer. This layer would move data between the domain model and storage. However, making the mappers work directly with different clients is a rather inflexible approach, whose functionality is limited to that provided by the mappers themselves and nothing else.

The big question, then, is this: how can the application be turned into a more flexible and abstract structure? This is exactly where a service layer comes into play. Sitting a service on top of the mappers would make it really simple to execute the aforementioned operations, plus any others needed by a different client layer.

In this fifth installment I’ll be building such a service layer, so that you can see how easy it is to use it to manipulate user entities in all sorts of clever ways.

Now, jump in and read the next few lines!

Handling collections of entities: a quick look at the classes previously created

As always, before I show you how to implement a functional user layer, we’ll quickly review the topics that were covered in the last tutorial. Below I reintroduced the pair of classes developed in that tutorial, which are tasked with manipulating collections of entities.

The first of these classes is an abstract one; it’s nothing but a simple array collection. Take a look at it:

(MyApplication/Collection/AbstractCollection.php)

<?php

namespace MyApplicationCollection;

abstract class AbstractCollection implements Iterator, Countable, ArrayAccess
{
    protected $_entities = array();
    protected $_entityClass;

    /**
     * Constructor
     */
    public function  __construct(array $entities = array())
    {
        if (!empty($entities)) {
            $this->_entities = $entities;
        }
        $this->rewind();
    }

    /**
     * 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’re more entities in the collection (implementation required by Iterator Interface)
     */
    public function valid()
    {
        return ($this->current() !== false);
    }
   
    /**
     * 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 ($entity instanceof $this->_entityClass) {
            if (!isset($key)) {
                $this->_entities[] = $entity;
            }
            else {
                $this->_entities[$key] = $entity;
            }
            return true;
        }
        throw new CollectionException(‘The specified entity is not allowed for this collection.’);
    }
   
    /**
     * Remove an entity from the collection (implementation required by ArrayAccess interface)
     */
    public function offsetUnset($key)
    {
        if ($key instanceof $this->_entityClass) {
            $this->_entities = array_filter($this->_entities, function ($v) use ($key) {
                return $v !== $key;
            });
            return true;
        }
        if (isset($this->_entities[$key])) {
            unset($this->_entities[$key]);
            return true;
        }
        return false;
    }
   
    /**
     * Get the specified entity in the collection (implementation required by ArrayAccess interface)
     */
    public function offsetGet($key)
    {
        return isset($this->_entities[$key]) ?
               $this->_entities[$key] :
               null;
    } 
   
    /**
     * Check if the specified entity exists in the collection (implementation required by ArrayAccess interface)
     */    
    public function offsetExists($key)
    {
        return isset($this->_entities[$key]);
    }
}

 

(MyApplication/Collection/CollectionException.php)

<?php

namespace MyApplicationCollection;

class CollectionException extends Exception{}

As I just noted, the previous “AbstractCollection” class is a basic wrapper for a plain PHP array that permits you to iterate, access and count its elements in a simple way, thanks to the implementation of the Iterator, Countable and ArrayAccess SPL interfaces. Nothing really complex to grasp here, right?

Now, we’ll move on and look at the following subclass. It’s a refined implementation of the earlier parent, and is charged with specifically handling collections of user entities:

(MyApplication/Collection/UserCollection.php)

<?php

namespace MyApplicationCollection;

class UserCollection extends AbstractCollection
{
    protected $_entityClass = ‘MyApplicationEntityUser';

  
Done. At this point, it should be pretty clear how this sample application does its business in its current incarnation. Given that, the next step is… yes, to start building the user service mentioned at the beginning!

To learn the fine details of this process, click on the link below and keep reading. 

{mospagebreak title=Building an abstract service}

Despite this rather intimidating definition, building a service layer is a straightforward process. Don’t believe me? Focus your attention on the following class, which implements a service that works with generic entities: 

(MyApplication/Service/AbstractService.php)

<?php

namespace MyApplicationService;
use MyApplicationMapper,
    MyApplicationEntity;

abstract class AbstractService
{
    protected $_mapper;

    /**
     * Constructor
     */
    public function  __construct(MapperAbstractDataMapper $mapper)
    {
        $this->_mapper = $mapper;
    }

    /**
     * Find an entity by their ID
     */
    public function findById($id)
    {
        return $this->_mapper->findById($id);
    }

    /**
     * Find all the entities
     */
    public function findAll()
    {
        return $this->_mapper->findAll();
    }

    /**
     * Insert a new entity
     */
    public function insert(EntityEntityAbstract $entity)
    {
        return $this->_mapper->insert($entity);
    }

    /**
     * Update an entity
     */
    public function update(EntityEntityAbstract $entity)
    {
        return $this->_mapper->update($entity);
    }

    /**
     * Delete an entity
     */
    public function delete($id)
    {
        return $this->_mapper->delete($id);
    }
}

As seen above, thanks to the “clever” (if that’s really the appropriate term) use of dependency injection, constructing an abstract service that runs CRUD operations on a few domain objects is a breeze. At first glance, the implementation of this additional abstraction layer seems to be somewhat redundant, especially when compared with the functionality already offered by the mapping layer — but this is a misleading impression, trust me.

In this case, the application’s concerns are quite trivial, reduced to fetching, saving and deleting entities from the database. But what if it’s necessary to do many other things, like interfacing with a different front-end, sending a newsletter or retrieving aggregated roots, like blog posts and their associated comments? Are we going to pollute our action controllers with all that stuff and suffer the side effects of code duplication nearly everywhere? That sounds lame, indeed.    

Undeniably, the best way to encapsulate all of this logic is behind a service layer. In the following segment I’ll be creating a user service, which naturally will be a derivative of the previous abstract parent.

Now, move ahead and read the lines to come.

Refining the earlier abstract parent class: implementing a user service

Since the abstract service defined earlier already implements the logic required to run CRUD functions on generic entities, creating a subclass that works specifically with user objects is a straightforward process that can be accomplished in a few easy steps.

If you still wonder how this user service will look, pay attention to the following class: 

(MyApplication/Service/UserService.php)

<?php

namespace MyApplicationService;
use MyApplicationMapper,
    MyApplicationEntity;

class UserService extends AbstractService
{
    /**
     * Constructor
     */
    public function  __construct(MapperUserMapper $mapper)
    {
        parent::__construct($mapper);
    }

    /**
     * Save a user to persistence layer
     */
    public function save(EntityUser $user)
    {
        return $user->id === null ?
               $this->insert($user) :
               $this->update($user);
    }

    /**
     * Fetch all users in XML format
     */
    public function toXML()
    {
        $users = $this->_mapper->findAll();
        $xml = "<?xml version="1.0" encoding="UTF-8"?>n<users>n";
        foreach($users as $user) {
            $xml .= "<user>n<fname>$user->fname</fname>n"
                  . "<lname>$user->lname</lname>n"
                  . "<email>$user->fname</email>n</user>n";
        }
        $xml .= "</users>";
        return $xml;
    }
}

There you have it. As shown above, the implementation of a service that manipulates user entities was a simple, not to say simplistic process! To demonstrate how extendable such a layer can actually be, the service in question not only can fetch, save, and delete domain objects from the persistence layer, but it can also return user objects in XML format.

Of course, I coded the “toXML()” method only for demonstration purposes; however, it should give you a clearer idea of how easy it is to encapsulate application logic behind a service, which can be consumed by several different clients layers without suffering the curse of code duplication.

At this point, the good news is you’ve already learned what a service is and how it can be implemented using OOP. The bad news is that, to get it up and running, it’s necessary to create a slightly complex object graph.

Final Thoughts

In this fifth installment of the series, things took an exciting twist, as I finally managed to implement a fully functional user service. As you just saw, this additional layer uses the functionality of a mapper for running CRUD functions on user entities, as well as retrieving these domain objects in XML format. That’s not too bad, considering that this is only an example that can be expanded and customized at will.

Naturally, if you’re anything like me, at this moment you’ll want to see a script that shows how to put this service into action. Before we come to that point, though, we need to do a few more things. Construction of the entire service graph requires that we create a MySQL adapter, which is then injected into the mapper’s constructor. This collaborator is finally dumped into the service itself. So we need to build a basic dependency injection container that sets this object graph and returns it to client code in a valid state.

That’s exactly what I’ll be doing in the upcoming tutorial, so don’t miss it! 

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan