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
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
$blogContext = new BlogContext();
$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
namespace Models;
class User {
protected $user_id;
protected $first_name;
protected $last_name;
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
$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
$blogContext = new BlogContext();
$user = $blogContext->getUsers()->find(3);
$user->setEmail('new@mail.com');
$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
$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