Nasrul Hazim Bin Mohamad

WordPress: ORM Model Class

Feb
17

Object Relational Mapping or ORM is something that WordPress is missing, but that doesn’t mean we can’t create an abstract Model that act similar to ORM behavior.

Here are the first version to implement the ORM in WordPres – far from completions.

<?php 

abstract class Model {

	static $table_name = '';

	public static function table() {
		global $wpdb;

		return $wpdb->prefix.static::$table_name;
	}
	
	public static function all($conditions = '') {

		if(!empty(self::table())) {
			global $wpdb;

			$sql = "SELECT * FROM ".self::table() . $conditions;

			return $wpdb->get_results($sql);
		}

		return false;
	}

	public function row($conditions = '') {

		if(!empty(self::table())) {
			global $wpdb;

			$sql = "SELECT * FROM ".self::table() . $conditions;

			return $wpdb->get_row($sql);
		}

		return false;
	}

	public function findById($id) {
		global $wpdb;

		$sql = "SELECT * FROM ".self::table() . " WHERE id = '".$id."'";

		return $wpdb->get_row($sql);
	}

	public static function find($type, $parameters) {
		if(empty($type)) {
			$type = 'all';
		}

		$conditions = isset($parameters['conditions']) ? (' WHERE ' . join(' AND ',$parameters['conditions'])) : null;

		if($type == 'all') {
			return self::all($conditions);
		} else if($type == 'row') {
			return self::row($conditions);
		}

		return false;
	}

	public static function create($data) {
		global $wpdb;

		if($wpdb->insert(self::table(),$data)) {
			$id = $wpdb->insert_id;

			$data = self::findById($id);

			return $data;	
		}

		return false;		
	}

	public static function update($data, $where, $format = null, $where_format = null) {
		global $wpdb;

		return $wpdb->update( self::table(), $data, $where, $format, $where_format );
	}

	public static function delete($data) {
		global $wpdb;

		return $wpdb->delete( self::table(), $data );	
	}
}

Here are the implementation:

<?php

// make sure to include the abstract class first before extending the Model Class

class State extends Model {
	static $table_name = 'states';
}

The usage:

<?php

// include the State class in your plugin first

$states = State::all(); // fetch all available states
$state = State::findById(1); // find state with id = 1
$deleted = State::delete(['id' => 1]); // delete state record with id = 1

More features to add in – to have columns properties, what to hide, what to show, column alias and many more. But this is my first attempt to make the WordPress Plugin Development much easier (an attempt to replicate any famous PHP framework such Laravel, CakePHP and so on. Prefer to have something like Eloquent ORM)

How to Include PHP ActiveRecord in WordPress as Global Feature

Sep
12

This post for developers who used to ORM/ActiveRecord and not familiar with WordPress Query (like me) and want the ORM/ActiveRecord to be available in WordPress.

As per post’s title, this post regarding how you can include PHP ActiveRecord for global use – let say you have multiple plugins that use PHP ActiveRecord, you can’t load same PHP ActiveRecord classes / redeclared classed in an application or system – to be precise in PHP.

So the solution is to add in PHP ActiveRecord in {wordpress_installation}/wp-includes

Configurations

  1. Download PHP ActiveRecord and extract to {wordpress_installation}/wp-includes
  2. Create a new file in {wordpress_installation}/wp-includes/php-activerecord/ called AR.php and use the codes below
    <?php
    
    class AR {
    	static $conn;
    	static $model_directories;
    
    	public static function add_model_directories($list) {
    		if(!is_array($list)) {
    			return false;
    		}
    
    		if(!is_array(self::$model_directories)) {
    			self::$model_directories = array();
    		}
    
    		foreach ($list as $key => $value) {
    			if(!in_array($value, self::$model_directories)) {
    				self::$model_directories[] = $value;
    			}
    		}
    
    		return true;
    	}
    }
    
    function ar_init() {
    	$connections = array(
    		'development' => 'mysql://'.DB_USER.':'.DB_PASSWORD.'@'.DB_HOST.'/'.DB_NAME,
    	);
    
    	AR::$conn = ActiveRecord\Config::instance();
    	AR::$conn->set_model_directory(AR::$model_directories);
    	AR::$conn->set_connections($connections);
    }
    
  3. Update ActiveRecord\Config class in php-activerecord/lib/Config.php for method set_model_directory and get_model_directory. This to ensure we can include multiple model directories from multiple plugins.:
    	public function set_model_directory($dir)
    	{
    		if( is_array($dir) ) {
    			foreach( $dir as $k => $path ) {
    				if( !file_exists($path ) ) {
    					throw new ConfigException('Invalid or non-existent model directory: '.$path);
    				}
    			}
    		} else if( $dir  && !file_exists( $dir) ) {
    			throw new ConfigException('Invalid or non-existent model directory: '. $dir);
    		}
    
    		$this->model_directory = $dir;
    	}
    
    	public function get_model_directory()
    	{
    		return $this->model_directory;
    	}
    
  4. Include ActiveRecord.php and AR.php in {wordpress_installation}/wp-settings.php after Line 155:
    require( ABSPATH . WPINC . '/php-activerecord/ActiveRecord.php' );
    require( ABSPATH . WPINC . '/php-activerecord/AR.php' );
    
  5. Add the following codes after Line 241 to initialize PHP ActiveRecord:
    /**
     * Initialize PHP ActiveRecord
     */
    ar_init();
    

How to Use in Your Plugin

  1. Create your WordPress Plugin (you can use wppb.me)
  2. Open up your WordPress Plugin main file and add in the following code in order to include your model directory in AR class:
    AR::add_model_directories(array(
    	PLUGIN_PATH . 'includes' . DIRECTORY_SEPARATOR . 'models'
    ));
    
  3. Now create a sample model file which extend ActiveRecord\Model in your plugin’s model directory. Following are the sample codes:
    <?php
    
    class GTState extends ActiveRecord\Model {
    	static $table_name = 'gt_states';
    }
    
  4. Now you’re ready to use the GTState class in your view / controller.

I hope this trick will speed up the development progress.

Sorry WordPress Developers, I need a little hack to develop fast with PHP ActiveRecord, because I’m a beginner with WordPress, more things to learn.

References

  1. Load Multiple Model Directories
  2. PHP ActiveRecord Wiki

Web Services: Slim Framework + Eloquent(Laravel)

May
02

Setting up the Restful API is easy with Slim Framework & Eloquent(Laravel). Here are the steps:

  1. Install required components via composer – Slim Framework + Eloquent(Laravel)
  2. Create a database called web_services and create users table.
  3. Setting up Eloquent
  4. Setting up Routes
  5. Final Touch
Web Service Restful API

Web Service Restful API

Install the necessary components using composer – create a composer.json and save it in a working directory and run composer install.

{
    "name": "Web Services",    
    "require": {
        "php": ">=5.3.0",
        "slim/slim": "2.*",
        "slim/views": "0.1.*",
        "illuminate/database": "5.0.*",
        "illuminate/events": "5.0.*"
    },
    "autoload": {
        "classmap": [
            "models"
        ]
    }
}

We’re creating a simple web services for Users – Simple CRUD. Create a database called web_services and run the following SQL statement to create user table.

CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `phone` varchar(45) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Next, setting up the Eloquent. Create a database.php in config folder(Please create this folder in the working directory) folder.

$database = [
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'database'  => 'web_services',
    'username'  => 'root',
    'password'  => '',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
];

use IlluminateDatabaseCapsuleManager as Capsule;

$capsule = new Capsule;

$capsule->addConnection($database);

// Set the event dispatcher used by Eloquent models... (optional)
use IlluminateEventsDispatcher;
use IlluminateContainerContainer;
$capsule->setEventDispatcher(new Dispatcher(new Container));

// Make this Capsule instance available globally via static methods... (optional)
$capsule->setAsGlobal();

// Setup the Eloquent ORM... (optional; unless you've used setEventDispatcher())
$capsule->bootEloquent();

Once we’re done with Eloquent, next we going to set up the routes for Users CRUD operations. Save your time, copy & paste the following codes and save it in config/routes.php.

<?php

$app->get('/api/users', function () {
    echo User::all()->toJson();
});

$app->get('/api/users/:id',  function($id) {
	try {
		echo User::find($id)->toJson();
	} catch (Exception $e) {
		echo '{"error":{"text":'. 'Unable to get the web service. ' . $e->getMessage() .'}}';
	}
});

$app->get('/api/users/search/:query', function($query) {
	echo User::where('name', '=', $query)->get()->toJson();
});

$app->post('/api/users/add', function() use ($app) {
    try {
        $user = new User;

        $user->name = $app->request()->post('name');
        $user->phone = $app->request()->post('phone');
        $user->email = $app->request()->post('email');

        if($user->save()) {
            echo '{"message":"Successfully add new user"}';
        } else {
             echo '{"message":"Failed to add new user"}';
        }
    } catch (Exception $e) {
        echo '{"error":{"text":'. 'Unable to get the web service. ' . $e->getMessage() .'}}';
    }

});

$app->put('/api/users/update/:id', function($id) use ($app) {
    try {
        $user = User::find($id);

        $user->name = $app->request()->post('name');
        $user->phone = $app->request()->post('phone');
        $user->email = $app->request()->post('email');

        if($user->save()) {
            echo '{"message":"Successfully update user info"}';
        } else {
             echo '{"message":"Failed update user info"}';
        }   
    } catch (Exception $e) {
        echo '{"error":{"text":'. 'Unable to get the web service. ' . $e->getMessage() .'}}';
    }
    
});

$app->delete('/api/users/:id', function($id) {
	$user = User::find($id);
	
    if($user->delete()) {
        echo '{"message":"Successfully delete user"}';
    } else {
         echo '{"message":"Failed to delete user"}';
    }
});

OK, here the final touch! 2 things to do, one the index.php and the other one is .htaccess. Here the index.php. Please take note, we require the header("Access-Control-Allow-Origin: *");, to ensured that we can access this Restful API from other domains. There might be security issues. Comments are welcomed.

<?php
try {
	header("Access-Control-Allow-Origin: *");
	require 'vendor/autoload.php';
	require 'config/database.php';
	require 'config/initialize.php';
} catch (Exception $e) {
	echo '{"error":{"text":'. 'Unable to start up the web service. ' . $e->getMessage() .'}}';
}

The .htaccess.

RewriteEngine On

# Some hosts may require you to use the `RewriteBase` directive.
# If you need to use the `RewriteBase` directive, it should be the
# absolute physical path to the directory that contains this htaccess file.
#
# RewriteBase /

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Source code available at: Web Service(Restful API)

I Have 3 demo available:

  1. Web Service App
  2. Web Service Demo
  3. Web Service Mobile Demo

 

Micro-framework: Slim

Apr
22

Setting up Slim Application

  • Create a folder for your project, mine called slim
  • create a composer.json in the directory and open up your Command Prompt / Terminal and run composer install. Later you should have something similar to Screenshot #2.
    mf-composer

    Screenshot #1

    mf-structure

    Screenshot #2

Setting up RedBeanPHP4

  • Download RedbeanPHP4
  • Create a folder named RedBeanPHP4 in vendor directory and extract download zip file above and copy the rb.php into slim/vendor/RedBeanPHP4 – see Screenshot #2.
  • Include the rb.php in slim/vendor/autoloader.php
    mf-autoloader
  • Then, add MySQL connection settings.

Let’s start it!

  • Create a index.php in slim directory and add the following:
    mf-slimframeworkBasically what’s in this index.php is to add new user and view a user details.
  • You may need to create new database, then new table named users. I used the following fields – id, name, email, created and modified
  • Next, when the database and tables created, you may run http://localhost/slim/users/add/Nasrul Hazim/email@gmail.com and you may want to add more records. Suppose each time new user created, you will be redirected to the view user info page – http://localhost/slim/users/view/1 and see the next screenshot – I only var_dump the user details and you want to add some nice view or probably pass a JSON encoding.
    mf-db
    mf-readbean-orm