Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / PHP

MercuryORM - Simple Entity Framework

3.77/5 (6 votes)
29 Jun 2014CPOL4 min read 16.2K   95  
A lightweight Entity based Framework
This article presents a lightweight ORM (MercuryORM), which is based on the Entity Framework. MercuryORM only provides basic CRUD operations and simple associations.

Using the Code

We begin with an introduction to the Mercury\DbContext class. The DbContext class is an abstract class and cannot be instantiated directly, it must be inherited from. A concrete class must be created by inheriting from DbContext. By inheriting from DbContext, you are essentially creating a wrapper for database related tasks. The DbContext class exposes a dbSet() method, which is used to register an entity. Listing 1.1 below shows a sample BlogContext class with two entities registered.

Listing 1.1

PHP
class BlogContext extends \Mercury\DbContext{
	public function getUsers(){
		return $this->dbSet('Models\User');
	}
	
	public function getPosts(){
		return $this->dbSet('Models\Post');
	}
}

The above context (BlogContext) has two methods defined (getUsers() and getPosts()), both of which, will return an instance of Mercury\DbSet, however each DbSet will maintain data for its entity. In the example above, the getUsers() method registers a DbSet that will be responsible for managing Models\User entities. DbSet instances are managed by the DbContext internally in an array. If subsequent calls to the getUsers() method is made, then a previously instantiated DbSet is returned. Before we go further, let's take a look at how to connect to a database with the newly created BlogContext. Listing 1.2 below shows how to connect to a MySQL database using default settings and using a connection string.

Listing 1.2

PHP
// Default settings
$blogContext = new BlogContext();

// Using a connection string
$blogContext = new BlogContext('driver=mysql;host=127.0.0.1;
               dbname=blogcontext;uid=root;pwd=pass;charset=utf8');

When instantiating a context using default settings, the name of the context is used as the database name and a connection is made on localhost with username "root" and empty password.

The DbSet

As mentioned, each DbSet instance manages a collection of entities for the type it has been registered for. Apart from managing an entity collection, the DbSet also maintains entity metadata. Entity metadata is data that describes the entity and is placed in the entity class using comments. Listing 1.3 below shows a Model\User entity with metadata.

Listing 1.3

PHP
namespace Models;

//[TableName("users")]
//[UniqueKey("user_id")]
class User {
    
    protected $user_id;
    
    //[Required]
    //[StringLength(30)]
    protected $first_name;
    
    //[Required]
    //[StringLength(30)]
    protected $last_name;

    //[Required]
    //[StringLength(50)]
	//[DefaultValue("user@mail.com")]
    protected $email;
    
    public function setFirstName($name){
        $this->first_name = $name;
    }
    
    public function setLastName($name){
        $this->last_name= $name;
    }
    
    public function setEmail($email){
        $this->email = $email;
    }
}

The code sample above uses comments to assign attributes to the entity and the entity properties. Each attribute is of a particular type. For example, the Required attribute is of type Mercury\Attributes\Required while the attribute StringLength is of type Mercury\Attributes\StringLength. Attributes help to enforce validation and provide metadata about the entity. Entity properties can also be given default data using the DefaultValue attribute.

An instance of an entity must be added to its related DbSet using the DbSets add() method. The add method does not persist the entity into the database, but rather changes the entities state to PERSIST, which means when the DbContext's saveChanges() method is called, the entity will be persisted to the underlying database. Once persisted, the DbContext will maintain the entity in an internal collection and change its state to PERSISTED. Adding an entity to an unrelated DbSet will throw an exception of type EntityException. Listing 1.4 below shows how to create a new user and persist the user into the database.

Listing 1.4

PHP
$user = new Models\User();
$user->setFirstName('John');
$user->setLastName('Smith');
$user->setEmail('j.smith@mail.com');

$blogContext = new BlogContext();
$blogContext->getUsers()->add($user);
$blogContext->saveChanges();

Multiple users can be added to the DbSet using the add method and then persisted using the saveChanges() method. The saveChanges() method is also used to update records. In the next example, the find() method of the DbSet class is used to find a user with a given user_id, the user details are then updated and the saveChanges() method is used to update the user details.

Listing 1.5

PHP
$blogContext = new BlogContext();

// Find a user with the user_id=3
$user = $blogContext->getUsers()->find(3);

// Change the email address
$user->setEmail('new@mail.com');

// Save changes
$blogContext->saveChanges();

The above code sample introduces a find() method. The find method is used to search the underlying database for a record and map the record to the DbSet entity type. Since the find method belongs to the DbSet class, it is aware of the table name and the primary key because the DbSet maintains entity metadata. Once a record is found, it is passed to the DbContext, which stores it in an internal collection and changes the entities state to PERSISTED before returning it. A findAll() method also exists, which returns an array of a given entity type. The findAll() method accepts a key/value array to be used as the search condition.

Simple associations can also be achieved. In some situations, you might have an insert that is dependent on another insert. Consider the following example, you want to create a user who has also posted an article. Both the user and the article are to be persisted. Normally, you would persist the user and return the last insert id of the user to be used as the foreign key value for the article's author_id. Listing 1.6 below shows how association can help to insert both a user and an article without the need to return the last insert id yourself.

Listing 1.6

PHP
$user = new Models\User();
$user->setFirstName('John');
$user->setLastName('Smith');
$user->setEmail('j.smith@mail.com');

$post = new Models\Post();
$post->setTitle('My first post');
$post->setBody('This is my first blog post');
$post->setAuthor($user);

$blogContext = new BlogContext();
$blogContext->getUsers()->add($user);
$blogContext->getPosts()->add($post);

$blogContext->saveChanges();

Notice how the setAuthor() method of the Models\Post entity accepts the Models\User entity object. The DbContext will do a lookup for the Models\User entity and return the last insert id when the Models\Post entity is being persisted.

This brings me to the end of this article. Please feel free to leave your comments and suggestions.

History

  • 29th June, 2014: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)