I've seen many PHP ORM libraries. Most of them make you write a new class for each item you want to keep in the database. Extending this and that for no apparent reason is repetitious and capricious.
Since items have fields of similar data and similar relations, a well-written class can be used for all. If you need a library which makes things easier, this is my approach.
arrayDB ORM library has only 5 classes. You mostly use a singleton of one, while others are used internally, that's all. Caching and keeping cache synchronized with the database is all automated, you don't need to keep track of these.
To start using this library, you simply have to define:
- Your data model (what items you need to keep, their fields and relations with each other).
- Your MySQL access.
- Your cache config.
Defining data model
All data model definitions are written as an array like this:
$model=array( 'user'=>array( 'conf'=>array('len'=>7), 'fields'=>array( 'name'=>array('len'=>50) ), 'has_many'=>array( 'posts'=>array('type'=>'post', 'foreign_name'=>'writer') ), 'many_to_many'=>array( 'liked_posts'=>array('type'=>'post', 'foreign_name'=>'likers'), ), 'self_ref'=>array('friends') ), 'post'=>array( 'conf'=>array('len'=>10), 'fields'=>array( 'text'=>array('len'=>200), 'view_count'=>array('type'=>'numeric', 'len'=>5) // default field type is text, define if numeric ) ) );
We have 2 items here: User and post.
User has names, many written posts and many liked posts. Posts has texts, view counts and likers.
User also has many users as friends.
With this model, we want to reach posts of a user as $user['posts'] and writers of a post as $post['writer']. This is a one-to-many relation.
We also want to reach liked posts of a user as $user['liked_posts'] and likers of a post as $post['likers']. This is a many-to-many relation.
Finally we want to reach friends of a user as $user['friends']. This is a self-referencing relation.
Defining MySQL access
Defining MySQL access is also writing an array like this:
$db_config=array( 'hostname'=>'localhost', 'database'=>'social', 'username'=>'root', 'password'=>'' );
Defining cache config
For now, we have three cache types implemented: APC, Memcached and plain text file.
To use APC, this config array is enough:
$cache_config=array('type'=>'apc');
To use Memcached, you need to give some parameters:
$cache_config=array('type'=>'memcached', 'host'=>'127.0.0.1', 'port'=>11211, 'timeout'=>1);
To use plain text files, you need to create a readable and writable directory and provide the absolute path:
$cache_config=array('type'=>'file', 'path'=>'/tmp/my_project_cache')
There is an optional "prefix" parameter. It is applied to cache keys if given.
Start Using
Now it’s time to use what we defined. We just need these lines to initialize the library.
DB::init($db_config); CACHE::init($cache_config); ADB::init($model); $adb=ADB::get_instance();
Creating Tables
This is a one time job. We tell the library to create the database tables as required. It does all the hard work for relations and etc.
$adb->create_tables();
We need to run this method once when we deploy it to somewhere and never again. It causes data loss if called after inserting data.
Using Items
We have this $adb instance in hand. We will reach all the data through that.
Creating Items
We provide the name of item and an array of data by field names to create an item.
$uid1=$adb->create('user', array('name'=>'John')); $uid2=$adb->create('user', array('name'=>'Marry')); $pid1=$adb->create('post', array( 'writer'=>$uid1, 'text'=>'What a wonderful world!' )); $pid2=$adb->create('post', array( 'writer'=>$uid2, 'text'=>'Life is beautiful!' ));
Creating Many-to-many Relations
The first parameter is the name of the item. The second parameter is the local name of the related item. The third parameter is the ID of the item, and the fourth parameter is the ID of the related item.
$adb->relate('user', 'friends', $uid1, $uid2); $adb->relate('user', 'liked_posts', $uid1, $pid1); // self post liking :) $adb->relate('user', 'liked_posts', $uid1, $pid2); $adb->relate('user', 'liked_posts', $uid2, $pid1);
Listing Data
We can list all users with posts and their likers in a simple loop:
foreach ($adb->id_list('user') as $uid) { // load user $user=$adb->load('user', $uid); echo '<h1>' . $user['name'] . '</h1>' . "\n"; echo '<h2>Posts: </h2>' . "\n"; echo '<ul>' . "\n"; foreach ($user['posts'] as $pid) { //load post of user $post=$adb->load('post', $pid); $likers=array(); foreach ($post['likers'] as $lid) { // load liker of post $liker=$adb->load('user', $lid); $likers[]=$liker['name']; } $likers=(count($likers)) ? '<br />' . implode(', ', $likers) . ' liked.' : ''; echo '<li>' . $post['text'] . ' ' . $likers . '</li>' . "\n"; } echo '</ul>' . "\n"; }
Updating Items
We can update any field of any item like this:
$user1=$adb->load('user', $uid1); $user1['name']='Jack'; // no save method needed, save and keeping cache updated is all automated.
If we need to update more than one field at a time, this is the alternative:
$post1=$adb->load('post', $pid1); $post1->update(array('writer'=>$uid2, 'text'=>'Not a wonderful world!'));
Removing Relations
It's just same as relating:
$adb->unrelate('user', 'friends', $uid1, $uid2); $adb->unrelate('user', 'liked_posts', $uid1, $pid1);
Removing Items
We can delete items while keeping or removing belongings.
$adb->delete('user', $uid1); // user deleted, posts remain unowned. $adb->delete('user', $uid1, true); // user and all posts of user deleted.
Querying More
As an example, we want to get the five most-liked posts. All we need is this:
foreach ($adb->id_list('post', false, 'likers DESC', 5) as $pid) { $post=$adb->load('post', $pid); // do anything with post }
Or, if we want to get posts of a user with more than 5 likes, we would do the following:
$user=$adb->load('user', $uid1); foreach ($user->id_list('post', 'view_count>5') as $pid) { $post=$adb->load('post', $pid); // do anything with post }
The Main Goal
For a separate post page, our code will be this simple:
$post=$adb->load('post', $pid1); $writer=$adb->load('user', $post['writer']); echo $writer['name'] . ' wrote' . "<br />\n"; echo $post['text'] . "<br />\n"; $post['view_count']++; // yes, increasing view_count is this simple
Are there any queries or cache logics in this code? No the main goal is simplicity.
There are a lot of famous alternatives to use. They are documented better and supported better. This library is not one of them; it is not a well-oiled machine yet. It is the most simple and easy to learn approach, in my opinion. If the goal of ORM libraries is to isolate coder from database logic, this one is the most assertive one among the other new ones . jQuery was the easiest javascript framework and became a standard just because of that. So, an easy to use PHP ORM library has a chance that way.
Here is the library: [arrayDB github]
All ideas and contributions are welcomed.
About the Author
Mustafa Dokumaci is a software developer from Istanbul, Turkey. His education is in Environmental Engineering and Accounting, but he now works for sporcum.com and hipsin.com. Mustafa has been working with PHP, MySQL, Apache, Nginx, Python, CodeIgniter, Magento etc. for the last six years.