Files
luos/workflow/engine/src/ProcessMaker/Project/Adapter/BpmnWorkflow.php

401 lines
14 KiB
PHP
Raw Normal View History

<?php
namespace ProcessMaker\Project\Adapter;
use ProcessMaker\Project;
use ProcessMaker\Util\Hash;
/**
* Class BpmnWorkflow
*
* @package ProcessMaker\Project\Adapter
* @author Erik Amaru Ortiz <aortiz.erik@gmail.com, erik@colosa.com>
*/
class BpmnWorkflow extends Project\Bpmn
{
/**
* @var \ProcessMaker\Project\Workflow
*/
protected $wp;
const BPMN_GATEWAY_COMPLEX = "COMPLEX";
const BPMN_GATEWAY_PARALLEL = "PARALLEL";
const BPMN_GATEWAY_INCLUSIVE = "INCLUSIVE";
const BPMN_GATEWAY_EXCLUSIVE = "EXCLUSIVE";
/**
* OVERRIDES
*/
public static function load($prjUid)
{
$parent = parent::load($prjUid);
//return new BpmnWorkflow();
$me = new BpmnWorkflow();
$me->project = $parent->project;
$me->prjUid = $parent->project->getPrjUid();
$me->wp = Project\Workflow::load($me->prjUid);
return $me;
}
public function create($data)
{
try {
parent::create($data);
} catch (\Exception $e) {
throw new \RuntimeException(sprintf("Can't create Bpmn Project." . PHP_EOL . $e->getMessage()));
}
try {
$wpData = array();
$wpData["PRO_UID"] = $this->getUid();
if (array_key_exists("PRJ_NAME", $data)) {
$wpData["PRO_TITLE"] = $data["PRJ_NAME"];
}
if (array_key_exists("PRJ_DESCRIPTION", $data)) {
$wpData["PRO_DESCRIPTION"] = $data["PRJ_DESCRIPTION"];
}
if (array_key_exists("PRJ_AUTHOR", $data)) {
$wpData["PRO_CREATE_USER"] = $data["PRJ_AUTHOR"];
}
$this->wp = new Project\Workflow();
$this->wp->create($wpData);
} catch (\Exception $e) {
$prjUid = $this->getUid();
$this->remove();
throw new \RuntimeException(sprintf(
"Can't create Bpmn Project with prj_uid: %s, workflow creation fails." . PHP_EOL . $e->getMessage()
, $prjUid
));
}
}
public static function getList($start = null, $limit = null, $filter = "", $changeCaseTo = CASE_UPPER)
{
$bpmnProjects = parent::getList($start, $limit, $filter, $changeCaseTo);
$workflowProjects = Project\Workflow::getList($start, $limit, "", "");
$workflowProjectsUids = array();
foreach ($workflowProjects as $workflowProject) {
$workflowProjectsUids[] = $workflowProject["PRO_UID"];
}
$prjUidKey = $changeCaseTo == CASE_UPPER ? "PRJ_UID" : "prj_uid";
$list = array();
foreach ($bpmnProjects as $bpmnProject) {
if (in_array($bpmnProject[$prjUidKey], $workflowProjectsUids)) {
$list[] = $bpmnProject;
}
}
return $list;
}
public function addActivity($data)
{
$taskData = array();
$taskData["TAS_UID"] = parent::addActivity($data);
if (array_key_exists("ACT_NAME", $data)) {
$taskData["TAS_TITLE"] = $data["ACT_NAME"];
}
if (array_key_exists("ACT_NAME", $data)) {
$taskData["TAS_POSX"] = $data["BOU_X"];
}
if (array_key_exists("ACT_NAME", $data)) {
$taskData["TAS_POSY"] = $data["BOU_Y"];
}
$this->wp->addTask($taskData);
}
public function updateActivity($actUid, $data)
{
parent::updateActivity($actUid, $data);
$taskData = array();
if (array_key_exists("ACT_NAME", $data)) {
$taskData["TAS_TITLE"] = $data["ACT_NAME"];
}
if (array_key_exists("ACT_NAME", $data)) {
$taskData["TAS_POSX"] = $data["BOU_X"];
}
if (array_key_exists("ACT_NAME", $data)) {
$taskData["TAS_POSY"] = $data["BOU_Y"];
}
$this->wp->updateTask($actUid, $taskData);
}
public function removeActivity($actUid)
{
parent::removeActivity($actUid);
$this->wp->removeTask($actUid);
}
public function addFlow($data, $diagram)
{
$flows = $diagram["flows"];
$gateways = $diagram["gateways"];
$events = $diagram["events"];
parent::addFlow($data);
// to add a workflow route
// - activity -> activity ==> route
// - activity -> gateway -> activity ==> selection, evaluation, parallel or parallel by evaluation route
$routes = self::mapBpmnFlowsToWorkflowRoute($data, $flows, $gateways, $events);
if ($routes !== null) {
foreach ($routes as $routeData) {
$this->wp->addRoute($routeData["from"], $routeData["to"], $routeData["type"]);
}
return true;
}
// to add start event->activity as initial or end task
switch ($data["FLO_ELEMENT_ORIGIN_TYPE"]) {
case "bpmnEvent":
switch ($data["FLO_ELEMENT_DEST_TYPE"]) {
case "bpmnActivity":
$event = \BpmnEventPeer::retrieveByPK($data["FLO_ELEMENT_ORIGIN"]);
switch ($event && $event->getEvnType()) {
case "START":
// then set that activity/task as "Start Task"
$this->wp->setStartTask($data["FLO_ELEMENT_DEST"]);
break;
}
break;
}
break;
}
}
public function updateFlow($floUid, $data)
{
if (! self::isModified("flow", $floUid, $data)) {
self::log("Update Flow: $floUid (No Changes)");
return false;
}
parent::updateFlow($floUid, $data);
}
public function removeFlow($floUid)
{
$flow = \BpmnFlowPeer::retrieveByPK($floUid);
parent::removeFlow($floUid);
// verify case: event(start) -> activity
// => find the corresponding task and unset it as start task
if ($flow->getFloElementOriginType() == "bpmnEvent" &&
$flow->getFloElementDestType() == "bpmnActivity"
) {
$event = \BpmnEventPeer::retrieveByPK($flow->getFloElementOrigin());
if (! is_null($event) && $event->getEvnType() == "START") {
$activity = \BpmnActivityPeer::retrieveByPK($flow->getFloElementDest());
$this->wp->setStartTask($activity->getActUid(), false);
}
} elseif ($flow->getFloElementOriginType() == "bpmnActivity" &&
$flow->getFloElementDestType() == "bpmnEvent") {
// verify case: activity -> event(end)
// => find the corresponding task and unset it as start task
$event = \BpmnEventPeer::retrieveByPK($flow->getFloElementDest());
if (! is_null($event) && $event->getEvnType() == "END") {
$activity = \BpmnActivityPeer::retrieveByPK($flow->getFloElementOrigin());
if (! is_null($activity)) {
$this->wp->setEndTask($activity->getActUid(), false);
}
}
}
}
public function addEvent($data)
{
if (! array_key_exists("EVN_TYPE", $data)) {
throw new \RuntimeException("Required param \"EVN_TYPE\" is missing.");
}
parent::addEvent($data);
}
public function removeEvent($evnUid)
{
// $event = \BpmnEventPeer::retrieveByPK($evnUid);
//
// switch ($event->getEvnType()) {
// case "START":
// $flow = \BpmnFlow::findOneBy(\BpmnFlowPeer::FLO_ELEMENT_ORIGIN, $event->getEvnUid());
// if (! is_null($flow) && $flow->getFloElementDestType() == "bpmnActivity") {
// $activity = \BpmnActivityPeer::retrieveByPK($flow->getFloElementDest());
// if (! is_null($activity)) {
// $this->wp->setStartTask($activity->getActUid(), false);
// }
// }
// break;
// case "END":
// $flow = \BpmnFlow::findOneBy(\BpmnFlowPeer::FLO_ELEMENT_DEST, $event->getEvnUid());
// if (! is_null($flow) && $flow->getFloElementOriginType() == "bpmnActivity") {
// $activity = \BpmnActivityPeer::retrieveByPK($flow->getFloElementOrigin());
// if (! is_null($activity)) {
// $this->wp->setEndTask($activity->getActUid(), false);
// }
// }
// break;
// }
parent::removeEvent($evnUid);
}
public static function mapBpmnFlowsToWorkflowRoute($flow, $flows, $gateways, $events)
{
$fromUid = $flow['FLO_ELEMENT_ORIGIN'];
$result = array();
if ($flow['FLO_ELEMENT_ORIGIN_TYPE'] != "bpmnActivity") {
// skip flows that comes from a element that is not an Activity
self::log("Skip map FlowsToWorkflowRoute for -> flow with FLO_UID: {$flow['FLO_UID']}, that have FLO_ELEMENT_ORIGIN: {$flow['FLO_ELEMENT_ORIGIN_TYPE']}:$fromUid");
return null;
}
if ($flow['FLO_TYPE'] != 'SEQUENCE') {
throw new \LogicException(sprintf(
"Unsupported flow type: %s, ProcessMaker only support type '', Given: '%s'",
'SEQUENCE', $flow['FLO_TYPE']
));
}
switch ($flow['FLO_ELEMENT_DEST_TYPE']) {
case 'bpmnActivity':
// the most easy case, when the flow is connecting a activity with another activity
$result[] = array("from" => $fromUid, "to" => $flow['FLO_ELEMENT_DEST'], "type" => 'SEQUENTIAL');
break;
case 'bpmnGateway':
$gatUid = $flow['FLO_ELEMENT_DEST'];
2014-02-11 16:55:50 -04:00
// if it is a gateway it can fork one or more routes
$gatFlows = self::findInArray($gatUid, "FLO_ELEMENT_ORIGIN", $flows);
foreach ($gatFlows as $gatFlow) {
switch ($gatFlow['FLO_ELEMENT_DEST_TYPE']) {
case 'bpmnActivity':
// getting gateway properties
$gateways = self::findInArray($gatUid, "GAT_UID", $gateways);
if (! empty($gateways)) {
$gateway = $gateways[0];
$routeType = "";
switch ($gateway['GAT_TYPE']) {
case self::BPMN_GATEWAY_COMPLEX:
$routeType = 'SELECT';
break;
case self::BPMN_GATEWAY_EXCLUSIVE:
$routeType = 'EVALUATE';
break;
case self::BPMN_GATEWAY_INCLUSIVE:
switch ($gateway['GAT_DIRECTION']) {
case "DIVERGING":
$routeType = 'PARALLEL-BY-EVALUATION';
break;
case "CONVERGING":
$routeType = 'SEC-JOIN';
break;
default:
throw new \LogicException(sprintf("Unsupported Gateway direction: %s", $gateway['GAT_DIRECTION']));
}
break;
case self::BPMN_GATEWAY_PARALLEL:
switch ($gateway['GAT_DIRECTION']) {
case "DIVERGING":
$routeType = 'PARALLEL';
break;
case "CONVERGING":
$routeType = 'SEC-JOIN';
break;
default:
throw new \LogicException(sprintf("Unsupported Gateway direction: %s", $gateway['GAT_DIRECTION']));
}
break;
default:
throw new \LogicException(sprintf("Unsupported Gateway type: %s", $gateway['GAT_TYPE']));
}
$result[] = array("from" => $fromUid, "to" => $gatFlow['FLO_ELEMENT_DEST'], "type" => $routeType);
}
break;
default:
// for processmaker is only allowed flows between "gateway -> activity"
// any another flow is considered invalid
throw new \LogicException(sprintf(
"For ProcessMaker is only allowed flows between \"gateway -> activity\" " . PHP_EOL .
"Given: bpmnGateway -> " . $gatFlow['FLO_ELEMENT_DEST_TYPE']
));
}
}
break;
case 'bpmnEvent':
$evnUid = $flow['FLO_ELEMENT_DEST'];
$events = self::findInArray($evnUid, "EVN_UID", $events);
if (! empty($events)) {
$event = $events[0];
switch ($event['EVN_TYPE']) {
case 'END':
$routeType = 'SEQUENTIAL';
$result[] = array("from" => $fromUid, "to" => "-1", "type" => $routeType);
break;
default:
throw new \LogicException("Invalid connection to Event object type");
}
}
break;
}
return empty($result) ? null : $result;
}
protected static function findInArray($value, $key, $list)
{
$result = array();
foreach ($list as $item) {
if (array_key_exists($key, $item) && $item[$key] == $value) {
$result[] = $item;
}
}
return $result;
}
// public function getActivities()
// {
// return parent::getActivities();
// }
public function remove()
{
parent::remove();
$this->wp->remove();
}
}