Embedding Custom Finds in Behaviors
Today, while outlining a datasource for a CDN, I REALLY needed to add a custom model find from outside of the model. Custom find types would greatly simplify the Model api and give the developer access to exotic types of finds without having to specify ridiculous option keys.
By default, adding a custom model find is as follows:
- Add the find to the
$_findMethods
model property:
class Post extends Model {
function __construct($id = false, $table = null, $ds = null) {
$this->_findMethods['custom'] = true;
parent::__construct($id, $table, $ds);
}
}
- Define the method
class Post extends Model {
function __construct($id = false, $table = null, $ds = null) {
$this->_findMethods['custom'] = true;
parent::__construct($id, $table, $ds);
}
function _findCustom($state, $query, $results = array()) {
if ($state == 'before') {
$this->log('using custom find method');
return $query;
}
return $results;
}
}
- Win at life
Unfortunately, this doesn’t quite work as I thought it would by doing similarly in the ModelBehavior::setup()
method. Usually methods are passed through to the behaviors via Model::call__()
, but they are not when the method begins with the underscore (_
) character. Lame.
So a work-around is to use the oft-forgotten $mapMethods
method. Lets define a simple behavior:
class CustomBehavior extends ModelBehavior {
var $mapMethods = array();
function setup(&$model, $settings = array()) {
}
}
- Define the custom find method:
class CustomBehavior extends ModelBehavior {
var $mapMethods = array();
function setup(&$model, $settings = array()) {
}
function _findCustom($state, $query, $results = array()) {
if ($state == 'before') {
$this->log('using custom find method');
return $query;
}
return $results;
}
}
- Add the custom find to the available custom finds for the Model:
class CustomBehavior extends ModelBehavior {
var $mapMethods = array();
function setup(&$model, $settings = array()) {
$model->_findMethods['custom'] = true;
}
function _findCustom($state, $query, $results = array()) {
if ($state == 'before') {
$this->log('using custom find method');
return $query;
}
return $results;
}
}
- Add the appropriate
$mapMethods
regex:
class CustomBehavior extends ModelBehavior {
var $mapMethods = array('/\b_findCustom\b/' => '_findCustom');
function setup(&$model, $settings = array()) {
$model->_findMethods['custom'] = true;
}
function _findCustom($state, $query, $results = array()) {
if ($state == 'before') {
$this->log('using custom find method');
return $query;
}
return $results;
}
}
- Ensure that the arguments in your
_findCustom()
method don’t overlap with howModelBehavior::dispatchMethod()
works:
class CustomBehavior extends ModelBehavior {
var $mapMethods = array('/\b_findCustom\b/' => '_findCustom');
function setup(&$model, $settings = array()) {
$model->_findMethods['custom'] = true;
}
function _findCustom(&$model, $functionCall, $state, $query, $results = array()) {
if ($state == 'before') {
$this->log('using custom find method');
return $query;
}
return $results;
}
}
- Win at life
This method can be used to add as many custom finds as necessary to your models. You will need to be careful that your regex doesn’t match incorrectly, as $mapMethods
uses a case-insensitive preg_match()
in order to map the method. So just make your custom finds unique and everything should be fine. You can also use \b
in your pattern so that partial matches do not trigger a find incorrectly.