Home arrow PHP arrow Page 2 - Developing a Simple Blog Application in PHP

Defining generic entities for the domain layer - PHP

In this third part of the series, I develop the domain layer of a sample blog application. In this case, for the sake of simplicity, the domain layer will be able to fetch, save, and delete blog post entries from the underlying storage layer.

TABLE OF CONTENTS:
  1. Developing a Simple Blog Application in PHP
  2. Defining generic entities for the domain layer
By: Alejandro Gervasio
Rating: starstarstarstarstar / 5
February 16, 2011

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

In the real world, a blog application usually manages several entities and value objects. To keep things clear and understandable, however, in this case my blog application's domain layer will be composed only of plain blog entries. Even though this schema is really primitive, it's necessary first to create a class that models generic entities, and then one that specifically defines the data structure and constraints of the pertinent entries.

With that said, here's the source code of the former. Check it out:  

(Blog/Entity/EntityAbstract.php)

<?php

namespace BlogEntity;

abstract class EntityAbstract
{
    protected $_values = array();
    protected $_allowedFields = array();
   
    /**
     * Class constructor
     */
    public function __construct(array $data)
    {
        foreach ($data as $name => $value) {
            $this->$name = $value;
        }
    }
   
    /**
     * Assign a value to the specified field via the corresponding mutator (if it exists);
     * otherwise, assign the value directly to the '$_values' protected array
     */
    public function __set($name, $value)
    {  
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException('The field ' . $name . ' is not allowed for this entity.'); 
        }
        $mutator = 'set' . ucfirst($name);
        if (method_exists($this, $mutator) && is_callable(array($this, $mutator))) {
            $this->$mutator($value);          
        }
        else {
            $this->_values[$name] = $value;
        }   
    }
   
    /**
     * Get the value assigned to the specified field via the corresponding getter (if it exists);
    otherwise, get the value directly from the '$_values' protected array
     */
    public function __get($name)
    {
        if (!in_array($name, $this->_allowedFields)) {
            throw new EntityException('The field ' . $name . ' is not allowed for this entity.');   
        }
        $accessor = 'get' . ucfirst($name);
        if (method_exists($this, $accessor) && is_callable(array($this, $accessor))) {
            return $this->$accessor;   
        }
        if (array_key_exists($name, $this->_values)) {
            return $this->_values[$name];
        }
        throw new EntityException('The field ' . $name . ' has not been set for this entity yet.');
    }
   
    /**
     * Check if the specified field exists in the entity
     */  
    public function __isset($name)
    {
        return isset($this->_values[$name]);
    }
         
    /**
     * Unset the specified field from the entity
     */  
    public function __unset($name)
    {
        if (isset($name, $this->_values)) {
            unset($this->_values[$name]);
        }
    }
      
    /**
     * Get the values assigned to the fields of the entity
     */
    public function toArray()
    {
        return $this->_values;
    }             
}

 

(Blog/Entity/EntityException.php)

<?php

namespace BlogEntity;

class EntityException extends Exception {}

If you found it easy to grasp the underlying logic of the autoloader, you should feel the same way about the "EntityAbstract" class above, since I've used it previously in other Dev Shed tutorials as well. Nevertheless, if you're not familiar with the operations that this class performs, don't worry; they're very simple to follow. This class uses the "__set()" and "__get()" PHP magic methods to assign and retrieve, via the corresponding mutators/getters (when possible), the values of the fields of an entity. The $_allowedFields property controls which fields are allowable for a particular entity, which makes it easy to define specific constraints for certain types of domain objects.

With this abstract parent laid down comfortably on top of the hierarchy, it's really simple to create a refined implementation of it to model blog post entries. Given that, in the next section I'm going to spawn a subclass from the parent, which will define the metadata and constraints that will be applied to the  blog entries.

To see how this subclass will look, keep reading.

Going one step further: modeling blog post entities

As I just explained, creating a class that specifically models blog post entries is a straightforward process. In fact, it's reduced to subclassing the previous abstract parent and implementing (optionally) the corresponding mutators/getters for the entries. This is all that it takes. 

To understand this modeling process more clearly, pay attention to the definition of the following child class, which not surprisingly has been named "BlogPost." Here it is:

(Blog/Entity/BlogPost.php)

<?php

namespace BlogEntity;

class BlogPost extends EntityAbstract
{
    protected $_allowedFields = array('id', 'title', 'content', 'author');
   
    /**
     * Set the blog post ID
     */
    public function setId($id)
    {
        if(!filter_var($id, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 99999)))) {
            throw new EntityException('The blog ID ' . $id . ' is invalid.');
        }
        $this->_values['id'] = $id;
    }
   
    /**
     * Set the blog post title
     */ 
    public function setTitle($title)
    {
        if (!is_string($title) || strlen($title) < 2 || strlen($title) > 64) {
            throw new EntityException('The blog post title is invalid.');
        }
        $this->_values['title'] = $title;
    }
   
    /**
     * Set the blog post content
     */ 
    public function setContent($content)
    {
        if (!is_string($content)) {
            throw new EntityException('The blog post content is invalid.');
        }
        $this->_values['content'] = $content;
    }
   
    /**
     * Set the blog post author (usually this should be another entity. Now it will be a string for brevity)
     */
    public function setAuthor($author)
    {
        if (!is_string($author) || strlen($author) < 2 || strlen($author) > 32) {
            throw new EntityException('The blog post author is invalid.');
        }
        $this->_values['author'] = $author;
    }       
}

Apart from seeing that PHP needs to support type hinting for primitive types like integers and strings, understanding the underlying logic of the "BlogPost" class is a breeze. Simply put, the class acts like a container that permits you to assign, via the corresponding mutators, the "id," "title," "content" and "author" to a given blog entry. Effectively, the methods impose some generic restrictions to these properties, which can be modified to suit more specific needs. So, feel free to play around with them.

In addition, you should notice that the setter for the "author" treats this property as a plain string. I've done this deliberately to keep the code sample short and uncluttered. However, in a real situation, the author should be a separate entity with its own attributes and constraints, which should be modeled by a separate subclass and handled by a specific data mapper. Make sure to stick to this approach, especially if you plan to use a domain layer in the development of your next killer PHP application.

So far, so good. At this point, the domain layer of this sample blog program is finally complete. But hold on a second! Wasn't this supposed to be a series that explores the use of service locators? Well, it is. It's just that the classes developed so far need no dependencies, as they only model entities.

Don't feel concerned, though; in the next tutorial I'll be building the data access and mapping layers of the blog, which will need to set up some object graphs to work correctly. Remember that in this first approach, those object graphs will use only injected collaborators. But, in upcoming parts I'll be demonstrating how to get similar results using static and dynamic service locators.   

Final thoughts

In this third installment of the series, I went through the development of the domain layer of a sample blog application, which in this particular case (for the sake of simplicity) will be able to fetch, save, and delete blog post entries from the underlying storage layer.

In its current state, the blog program looks pretty skeletal. It's only able to model the aforementioned blog entries. But this is about to change. As I said before, in the next tutorial I'll be defining its data access and mapping layers. The latter will make use of dependency injection to construct its object graphs. But, keep in mind that I plan to deploy the application using static and dynamic service locators as well -- so be ready to be faced with a huge amount of code samples.

Don't miss the upcoming part!



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: