<?php
/**
* A class to help with adding build methods
* to your fActiveRecord models.
*
* @author Jeff Turcotte <jeff.turcotte@gmail.com>
* @license MIT
* @version 1.0
*
* Usage:
*
* $builder = Builder::create('Page');
*
* $builder->setDefaultOrder(array('name' => 'asc'));
*
* $builder->register('buildActive', function(&$where) {
* $where['status='] = 'active';
* });
*
* $builder->extend('buildActive', 'buildActivePublished', function(&$where) {
* $where['published='] = TRUE;
* });
*
* Page::buildActivePublished(10, 1);
*
*/
class Builder {
static protected $registry;
protected $callbacks;
protected $class;
protected $default_order;
/**
* Create a Builder based off of the class name.
* If a Builder for the supplied class exists, it will
* return that.
*
* @param $class string The fActiveRecord class to use
*
* @return Builder
*/
public function create($class) {
if (isset(self::$registry[$class])) {
return $registry[$class];
}
$instance = new self();
$instance->callbacks = array();
$instance->class = $class;
self::$registry[$class] = $instance;
return $instance;
}
/**
* Set a default order by clause for all build
* methods. Can be altered by the registered build
* methods if necessary.
*
* @param $order Array The order by
*
* @return void
*/
public function setDefaultOrder($order)
{
$this->default_order = (array) $order;
}
/**
* Register a build method on the record
*
* The callback can take four references:
* &$where The where clauses to modify
* &$order The order by clauses to modify
* &$limit The limit to modify
* &$page The page to modify
*
* @param $method string The method name to register
* @param $callback callable The builder callback, see method desc.
*
* @return void
*/
public function register($method, $callback=NULL)
{
$this->callbacks[$method] = $callback;
fORM::registerActiveRecordStaticMethod(
$this->class, $method, $this->makeBuilder($callback)
);
}
/**
* Extends an existing build method.
*
* @param $parent string The existing method name to extend
* @param $method string The new method name to register
* @param $callback callable The builder callback, see register() docs for desc.
*
* @return void
*/
public function extend($parent, $method, $callback=NULL)
{
$parent_callback = $this->callbacks[$parent];
$this->register($method, function(&$where, &$order, &$limit, &$page) use ($parent_callback, $callback) {
$parent_callback($where, $order, $limit, $page);
$callback($where, $order, $limit, $page);
});
}
/**
* Makes a builder callback to register on fActiveRecord
*
* This creates an fActiveRecord method that takes the following parameters:
*
* $limit
* The custom limit for the returned fRecordSet
* $page
* The custom page to limit to
* $order
* The custom order array for the fRecordSet
* $inline_callback
* A callback in the same form as when registering a builder.
* Can be in any argument position, as long as it's last.
* For inline extensions, such as search.
*
* @param $callback callable The builder callback, see register() docs for desc
*
* @return Closure
*/
protected function makeBuilder($callback=NULL)
{
$default_order = $this->default_order;
return function($class, $method, $args) use ($callback, &$default_order) {
$where = array();
$order = array();
$limit = NULL;
$page = NULL;
$inline_callback = NULL;
$arg_names = array(
'limit',
'page',
'order',
'inline_callback'
);
foreach($arg_names as $key => $value) {
if (isset($args[$key])) {
if (count($args)-1 == $key && is_callable($args[$key])) {
$inline_callback = $args[$key];
break;
}
$$value = $args[$key];
}
}
call_user_func_array($callback, array(
&$where, &$order, &$limit, &$page
));
if ($inline_callback) {
call_user_func_array($inline_callback, array(
&$where, &$order, &$limit, &$page
));
}
foreach($default_order as $column => $dir) {
if (!isset($order[$column])) {
$order[$column] = $dir;
}
}
return fRecordSet::build(
$class, $where, $order, $limit, $page
);
};
}
}