Last active
October 2, 2015 06:53
-
-
Save raoul2000/c38165b7e6b5f12a6945 to your computer and use it in GitHub Desktop.
yii2-workflow - WorkflowDbSource prototype
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | |
-- schema screenshot : http://s172418307.onlinehome.fr/public/raoul2000/static/workflowDbSource-schema.png | |
-- | |
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; | |
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; | |
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES'; | |
-- ----------------------------------------------------- | |
-- Schema yii2_workflow_test | |
-- ----------------------------------------------------- | |
-- ----------------------------------------------------- | |
-- Schema yii2_workflow_test | |
-- ----------------------------------------------------- | |
CREATE SCHEMA IF NOT EXISTS `yii2_workflow_test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ; | |
USE `yii2_workflow_test` ; | |
-- ----------------------------------------------------- | |
-- Table `yii2_workflow_test`.`sw_status` | |
-- ----------------------------------------------------- | |
CREATE TABLE IF NOT EXISTS `yii2_workflow_test`.`sw_status` ( | |
`id` VARCHAR(20) NOT NULL, | |
`workflow_id` VARCHAR(20) NOT NULL, | |
`label` VARCHAR(45) NULL, | |
PRIMARY KEY (`id`, `workflow_id`), | |
INDEX `fk_sw_status_sw_workflow1_idx` (`workflow_id` ASC), | |
CONSTRAINT `fk_sw_status_sw_workflow1` | |
FOREIGN KEY (`workflow_id`) | |
REFERENCES `yii2_workflow_test`.`sw_workflow` (`id`) | |
ON DELETE NO ACTION | |
ON UPDATE NO ACTION) | |
ENGINE = InnoDB; | |
-- ----------------------------------------------------- | |
-- Table `yii2_workflow_test`.`sw_workflow` | |
-- ----------------------------------------------------- | |
CREATE TABLE IF NOT EXISTS `yii2_workflow_test`.`sw_workflow` ( | |
`id` VARCHAR(20) NOT NULL, | |
`initial_status_id` VARCHAR(20) NULL, | |
PRIMARY KEY (`id`), | |
INDEX `fk_sw_workflow_sw_status_idx` (`initial_status_id` ASC), | |
CONSTRAINT `fk_sw_workflow_sw_status` | |
FOREIGN KEY (`initial_status_id`) | |
REFERENCES `yii2_workflow_test`.`sw_status` (`id`) | |
ON DELETE NO ACTION | |
ON UPDATE NO ACTION) | |
ENGINE = InnoDB; | |
-- ----------------------------------------------------- | |
-- Table `yii2_workflow_test`.`sw_transition` | |
-- ----------------------------------------------------- | |
CREATE TABLE IF NOT EXISTS `yii2_workflow_test`.`sw_transition` ( | |
`start_status_id` VARCHAR(20) NOT NULL, | |
`start_status_workflow_id` VARCHAR(20) NOT NULL, | |
`end_status_id` VARCHAR(20) NOT NULL, | |
`end_status_workflow_id` VARCHAR(20) NOT NULL, | |
PRIMARY KEY (`start_status_id`, `start_status_workflow_id`, `end_status_id`, `end_status_workflow_id`), | |
INDEX `fk_sw_transition_sw_status1_idx` (`end_status_id` ASC, `end_status_workflow_id` ASC), | |
CONSTRAINT `fk_start_status` | |
FOREIGN KEY (`start_status_id` , `start_status_workflow_id`) | |
REFERENCES `yii2_workflow_test`.`sw_status` (`id` , `workflow_id`) | |
ON DELETE NO ACTION | |
ON UPDATE NO ACTION, | |
CONSTRAINT `fk_end_status` | |
FOREIGN KEY (`end_status_id` , `end_status_workflow_id`) | |
REFERENCES `yii2_workflow_test`.`sw_status` (`id` , `workflow_id`) | |
ON DELETE NO ACTION | |
ON UPDATE NO ACTION) | |
ENGINE = InnoDB; | |
SET SQL_MODE=@OLD_SQL_MODE; | |
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; | |
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace app\models; | |
use Yii; | |
/** | |
* This is the model class for table "{{%sw_status}}". | |
* | |
* @property string $id | |
* @property string $workflow_id | |
* @property string $label | |
* | |
* @property SwWorkflow $workflow | |
* @property SwTransition[] $swTransitions | |
* @property SwWorkflow[] $swWorkflows | |
*/ | |
class SwStatus extends \yii\db\ActiveRecord | |
{ | |
/** | |
* @inheritdoc | |
*/ | |
public static function tableName() | |
{ | |
return '{{%sw_status}}'; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function rules() | |
{ | |
return [ | |
[['id', 'workflow_id'], 'required'], | |
[['id', 'workflow_id'], 'string', 'max' => 20], | |
[['label'], 'string', 'max' => 45] | |
]; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function attributeLabels() | |
{ | |
return [ | |
'id' => 'ID', | |
'workflow_id' => 'Workflow ID', | |
'label' => 'Label', | |
]; | |
} | |
/** | |
* @return \yii\db\ActiveQuery | |
*/ | |
public function getWorkflow() | |
{ | |
return $this->hasOne(SwWorkflow::className(), ['id' => 'workflow_id']); | |
} | |
/** | |
* @return \yii\db\ActiveQuery | |
*/ | |
public function getTransitions() | |
{ | |
return $this->hasMany(SwTransition::className(), ['end_status_id' => 'id', 'end_status_workflow_id' => 'workflow_id']); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace app\models; | |
use Yii; | |
/** | |
* This is the model class for table "{{%sw_transition}}". | |
* | |
* @property string $start_status_id | |
* @property string $start_status_workflow_id | |
* @property string $end_status_id | |
* @property string $end_status_workflow_id | |
* | |
* @property SwStatus $startStatus | |
* @property SwStatus $endStatus | |
*/ | |
class SwTransition extends \yii\db\ActiveRecord | |
{ | |
/** | |
* @inheritdoc | |
*/ | |
public static function tableName() | |
{ | |
return '{{%sw_transition}}'; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function rules() | |
{ | |
return [ | |
[['start_status_id', 'start_status_workflow_id', 'end_status_id', 'end_status_workflow_id'], 'required'], | |
[['start_status_id', 'start_status_workflow_id', 'end_status_id', 'end_status_workflow_id'], 'string', 'max' => 20] | |
]; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function attributeLabels() | |
{ | |
return [ | |
'start_status_id' => 'Start Status ID', | |
'start_status_workflow_id' => 'Start Status Workflow ID', | |
'end_status_id' => 'End Status ID', | |
'end_status_workflow_id' => 'End Status Workflow ID', | |
]; | |
} | |
/** | |
* @return \yii\db\ActiveQuery | |
*/ | |
public function getStartStatus() | |
{ | |
return $this->hasOne(SwStatus::className(), ['id' => 'start_status_id', 'workflow_id' => 'start_status_workflow_id']); | |
} | |
/** | |
* @return \yii\db\ActiveQuery | |
*/ | |
public function getEndStatus() | |
{ | |
return $this->hasOne(SwStatus::className(), ['id' => 'end_status_id', 'workflow_id' => 'end_status_workflow_id']); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace app\models; | |
use Yii; | |
/** | |
* This is the model class for table "{{%sw_workflow}}". | |
* | |
* @property string $id | |
* @property string $initial_status_id | |
* | |
* @property SwStatus[] $swStatuses | |
* @property SwStatus $initialStatus | |
*/ | |
class SwWorkflow extends \yii\db\ActiveRecord | |
{ | |
/** | |
* @inheritdoc | |
*/ | |
public static function tableName() | |
{ | |
return '{{%sw_workflow}}'; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function rules() | |
{ | |
return [ | |
[['id'], 'required'], | |
[['id', 'initial_status_id'], 'string', 'max' => 20] | |
]; | |
} | |
/** | |
* @inheritdoc | |
*/ | |
public function attributeLabels() | |
{ | |
return [ | |
'id' => 'ID', | |
'initial_status_id' => 'Initial Status ID', | |
]; | |
} | |
/** | |
* @return \yii\db\ActiveQuery | |
*/ | |
public function getStatuses() | |
{ | |
return $this->hasMany(SwStatus::className(), ['workflow_id' => 'id']); | |
} | |
/** | |
* @return \yii\db\ActiveQuery | |
*/ | |
public function getInitialStatus() | |
{ | |
return $this->hasOne(SwStatus::className(), ['id' => 'initial_status_id']); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace app\components; | |
use Yii; | |
use yii\base\Object; | |
use yii\base\InvalidConfigException; | |
use yii\helpers\Inflector; | |
use yii\helpers\VarDumper; | |
use raoul2000\workflow\base\Status; | |
use raoul2000\workflow\base\Transition; | |
use raoul2000\workflow\base\Workflow; | |
use raoul2000\workflow\base\WorkflowException; | |
use raoul2000\workflow\source\IWorkflowSource; | |
use yii\helpers\ArrayHelper; | |
use raoul2000\workflow\base\WorkflowValidationException; | |
use app\models\SwStatus; | |
use app\models\SwWorkflow; | |
use app\models\SwTransition; | |
/** | |
* This is a draft WorkflowDbSource component dedicated to read workflow | |
* definition from DB. | |
* | |
* IT IS FAR FROM COMPLETE AND MUST NOT BE USED FOR ANY OTHER PURPOSE THAN | |
* TEST AND PROTOTYPE. | |
* | |
* It doesn't implement many features available in the WorkflowFileSource component | |
* released with yii2-workflow but can be used as a starting point to develop | |
* a production ready component. | |
* | |
* Among missing features : | |
* - Status, Transition and Workllow class mapping to allow usage of custom | |
* classes for those objects | |
* - metadata | |
* - short id usage (in this version canonical status ids must be used) | |
* - robust test for method arguments (in particular ids) | |
* | |
* The underlying DB schema is also very simple and only include those columns | |
* which are required by yii2-workflow. | |
* | |
* @author Raoul | |
* | |
*/ | |
class WorkflowDbSource extends Object implements IWorkflowSource | |
{ | |
const SEPARATOR_STATUS_NAME = '/'; | |
/** | |
* @var Workflow[] list of workflow instances indexed by workflow id | |
*/ | |
private $_w = []; | |
/** | |
* @var Status[] list status instances indexed by their id | |
*/ | |
private $_s = []; | |
private $_allStatusLoaded = false; | |
/** | |
* @var Transition[] list of out-going Transition instances indexed by the start status id | |
*/ | |
private $_t = []; | |
/** | |
* @see \raoul2000\workflow\source\IWorkflowSource::getStatus() | |
*/ | |
public function getStatus($id, $model = null) { | |
list($wId, $stId) = $this->parseStatusId($id); | |
$canonicalStId = $wId . self::SEPARATOR_STATUS_NAME . $stId; | |
// TODO : implement status class map | |
if ( ! array_key_exists($canonicalStId, $this->_s) ) { | |
$statusModel = SwStatus::findOne([ | |
'workflow_id' => $wId, | |
'id' => $stId | |
]); | |
if( $statusModel == null) { | |
throw new WorkflowException('No status found with id '. $id); | |
} | |
$this->_s[$canonicalStId] = Yii::createObject([ | |
'class' => 'raoul2000\workflow\base\Status', | |
'id' => $canonicalStId, | |
'workflowId' => $statusModel->workflow_id, | |
'label' => isset($statusModel->label) ? $statusModel->label : Inflector::camel2words($stId, true), | |
'source' => $this | |
]); | |
} | |
return $this->_s[$canonicalStId]; | |
} | |
/** | |
* @see \raoul2000\workflow\source\IWorkflowSource::getAllStatuses() | |
*/ | |
public function getAllStatuses($workflowId) { | |
if ( ! $this->_allStatusLoaded ) { | |
$loadedStatusIds = array_keys($this->_s); | |
$dbStatus = SwStatus::find() | |
->where(['workflow_id' => $workflowId]) | |
->andWhere(['NOT IN', 'id', $loadedStatusIds]) | |
->all(); | |
foreach($dbStatus as $status){ | |
$canonicalStId = $status->workflow_id.self::SEPARATOR_STATUS_NAME.$status->id; | |
$this->_s[$canonicalStId] = Yii::createObject([ | |
'class' => 'raoul2000\workflow\base\Status', | |
'id' => $canonicalStId, | |
'workflowId' => $status->workflow_id, | |
'label' => isset($status->label) ? $status->label : Inflector::camel2words($status->id, true), | |
'source' => $this | |
]); | |
} | |
$this->_allStatusLoaded = true; | |
} | |
return $this->_s; | |
} | |
/** | |
* @see \raoul2000\workflow\source\IWorkflowSource::getTransitions() | |
*/ | |
public function getTransitions($startStatusId, $model = null) { | |
list($wId, $stId) = $this->parseStatusId($startStatusId); | |
$startId = $wId.self::SEPARATOR_STATUS_NAME.$stId; | |
if ( ! array_key_exists($startId, $this->_t) ) { | |
$transInstance = []; | |
$transitions = SwTransition::findAll([ | |
'start_status_id' => $stId, | |
'start_status_workflow_id' => $wId | |
]); | |
foreach($transitions as $transition) { | |
// TODO : implement transition class map | |
$endId = $transition->end_status_workflow_id.self::SEPARATOR_STATUS_NAME.$transition->end_status_id; | |
$transInstance[] = Yii::createObject([ | |
'class' => 'raoul2000\workflow\base\Transition', | |
'start' => $this->getStatus($startId), | |
'end' => $this->getStatus($endId), | |
'source' => $this | |
]); | |
} | |
$this->_t[$startId] = $transInstance; | |
} | |
return $this->_t[$startId]; | |
} | |
/** | |
* @see \raoul2000\workflow\source\IWorkflowSource::getTransition() | |
*/ | |
public function getTransition($startId, $endId, $defaultWorkflowId = null) { | |
$tr = $this->getTransitions($startId, $defaultWorkflowId); | |
if ( count($tr) > 0 ) { | |
foreach ($tr as $aTransition) { | |
if ($aTransition->getEndStatus()->getId() == $endId) { | |
return $aTransition; | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* @see \raoul2000\workflow\source\IWorkflowSource::getWorkflow() | |
*/ | |
public function getWorkflow($id) { | |
// TODO : validate that initial status is valid | |
// TODO : implement status class map | |
if ( ! array_key_exists($id, $this->_w) ) { | |
$workflowModel = SwWorkflow::findOne([ | |
'id' => $id | |
]); | |
if( $workflowModel == null) { | |
throw new WorkflowException('No workflow found with id '. $id); | |
} | |
$initialStatusId = $workflowModel->id.self::SEPARATOR_STATUS_NAME.$workflowModel->initial_status_id; | |
$this->_w[$id] = Yii::createObject([ | |
'class' => 'raoul2000\workflow\base\Workflow', | |
'id' => $id, | |
'initialStatusId' => $initialStatusId, | |
'source' => $this | |
]); | |
} | |
return $this->_w[$id]; | |
} | |
/** | |
* | |
* @param string $val canonical id (e.g. myWorkflow/myStatus) | |
* @return array: | |
*/ | |
public function parseStatusId($val) { | |
// TODO : validate $val and once splitted in workflow_id and status_id | |
// ensure they are both valid | |
$tokens = array_map('trim', explode(self::SEPARATOR_STATUS_NAME, $val)); | |
return $tokens; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Migrate: