Home arrow PHP arrow Page 2 - Roll Your Own Repository in PHP: Building the Domain Layer

Building the domain layer: modeling generic entities - PHP

In this introductory part of a series, I give you an overview of what a repository is and how it can be used for handling collections of domain objects in PHP. Since my goal here is to address the subject from a practical point of view, after I explain some theoretical concepts, I will develop a simple domain layer comprised of two classes. The first one will be an abstract parent that defines the structure and behavior of generic entities, while the second class will be responsible for modeling simple user objects, according to a number of predefined constraints.

TABLE OF CONTENTS:
  1. Roll Your Own Repository in PHP: Building the Domain Layer
  2. Building the domain layer: modeling generic entities
By: Alejandro Gervasio
Rating: starstarstarstarstar / 2
November 17, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

While I have to admit that I’m not a big fan of creating classes that rely too much on the magic methods bundled with PHP, in this case I’m going to implement an approach similar to the one proposed by Ben Scholzen and use the complementary “__set()”/“__get()” pair to construct the abstract parent mentioned in the previous segment. This class will be responsible for defining the structure and behavior of generic entities (like users, blog posts and so forth), and its source code is shown below: 

(EntityAbstract.php)

<?php

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.');
    }
       
    /**
     * Unset the specified property from the entity
     */  
    public function __unset($name)
    {
        if (array_key_exists($name, $this->_values)) {
            unset($this->_values[$name]);
        }
    }
      
    /**
     * Get the values assigned to the fields of the entity
     */
    public function toArray()
    {
        return $this->_values;
    }             
}

 

(EntityException.php)

<?php

class EntityException extends Exception {}

As you can see above, the “EntityAbstract” class accepts, through its constructor, an associative array of data. This data is used to assign, all at once, a set of values to its undeclared fields. This process is carried out via the “__set()” method, which first attempts to perform the assignment process via the corresponding mutator; if this method doesn’t exist, the value is assigned directly through the protected "$_values” array.

A similar logic is also implemented within the counterpart “__get()”, even though in this case the method checks to see if the value assigned to the specified field can be retrieved by means of a getter. In addition, the class declares a property called $_allowedFields, which is used to restrict the fields that can be assigned to the entity. Now, do you understand the logic of this generic parent? Good.

Having already defined an abstract class that encapsulates the logic shared by generic entities, the next step in the development of the sample user repository is to derive a subclass capable of modeling user objects, so that they can be properly handled afterward by their associated mapper and, at an upper level, by the repository.

This new child class will be built in the next section below, so keep reading. 
 
Creating a refined implementation of the previous abstract class: modeling user objects

To be frank, building a class that models user objects is a straightforward process, since most of its logic has already been implemented by its abstract parent. In this specific case, and for the sake of brevity, any user object spawned from this class will be allowed to have only four fields: an ID, then a first and a last name, and finally an email address.

With those constraints properly outlined, here’s the definition of this concrete class, not surprisingly called “User”:

(User.php)

<?php

class User extends EntityAbstract
{  
    protected $_allowedFields = array('id', 'fname', 'lname', 'email');
   
    /**
     * Set the user's 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 specified ID ' . $id . ' is invalid.');
        }
        $this->_values['id'] = $id;
    }
   
    /**
     * Set the user's first name
     */ 
    public function setFname($fname)
    {
        if (strlen($fname) < 2 || strlen($fname) > 32) {
            throw new EntityException('The specified first name ' . $fname . ' is invalid.');
        }
        $this->_values['fname'] = $fname;
    }
       
    /**
     * Set the user's last name
     */
    public function setLname($lname)
    {
        if (strlen($lname) < 2 || strlen($lname) > 32) {
            throw new EntityException('The specified last name ' . $lname . ' is invalid.');
        }
        $this->_values['lname'] = $lname;
    }
   
    /**
     * Set the user's email address
     */
    public function setEmail($email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new EntityException('The specified email address ' . $email . ' is invalid.');
        }
        $this->_values['email'] = $email;
    }                   
}

I'm sure you’ll agree that the “User” class shown above is a breeze to follow. All it does is implement a bunch of mutators corresponding to each of its allowed fields. These methods impose some basic restrictions to the values assigned to the pertinent properties; it’s possible, however, to perform a more strict validation, either by using procedural code or through a set of injected validation objects.

Finally, to keep things short and simple I decided not to define any getters. This means that the values assigned to the fields of the class will be directly retrieved from the internal $_values array. Nevertheless, if you want to tackle this extra work on your own and implement these complementary methods, feel free to do so.

And with the inclusion of this concrete user-modeling class, the first step toward the construction of the user repository is now complete. Bear in mind that this is only the beginning of a long and hopefully instructive journey, so for the moment be patient.   

Final thoughts

In this introductory part of the series, I provided you with an overview of what a repository is and how it can be used for handling collections of domain objects in PHP. Since my goal here is to address the subject from a practical point of view, after explaining some theoretical concepts, I went through the development of a simple domain layer comprised of two classes. The first one was an abstract parent that defined the structure and behavior of generic entities, while the second class was responsible for modeling simple user objects, according to a number of predefined constraints.

Considering that a repository usually resides between the domain and mapping layers of an application, and the latter also accesses in turn the data access layer, in the next tutorial I’m going to create this last layer, which in this case will be tasked with talking to MySQL.

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: