Handling Blog Entries with PHP Locators EDITED

If you’re a PHP developer looking for a guide to implementing a service locator in a few concrete use cases, that also explains the differences between using this approach and dependency injection, then you’re reading the right article series. Made up of seven tutorials, the series teaches you in a step-by-step fashion how to build your object graphs through static and dynamic locators — and if you’re hungry for more, you’ll also learn how to combine locators with inversion of control.

And if you’ve been a patient reader and gone through all of the installments that precede this one, you probably have a solid background in using a service locator in the development of a “real-world” application. In earlier chapters I provided you with two different versions of a basic blog program, which was tasked with performing ACID operations on a few blog entries. The first one constructed its data mappers by means of dependency injection, while the second one used a static service locator to provide the mappers with the corresponding collaborators.

The second approach brought with it plenty of flaws. These were mostly due to the static nature of the locators, which made it harder to test the mappers in isolation. But, it’s possible to improve that version of our application a bit more and fix at least some of its issues. You’re wondering how, right? Well, it’s feasible to implement dynamic service locators (instead of static ones), which can be injected into the internals of a class. This method is particularly useful when the lifecycle of a collaborator is shorter than that of the object that needs its functionality.

The best way to see the benefits of using this approach is by example. Therefore, in this final part of the series I’m going to introduce some changes to the blog application previously developed. This time, it will “hydrate” its data mappers with an injected service locator.

Want to learn the full details of this refactoring process? Then start reading!  

Tweaking the blog’s data mappers: working with an injected locator

Since my goal is to demonstrate how to construct the mappers of the sample blog via an injected service locator, the first step is to modify the implementation of the mappers. So here is the improved version of the parent abstract mapper. It now acquires an instance of its database adapter through a dynamic resource locator:   

(Blog/Mapper/DataMapperAbstract.php)

<?php

namespace BlogMapper;
use BlogResources;

abstract class DataMapperAbstract implements DataMapperInterface
{
    protected $_adapter;
    protected $_collection;
    protected $_resourceLocator;
    protected $_entityClass;
    protected $_entityTable; 
        
    /**
     * Constructor
     */
    public function __construct(ResourcesResourceLocator $resourceLocator, $entityOptions = array())
    {
        $this->_resourceLocator = $resourceLocator;
        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()
    {
        if ($this->_adapter === null) {
            $this->_adapter = $this->_resourceLocator->getAdapter();
        }
        return $this->_adapter;
    }
   
    /**
     * Get the collection the mapper uses (implementation delegated to concrete mappers)
     */
    abstract public function getCollection();
   
    /**
     * 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)
    {
        $adapter = $this->getAdapter();
        $id = (int) $id;
        $adapter->select($this->_entityTable, "id = $id");
        if ($data = $adapter->fetch()) {
            return new $this->_entityClass($data);
        }
        return null;
    }
   
    /**
     * Find all the entities
     */
    public function findAll()
    {
        $adapter = $this->getAdapter();
        $collection = $this->getCollection();
        $collection->clear();
        $adapter->select($this->_entityTable);
        while ($data = $adapter->fetch()) {
            $collection->offsetSet($data[‘id’], new $this->_entityClass($data));   
        }
        return $collection->count() !== 0 ?
               $collection :
               null;
    }
   
    /**
     * Find all the entities that match the specified criteria
     */
    public function search($criteria)
    {
        $adapter = $this->getAdapter();
        $collection = $this->getCollection();
        $collection->clear();
        $adapter->select($this->_entityTable, $criteria);
        while ($data = $adapter->fetch()) {
            $collection->offsetSet($data[‘id’], new $this->_entityClass($data));   
        }
        return $collection->count() !== 0 ?
               $collection :
               null;
    }
     
    /**
     * Insert a new row in the table corresponding to the specified entity
     */
    public function insert($entity)
    {
        if (!$entity instanceof $this->_entityClass) {
            throw new DataMapperException(‘The specified entity is not allowed for this mapper.’);
        }
        $data = $entity->toArray();
        return $this->getAdapter()->insert($this->_entityTable, $data);
    }
   
    /**
     * Update the row in the table corresponding to the specified entity
     */
    public function update($entity)
    {
        if (!$entity instanceof $this->_entityClass) {
            throw new DataMapperException(‘The specified entity is not allowed for this mapper.’);
        }
        $id = (int) $entity->id;
        $data = $entity->toArray();
        unset($data[‘id’]);
        return $this->getAdapter()->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 $this->_entityClass) {
            $id = $id->id;
        }
        $id = (int) $id;
        return $this->getAdapter()->delete($this->_entityTable, "id = $id");
    }               
}

Since the logic driving the above abstract mapper should be quite familiar to you, just focus your attention on its constructor. Effectively, instead of using a static resource locator, it now asks for a dynamic one, which is stored in a protected property. This subtle change permits it to lazy-load the database adapter (notice the definition of the “getAdapter()” method), and in turn, the appropriate collection handler. 

Of course, every concrete mapper derived from the abstract one must be provided with its own collection object. So, here’s the mapper that exclusively handles blog entries. Check it out:

(Blog/Mapper/BlogPostMapper.php)

<?php

namespace BlogMapper;
use BlogResources;

class BlogPostMapper extends DataMapperAbstract
{
    protected $_entityClass = ‘BlogEntityBlogPost’;
    protected $_entityTable = ‘blogs’; 
   
    /**
     * Get the blog post collection
     */
    public function getCollection()
    {
        if ($this->_collection === null) {
            $this->_collection = $this->_resourceLocator->getBlogPostCollection();
        }
        return $this->_collection;
    } 
}

That was easy to code and grasp, wasn’t it? As you can see, the only additional thing that the above blog post mapper does is implement a method called “getCollection().” This method, logically, is responsible for acquiring the appropriate collection handler on demand via the injected resource locator.

At this stage, it’s clear to see that the mappers do their work a bit more efficiently, as they’re easier to test, even when there’s still a strong coupling between them and the resource locator. However, things look somewhat disarticulated, since I haven’t shown you the implementation of the latter.

The definition of this class will be discussed in the next section segment. So click on the link that appears below and keep reading.     

{mospagebreak title=Obtaining resources dynamically with a concrete resource locator}

If you’re anything like me, you’re eager to see the source code of the dynamic locator discussed in the preceding segment. Take a look at the definition of the following class, which not surprisingly is named “ResourceLocactor”:

(Blog/Resources/ResourceLocator.php)

<?php

namespace BlogResources;
use BlogDataBase,
    BlogCollection;

class ResourceLocator
{  
    protected $_adapter;
    
    /**
     * Get the database adapter
     */
    public function getAdapter()
    {
        if ($this->_adapter === null) {
            $this->_adapter = new DatabaseMySQLAdapter(array(‘host’, ‘user’, ‘password’, ‘blog_database’));
        }
        return $this->_adapter;
    }
   
    /**
     * Get the blog post collection
     */
    public function getBlogPostCollection()
    {
        return new CollectionBlogPostCollection;
    }   
}

There’s no need to be a rocket scientist to see that the above class is a concrete factory. Its methods are responsible for returning to client code an instance of the pertinent MySQL adapter, as well as one of the blog post collection. It’s the only thing this dynamic locator does.

Now that you’ve grasped the underlying logic of the previous class, it’s time to set up an example that shows how to use it to perform CRUD operations on some blog post entries.

This concluding example will be created in the following segment. So just keep reading.

Putting the resource locator to work

As I said in the earlier section, below I coded a small script which demonstrates how to fetch, save and remove some fictional blog posts from the same sample “blogs” MySQL table using the previous resource locator class. Here’s the script: 

<?php

// example injecting the service locator

use BlogMapperBlogPostMapper as BlogPostMapper,
    BlogResourcesResourceLocator as ResourceLocator,
    BlogEntityBlogPost as BlogPost;

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

// create an instance of the blog post mapper
$blogMapper = new BlogPostMapper(new ResourceLocator);

// find the first blog post
$blog = $blogMapper->findById(1);
echo $blog->title . $blog->content . $blog->author . ‘<br />’;


// find all the blog posts
$blogs = $blogMapper->findAll();
foreach ($blogs as $blog) {
    echo $blog->title . $blog->content . $blog->author . ‘<br />’;
}

// insert a new blog post
$blog = new BlogPost(array(‘title’ => ‘This is the title for a new blog post’,
                           ‘content’ => ‘This the content of the new blog post’,
                           ‘author’ => ‘Alex Gervasio’
                     ));
$blogMapper->insert($blog);

// delete an existing blog post
$blogMapper->delete(4);

Since the above script is pretty self-explanatory, I’m not going to waste your time explaining how it works. The only detail worth stressing here is the instantiation of the blog post mapper, as this process shows clearly how the resource locator is injected into the mapper’s constructor.

And now that you’ve learned how to build different kinds of service locators in PHP, it’s up to you to decide what approach you are going to use to construct your object graphs. As I said before, static locators have some serious pitfalls, especially when performing unit tests. On the other hand, dynamic locators are slightly more efficient, but there’s always a strong coupling with the client classes that consume them. If you can live happily with this issue, this last approach can be viable, even though it should be used with caution.      

Closing thoughts

In the flicker of an eye, we’ve come to the end of this series. Hopefully, the journey has been educational. I attempted to use a practical approach to show you the pros and cons of using service locators to build object graphs in PHP. In general, a service locator is considered a degradation of dependency injection, and overall, I must agree with this opinion.

There exists, however, a certain number of use cases where a locator can be pretty useful, especially when it’s injected instead of  consumed statically. So, if you feel that, for whatever reasons, pure dependency injection doesn’t fit your needs, an injected locator might be worth a look.

See you in the next PHP development tutorial!  

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

chat sex hikayeleri Ensest hikaye