2014-02-05 12:33:36 -04:00
< ? php
namespace ProcessMaker\Project\Adapter ;
use ProcessMaker\Project ;
use ProcessMaker\Util\Hash ;
2014-02-06 11:00:15 -04:00
/**
* Class BpmnWorkflow
2014-02-10 13:09:50 -04:00
*
2014-02-06 11:00:15 -04:00
* @ package ProcessMaker\Project\Adapter
2014-02-10 13:09:50 -04:00
* @ author Erik Amaru Ortiz < aortiz . erik @ gmail . com , erik @ colosa . com >
2014-02-06 11:00:15 -04:00
*/
2014-02-05 12:33:36 -04:00
class BpmnWorkflow extends Project\Bpmn
{
2014-02-06 16:21:46 -04:00
/**
* @ var \ProcessMaker\Project\Workflow
*/
protected $wp ;
2014-02-21 19:26:55 -04:00
const BPMN_GATEWAY_COMPLEX = " COMPLEX " ;
const BPMN_GATEWAY_PARALLEL = " PARALLEL " ;
const BPMN_GATEWAY_INCLUSIVE = " INCLUSIVE " ;
const BPMN_GATEWAY_EXCLUSIVE = " EXCLUSIVE " ;
2014-02-05 14:09:48 -04:00
/**
* OVERRIDES
*/
2014-02-05 12:33:36 -04:00
2014-02-06 16:21:46 -04:00
public static function load ( $prjUid )
{
2014-02-13 16:40:00 -04:00
2014-02-06 16:21:46 -04:00
$parent = parent :: load ( $prjUid );
2014-02-13 16:40:00 -04:00
//return new BpmnWorkflow();
2014-02-06 16:21:46 -04:00
2014-02-13 16:40:00 -04:00
$me = new BpmnWorkflow ();
2014-02-06 16:21:46 -04:00
$me -> project = $parent -> project ;
$me -> prjUid = $parent -> project -> getPrjUid ();
$me -> wp = Project\Workflow :: load ( $me -> prjUid );
return $me ;
}
2014-02-05 14:09:48 -04:00
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 " ];
}
2014-02-10 16:35:24 -04:00
$this -> wp = new Project\Workflow ();
$this -> wp -> create ( $wpData );
2014-02-05 14:09:48 -04:00
} 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 ;
}
2014-02-06 16:21:46 -04:00
public function addActivity ( $data )
{
$taskData = array ();
2014-02-10 16:35:24 -04:00
2014-02-24 19:10:45 -04:00
$actUid = parent :: addActivity ( $data );
$taskData [ " TAS_UID " ] = $actUid ;
2014-02-06 16:21:46 -04:00
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 );
2014-02-24 19:10:45 -04:00
return $actUid ;
2014-02-06 16:21:46 -04:00
}
2014-02-06 19:17:11 -04:00
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 );
}
2014-02-06 16:21:46 -04:00
public function removeActivity ( $actUid )
{
parent :: removeActivity ( $actUid );
$this -> wp -> removeTask ( $actUid );
}
2014-02-06 21:26:06 -04:00
2014-02-24 19:58:32 -04:00
public function removeGateway ( $gatUid )
{
2014-02-25 13:47:59 -04:00
// $gatewayData = $this->getGateway($gatUid);
// $flowsDest = \BpmnFlow::findAllBy(\BpmnFlowPeer::FLO_ELEMENT_DEST, $gatUid);
// foreach ($flowsDest as $flowDest) {
// switch ($flowDest->getFloElementOriginType()) {
// case "bpmnActivity":
// $actUid = $flowDest->getFloElementOrigin();
// $flowsOrigin = \BpmnFlow::findAllBy(\BpmnFlowPeer::FLO_ELEMENT_ORIGIN, $gatUid);
//
// foreach ($flowsOrigin as $flowOrigin) {
// switch ($flowOrigin->getFloElementDestType()) {
// case "bpmnActivity":
// $toActUid = $flowOrigin->getFloElementDest();
// $this->wp->removeRouteFromTo($actUid, $toActUid);
// break;
// }
// }
// break;
// }
// }
2014-02-24 19:58:32 -04:00
parent :: removeGateway ( $gatUid );
}
2014-02-24 19:10:45 -04:00
// public function addFlow($data)
// {
// parent::addFlow($data);
2014-02-06 21:26:06 -04:00
2014-02-10 19:43:54 -04:00
// to add a workflow route
// - activity -> activity ==> route
// - activity -> gateway -> activity ==> selection, evaluation, parallel or parallel by evaluation route
2014-02-24 19:10:45 -04:00
// $routes = self::mapBpmnFlowsToWorkflowRoute($data, $flows);
//
// 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;
// }
2014-02-06 21:26:06 -04:00
2014-02-24 19:10:45 -04:00
// }
2014-02-18 17:41:45 -04:00
2014-02-24 19:10:45 -04:00
// public function updateFlow($floUid, $data, $flows)
// {
// parent::updateFlow($floUid, $data);
// }
2014-02-18 17:41:45 -04:00
2014-02-12 17:01:19 -04:00
public function removeFlow ( $floUid )
{
2014-02-13 16:40:00 -04:00
$flow = \BpmnFlowPeer :: retrieveByPK ( $floUid );
2014-02-12 17:01:19 -04:00
parent :: removeFlow ( $floUid );
2014-02-13 16:40:00 -04:00
// 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 );
}
}
2014-02-24 19:10:45 -04:00
} else {
switch ( $flow -> getFloElementOriginType ()) {
case " bpmnActivity " :
switch ( $flow -> getFloElementDestType ()) {
// activity->activity
case " bpmnActivity " :
$this -> wp -> removeRouteFromTo ( $flow -> getFloElementOrigin (), $flow -> getFloElementDest ());
break ;
}
break ;
}
2014-02-13 16:40:00 -04:00
}
2014-02-24 19:10:45 -04:00
// TODO Complete for other routes, activity->activity, activity->gateway and viceversa
2014-02-12 17:01:19 -04:00
}
2014-02-07 08:41:11 -04:00
2014-02-10 19:43:54 -04:00
public function addEvent ( $data )
{
if ( ! array_key_exists ( " EVN_TYPE " , $data )) {
throw new \RuntimeException ( " Required param \" EVN_TYPE \" is missing. " );
}
parent :: addEvent ( $data );
}
2014-02-24 19:10:45 -04:00
public function mapBpmnFlowsToWorkflowRoutes ()
2014-02-10 19:43:54 -04:00
{
2014-02-24 19:10:45 -04:00
$activities = $this -> getActivities ();
foreach ( $activities as $activity ) {
2014-02-10 19:43:54 -04:00
2014-02-24 19:10:45 -04:00
$flows = \BpmnFlow :: findAllBy ( array (
\BpmnFlowPeer :: FLO_ELEMENT_ORIGIN => $activity [ " ACT_UID " ],
\BpmnFlowPeer :: FLO_ELEMENT_ORIGIN_TYPE => " bpmnActivity "
));
//
foreach ( $flows as $flow ) {
switch ( $flow -> getFloElementDestType ()) {
case " bpmnActivity " :
// (activity -> activity)
$this -> wp -> addRoute ( $activity [ " ACT_UID " ], $flow -> getFloElementDest (), " SEQUENTIAL " );
break ;
case " bpmnGateway " :
// (activity -> gateway)
// we must find the related flows: gateway -> <object>
$gatUid = $flow -> getFloElementDest ();
$gatewayFlows = \BpmnFlow :: findAllBy ( array (
\BpmnFlowPeer :: FLO_ELEMENT_ORIGIN => $gatUid ,
\BpmnFlowPeer :: FLO_ELEMENT_ORIGIN_TYPE => " bpmnGateway "
));
2014-02-25 13:47:59 -04:00
if ( $gatewayFlows > 0 ) {
$this -> wp -> resetTaskRoutes ( $activity [ " ACT_UID " ]);
}
2014-02-24 19:10:45 -04:00
foreach ( $gatewayFlows as $gatewayFlow ) {
$gatewayFlow = $gatewayFlow -> toArray ();
switch ( $gatewayFlow [ 'FLO_ELEMENT_DEST_TYPE' ]) {
case 'bpmnActivity' :
// (gateway -> activity)
$gateway = \BpmnGateway :: findOneBy ( \BpmnGatewayPeer :: GAT_UID , $gatUid ) -> toArray ();
switch ( $gateway [ " GAT_TYPE " ]) {
//case 'SELECTION':
case self :: BPMN_GATEWAY_COMPLEX :
$routeType = " SELECT " ;
break ;
//case 'EVALUATION':
case self :: BPMN_GATEWAY_EXCLUSIVE :
$routeType = " EVALUATE " ;
break ;
//case 'PARALLEL':
case self :: BPMN_GATEWAY_PARALLEL :
if ( $gateway [ " GAT_DIRECTION " ] == " DIVERGING " ) {
$routeType = " PARALLEL " ;
} elseif ( $gateway [ " GAT_DIRECTION " ] == " CONVERGING " ) {
$routeType = " SEC-JOIN " ;
} else {
throw new \LogicException ( sprintf (
" Invalid Gateway direction, accepted values: [%s|%s], given: %s. " ,
" DIVERGING " , " CONVERGING " , $gateway [ " GAT_DIRECTION " ]
));
}
break ;
//case 'PARALLEL_EVALUATION':
case self :: BPMN_GATEWAY_INCLUSIVE :
if ( $gateway [ " GAT_DIRECTION " ] == " DIVERGING " ) {
$routeType = " PARALLEL-BY-EVALUATION " ;
} elseif ( $gateway [ " GAT_DIRECTION " ] == " CONVERGING " ) {
$routeType = " SEC-JOIN " ;
} else {
throw new \LogicException ( sprintf (
" Invalid Gateway direction, accepted values: [%s|%s], given: %s. " ,
" DIVERGING " , " CONVERGING " , $gateway [ " GAT_DIRECTION " ]
));
}
break ;
// case 'PARALLEL_JOIN':
// $routeType = 'SEC-JOIN';
// break;
default :
throw new \LogicException ( sprintf ( " Unsupported Gateway type: %s " , $gateway [ 'GAT_TYPE' ]));
}
$this -> wp -> addRoute ( $activity [ " ACT_UID " ], $gatewayFlow [ 'FLO_ELEMENT_DEST' ], $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 -> " . $gatewayFlow [ 'FLO_ELEMENT_DEST_TYPE' ]
));
}
}
break ;
}
}
}
2014-02-06 21:26:06 -04:00
}
2014-02-07 08:41:11 -04:00
2014-02-24 19:10:45 -04:00
public static function mapBpmnFlowsToWorkflowRoute2 ( $flow , $flows , $gateways , $events )
2014-02-07 08:41:11 -04:00
{
$fromUid = $flow [ 'FLO_ELEMENT_ORIGIN' ];
2014-02-11 20:32:20 -04:00
$result = array ();
2014-02-07 08:41:11 -04:00
2014-02-07 15:42:24 -04:00
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 ;
}
2014-02-07 08:41:11 -04:00
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
2014-02-11 20:32:20 -04:00
$result [] = array ( " from " => $fromUid , " to " => $flow [ 'FLO_ELEMENT_DEST' ], " type " => 'SEQUENTIAL' );
2014-02-07 08:41:11 -04:00
break ;
case 'bpmnGateway' :
$gatUid = $flow [ 'FLO_ELEMENT_DEST' ];
2014-02-11 16:55:50 -04:00
2014-02-07 08:41:11 -04:00
// if it is a gateway it can fork one or more routes
2014-02-11 20:32:20 -04:00
$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 ];
2014-02-21 19:26:55 -04:00
$routeType = " " ;
2014-02-11 20:32:20 -04:00
switch ( $gateway [ 'GAT_TYPE' ]) {
2014-02-21 19:26:55 -04:00
case self :: BPMN_GATEWAY_COMPLEX :
2014-02-11 20:32:20 -04:00
$routeType = 'SELECT' ;
break ;
2014-02-21 19:26:55 -04:00
case self :: BPMN_GATEWAY_EXCLUSIVE :
2014-02-11 20:32:20 -04:00
$routeType = 'EVALUATE' ;
break ;
2014-02-21 19:26:55 -04:00
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' ]));
}
2014-02-11 20:32:20 -04:00
break ;
2014-02-21 19:26:55 -04:00
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' ]));
}
2014-02-11 20:32:20 -04:00
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' ]
));
}
2014-02-07 08:41:11 -04:00
}
break ;
case 'bpmnEvent' :
$evnUid = $flow [ 'FLO_ELEMENT_DEST' ];
2014-02-11 20:32:20 -04:00
$events = self :: findInArray ( $evnUid , " EVN_UID " , $events );
2014-02-07 08:41:11 -04:00
2014-02-13 16:40:00 -04:00
2014-02-11 20:32:20 -04:00
if ( ! empty ( $events )) {
$event = $events [ 0 ];
2014-02-07 08:41:11 -04:00
2014-02-11 20:32:20 -04:00
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 " );
}
}
2014-02-07 08:41:11 -04:00
break ;
}
2014-02-11 20:32:20 -04:00
return empty ( $result ) ? null : $result ;
2014-02-07 08:41:11 -04:00
}
2014-02-10 16:35:24 -04:00
public function remove ()
{
parent :: remove ();
$this -> wp -> remove ();
}
2014-02-13 16:40:00 -04:00
2014-02-05 12:33:36 -04:00
}