chaw / branches / master / models / behaviors / state_machine.php
history
<?php
/**
* Chaw : source code and project management
*
* @copyright Copyright 2009, Garrett J. Woodworth (gwoohoo@gmail.com)
* @license GNU AFFERO GENERAL PUBLIC LICENSE v3 (http://opensource.org/licenses/agpl-v3.html)
*
*/
/**
* undocumented class
*
* @package default
*/
class StateMachineBehavior extends ModelBehavior {
/**
* undocumented function
*
* @param string $model
* @param string $config
* @return void
*/
function setup(&$model, $config) {
$defaults = array(
'field' => 'state', 'default' => null, 'states' => array(),
'events' => array(), 'transitions' => array(), 'auto' => false
);
$config += $defaults;
if (empty($config['events']) && !empty($config['transitions'])) {
$config['events'] = array_keys($config['transitions']);
}
if (empty($config['events'])) {
$message = 'StateMachineBehavior::setup() - You must define at least one event';
trigger_error($message, E_USER_WARNING);
return false;
}
$this->settings[$model->name] = ($config += $defaults);
$this->mapMethods['/^(' . join('|', $config['events']) . ')$/'] = 'event';
}
/**
* undocumented function
*
* @param string $model
* @param string $from
* @param string $to
* @return void
*/
function transition(&$model, $from, $to = null) {
$model->recursive = -1;
if (!empty($model->data[$model->alias][$this->settings[$model->name]['field']])) {
$state = $model->data[$model->alias][$this->settings[$model->name]['field']];
} else {
$state = $model->field($this->settings[$model->name]['field']);
}
if (in_array($state, (array)$from)) {
if (empty($model->data[$model->alias]['event'])) {
return $model->saveField($this->settings[$model->name]['field'], $to);
} else {
$model->data[$model->alias][$this->settings[$model->name]['field']] = $to;
return true;
}
}
return false;
}
function event(&$model, $event) {
if (!isset($this->settings[$model->name]['transitions'][$event])) {
return false;
}
$settings = $this->settings[$model->name];
if ($settings['auto'] === false) {
return $model->transitions($event);
}
if ($settings['auto'] == true || $settings['auto'] == 'before') {
if (!$model->transitions($event)) {
return false;
}
}
$success = false;
if (isset($settings['transitions'][$event])) {
foreach ($settings['transitions'][$event] as $from => $to) {
if ($this->transition($model, $from, $to)) {
$success = true;
break;
}
}
}
if ($success && $settings['auto'] == 'after') {
return $model->transitions($event);
}
return $success;
}
/**
* If 'transitions' is defined in settings, returns the possible transition events for the
* current state.
*
* @return array
*/
function events(&$model, $state = null) {
$settings = $this->settings[$model->name];
$model->recursive = -1;
$state = is_null($state) ? $model->field($settings['field']) : $state;
$results = array();
if (empty($settings['transitions'])) {
return array();
}
foreach ((array)$state as $key) {
foreach ($settings['transitions'] as $event => $transitions) {
if (isset($transitions[$key]) && $event != 'close') {
$results[$event] = $event;
}
}
}
return $results;
}
/**
* If 'transitions' is defined in settings, returns the possible transition events for the
* current state.
*
* @return array
*/
function states(&$model) {
$settings = $this->settings[$model->name];
return array_combine($settings['states'], $settings['states']);
}
/**
* Must be overridden in the attached model class to handle state transitions. I was going to
* do something else with this, but I can't remember what.
*
* @param string $event
* @return void
*/
function transitions($event = null) {
if (is_object($event)) {
$message = "StateMachineBehavior::transitions() - You must define {$event->name}";
$message .= "::(\$event) in your {$event->name} model before continuing";
trigger_error($message, E_USER_WARNING);
}
return false;
}
/**
* undocumented function
*
* @param string $model
* @return void
*/
function beforeValidate(&$model) {
if ($model->exists()) {
return true;
}
$settings = $this->settings[$model->name];
if (empty($model->data[$model->alias][$settings['field']])) {
$model->set($settings['field'], $settings['default']);
}
return true;
}
}
?>