Skip to content

Instantly share code, notes, and snippets.

@jeffturcotte
Last active December 11, 2015 03:58
Show Gist options
  • Save jeffturcotte/4541246 to your computer and use it in GitHub Desktop.
Save jeffturcotte/4541246 to your computer and use it in GitHub Desktop.
<?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
);
};
}
}
@mattsah
Copy link

mattsah commented Jan 22, 2013

Did you give any thought to also building out such methods in traits? Just curious. Not sure how many servers are running 5.4, but I'd imagine a few traits might offer a pretty good build*() collection on most classes. I was just thinking about this cause I was writing that documentation stuff, and remembered I was aiming to get away from a lot of hidden methods like this in my own code:

trait BuildActive
{
      static public function buildActive(...)
      {
           return fRecordSet::build(get_called_class(), ['active=' => 1], ...);
      }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment