PHP Proxy Patterns: Completing a Blog

In this third part of a series on proxy objects, we’ll create a simple service layer to add to our blog program. With this finished application, you’ll see exactly how proxy objects can be powerful allies when you need to persist the entities of a domain model in a database.

While proxy objects can be utilized in a variety of situations and environments, they yield excellent results when lazy-loading data from an application’s persistence layer (usually this layer is seated upon a RDBMS, but it could be a web service or anything else). With the progressive adoption in the PHP field of Domain-Driven Design methodologies, the use of proxies for loading and reconstituting collections of domain objects on request from the underlying storage can help in the implementation of more efficient persistence strategies. This is true despite the need in some cases to create a few additional abstraction layers, like data mappers, repositories and even services.

It’s relatively easy to use the capabilities of proxies and lazy-load sets of entities from a database, even in the most simple use cases. Indeed, in the two previous parts of this tutorial, I started building an extendable blog program. It used a couple of proxy classes to pull, from a MySQL table, the comments related to a particular blog entry.

Unfortunately, the blog in its current state isn’t entirely functional. It’s necessary to add to it a simple service layer. With this layer, all of the logic required by the interaction between the mappers and the database can be neatly encapsulated behind a cleaner and uncluttered API. In this final installment, I’ll set up this additional layer, thus finishing the sample blog program.

Now, it’s time to continue learning how to make proxy objects work for us in PHP. Let’s jump in!

Adding a service layer to the blog

In reality, adding a service layer to the blog’s existing structure is optional, as it’s possible to demonstrate the functionality of proxies without implementing this extra abstraction tier. However, since I want to keep all the logic required to perform CRUD operations on blog entries hidden behind a single entry point, accessible to different front-ends, I’ll be bold and bring this layer to life anyway.

Having said that, this is the class that will make the service work. Take a close look at it:   

(MyApp/Service/EntryService.php)

<?php

namespace MyAppService;
use MyAppModelMapper,
    MyAppModel;

class EntryService
{
    protected $_entryMapper;
   
    /**
     * Constructor
     */
    public function __construct(MapperEntryMapper $entryMapper)
    {
        $this->_entryMapper = $entryMapper;
    }
   
    /**
     * Get the entry mapper
     */
    public function getEntryMapper()
    {
        return $this->_entryMapper;
    }
   
    /**
     * Find an entry by its ID
     */ 
    public function findById($id)
    {
        return $this->_entryMapper->findById($id);  
    }
   
    /**
     * Find entries according to the given criteria
     */
    public function find($criteria = ”)
    {
        return $this->_entryMapper->find($criteria);
    }
   
    /**
     * Insert a new entry
     */
    public function insert(ModelEntry $entry)
    {
        return $this->_entryMapper->insert($entry);
    }
   
    /**
     * Delete an existing entry
     */
    public function delete($id)
    {
        return $this->_entryMapper->delete($id);
    }                 
}

As you can see above, this new class is a basic wrapper for the previous entry mapper, which can fetch, save, and remove entries from the database. At first glance, it looks like a repository; it can deal with aggregate roots (the blog entries). Keep in mind, however, that it can be easily extended to perform many other tasks with the involved domain objects, such as representing them in XML, emailing them, or whatever comes to mind.

If you understand how the service works, the next step is to create a simple dependency injection container (DIC). This container will take care of spawning (in this case manually) the service’s object graph in a snap. To do this, I’ll make the container an implementer of the following interface:
 
(MyApp/Service/InjectorInterface.php)

<?php

namespace MyAppService;

interface InjectorInterface
{
    public function create(); 
}

That was simple to code and read, right? So, move on and take a look at the originating class of the aforementioned DIC:

(MyApp/Service/EntryServiceInjector.php)

<?php

namespace MyAppService;

use MyAppModelMapper,
    MyAppLibraryDatabase;

class EntryServiceInjector implements InjectorInterface
{
    protected $_entryService;
   
     /**
     * Create the entry service
     */
    public function create()
    {
        if ($this->_entryService === null) {
            $mysqlAdapter = new DatabaseMysqlAdapter(array(
                ‘host’,
                ‘user’,
                ‘password’,
                ‘database’
            ));
            $this->_entryService = new EntryService(
                new MapperEntryMapper($mysqlAdapter, new MapperCommentMapper($mysqlAdapter))
            );
        }
        return $this->_entryService;
    }  
}

As the above code fragment shows, the “EntryServiceInjector” class’s responsibility is to build the graph corresponding to the previous entry service. As I said before, in this case all the dependencies are injected manually; even so, implementing this DIC helps to separate object instantiation from application logic, which is a good programming practice.

So far, so good. At this point, the blog’s service layer is finally ready for some real action. However, there’s one more thing that I’d like to do. Since the blog might be coupled with an existing MVC stack (for instance, the infrastructure provided by a framework from the many available out there), it’d be helpful to create a basic helper. This helper would allow you to create the service on request, and can be consumed easily within an action controller.

The helper’s definition will be shown in the next section.

{mospagebreak title=Building a basic service locator}

If you want to see how the helper mentioned in the previous section looks, check the following code fragment:

(MyApp/Service/EntryServiceLocator.php)

<?php

namespace MyAppService;

class EntryServiceLocator
{
    protected $_entryServiceInjector;
    protected $_entryService;
   
    /**
     * Constructor
     */
    public function __construct(EntryServiceInjector $entryServiceInjector)
    {
        $this->_entryServiceInjector = $entryServiceInjector;
    }
   
    /**
     * Get the entry service injector
     */
    public function getEntryServiceInjector()
    {
        return $this->_entryServiceInjector;
    }
   
    /**
     * Set the entry service
     */
    public function setEntryService(EntryService $entryService)
    {
        $this->_entryService = $entryService;
    }
   
    /**
     * Get the entry service (if not set already, the service is created via the related injector)
     */
    public function getEntryService()
    {
        if ($this->_entryService === null) {
            $this->_entryService = $this->_entryServiceInjector->create();
            if (!$this->_entryService instanceof EntryService) {
                throw new RunTimeException(‘Unable to create the entry service.’);
            }
        }
        return $this->_entryService;
    }      
}

As shown above, this helper class is a very basic service locator. It allows you to inject and create, via the associated DIC, an instance of the blog entry service that you saw before. In a typical situation, this locator should be initialized in a bootstrap class or something like that, so that it could be used by any action controller, based on a specific request. For the sake of brevity, though, the details of how to accomplish this will be left as homework for you. If you feel adventurous, you can integrate this helper (or a similar one) into your existing framework.

So far, so good. With this service locator ready to go, it’s finally time to give the blog a shot and see if its proxy classes are as functional as they look. However, this testing process will be conducted in the following section. 

Taking the final step: seeing the blog in action

To test the blog, I’m going to create a hypothetical scenario where there are a few blog entries posted by myself, which have been commented upon by some insightful users. To do this, I’ll be using the following “entries” and “comments” MySQL tables, which have been populated with the following data:

As the tables show, I’m rather lazy and not very chatty when posting; even so, some people did take some time to comment on my short posts, which is quite remarkable.

However, the important part of this fictional example is that it demonstrates how to pull my posts from the corresponding table and load the related comments on request. The following script does that (as usual, the implementation of the autoloader has been omitted to keep the code shorter and easier to read):     

(index.php)

<?php

use MyAppLibraryLoaderAutoloader as Autoloader,
    MyAppServiceEntryServiceLocator as EntryServiceLocator,
    MyAppServiceEntryServiceInjector as EntryServiceInjector,
    MyAppViewsView as View,
    MyAppModelEntry as Entry;

require_once __DIR__ . ‘/Library/Loader/Autoloader.php';

// create an instance of the autoloader
$autoloader = new Autoloader;

// create an instance of the entry service locator
$entryServiceLocator = new EntryServiceLocator(new EntryServiceInjector);

// get the entry service (created by the related injector)
$entryService = $entryServiceLocator->getEntryService();

// fetch all the entries from the storage
$entries = $entryService->find();

// create a view and assign to it the entries
$view = new View(array(‘templateFile’ => ‘entries.php’));
$view->header = ‘Using the proxy pattern in PHP';
$view->entries = $entries;

// display the entries (the related comments are lazy-loaded)
echo $view->render();

As the above script shows, once an instance of the entry service is created via the associated injector, the object is used for fetching blog entries from the database. Then, they’re assigned to a view, which is finally rendered on screen by means of the corresponding template file.

As you’ll possibly recall, this file first echoes the blog entries, which is the expected behavior; however, the comments are initially filled in with a proxy object, and are only retrieved when the proxy is used in a “foreach” loop. The result of this process can be seen more clearly in the following image:    

Not bad at all, right? This shows in a nutshell the functionality of proxies for lazy-loading data — in this case, a bunch of domain objects. If you’re wondering how to add new blog entries or delete an existing one using the previous script, take a look: 

// add a new entry to the storage
$entry = new Entry(array(
    ‘title’   => ‘This is a sample entry’,
    ‘author’  => ‘Lisa Williams’,
    ‘content’ => ‘This is the content of the sample entry.’
));

$entryService->insert($entry);

// delete an existing entry from the storage
$entryService->delete(1);

Mission accomplished. At this stage, you should have a spot-on idea of how to use proxy objects for loading entities from a database on request. Of course, the implementation of my own lazy-loading proxies may be more complex than usual, as they have to interact with a mapping layer. Nevertheless, if you don’t use data mappers to fetch domain objects from your persistence layers, the construction of such proxies might be much simpler.

Final Thoughts

We’ve come to the end of this tutorial. Hopefully, you found it an educational journey, as you learned  how proxy objects can be powerful allies when you need to persist in a database the entities of a domain model. While in this case, the model was inherently anemic (after all, blog entries and comment objects are usually simple data holders that don’t carry much behavior per se), the example demonstrated how to exploit the functionality of proxies to implement more efficient data persistence strategies.  

As I said at the beginning, proxies can be used in several situations, and for many purposes. So, make sure that you grasp their driving logic, as this will help you get the most out of them when developing your own object-oriented applications.

See you in the next PHP tutorial!

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

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort