/**
* Save a workflow definition to the database.
*
* @param ezcWorkflow $workflow
* @throws ezcWorkflowDefinitionStorageException
*/
public function save(\ezcWorkflow $workflow)
{
// Verify the workflow.
$workflow->verify();
if (strlen($workflow->name) == 0) {
throw new \ezcWorkflowDefinitionStorageException();
}
$platform = $this->conn->getDatabasePlatform();
// what mode of saving should it be? Update or Re-Generate?
//
// Conditions that an update sufficies:
// 1. No node has been deleted
// 2. No node has changed its meaning (action class or type)
// 3. For simplicitly only zero or one new nodes will be created.
$hasExistingNodeIds = array();
$newNodes = 0;
foreach ($workflow->nodes as $node) {
$oid = spl_object_hash($node);
if (!isset($this->nodeMap[$oid])) {
$newNodes++;
} else {
$hasExistingNodeIds[] = $this->nodeMap[$oid];
}
}
$canBeUpdate = false;
if ($newNodes < 2 && count(array_diff($hasExistingNodeIds, $this->workflowNodeIds[$workflow->id])) == 0 && $workflow->id) {
$canBeUpdate = true;
}
$this->workflowNodeIds[$workflow->id] = array();
try {
$this->conn->beginTransaction();
$workflowVersion = $this->getCurrentVersion($workflow->name) + 1;
$this->conn->update($this->options->workflowTable(), array('workflow_outdated' => 1), array('workflow_name' => $workflow->name));
$date = new \DateTime("now");
if ($canBeUpdate) {
$this->conn->update($this->options->workflowTable(), array('workflow_version' => $workflowVersion, 'workflow_created' => $date->format($platform->getDateTimeFormatString())), array('workflow_id' => $workflow->id));
} else {
$data = array('workflow_name' => $workflow->name, 'workflow_version' => $workflowVersion, 'workflow_created' => $date->format($platform->getDateTimeFormatString()), 'workflow_outdated' => 0);
// For sequences: get id before insert
if ($platform->prefersSequences()) {
$id = (int) $this->conn->fetchColumn($platform->getSequenceNextValSQL($this->options->workflowSequence()));
$data['workflow_id'] = $id;
$workflow->id = $id;
}
$this->conn->insert($this->options->workflowTable(), $data);
if ($platform->prefersIdentityColumns()) {
$workflow->id = (int) $this->conn->lastInsertId();
}
$workflow->definitionStorage = $this;
}
// Write node table rows.
$nodeMap = array();
foreach ($workflow->nodes as $node) {
/* @var $node \ezcWorkflowNode */
$oid = spl_object_hash($node);
if ($canBeUpdate && isset($this->nodeMap[$oid])) {
$nodeId = (int) $this->nodeMap[$oid];
$this->conn->update($this->options->nodeTable(), array('node_configuration' => $this->options->getSerializer()->serialize($node->getConfiguration())), array('node_id' => $nodeId));
} else {
$data = array('workflow_id' => (int) $workflow->id, 'node_class' => get_class($node), 'node_configuration' => $this->options->getSerializer()->serialize($node->getConfiguration()));
if ($platform->prefersSequences()) {
$nodeId = (int) $this->conn->fetchColumn($platform->getSequenceNextValSQL($this->options->nodeSequence()));
$data['node_id'] = $nodeId;
}
$this->conn->insert($this->options->nodeTable(), $data);
if ($platform->prefersIdentityColumns()) {
$nodeId = (int) $this->conn->lastInsertId();
}
}
$nodeMap[$nodeId] = $node;
$this->workflowNodeIds[$workflow->id][] = $nodeId;
$this->nodeMap[$oid] = $nodeId;
}
if ($canBeUpdate) {
// Delete all the node connections, NodeMap Keys are casted to (int) so usage here is safe.
$query = "DELETE FROM " . $this->options->nodeConnectionTable() . " " . "WHERE incoming_node_id IN (" . implode(",", array_keys($nodeMap)) . ") OR " . "outgoing_node_id IN (" . implode(",", array_keys($nodeMap)) . ")";
$this->conn->executeUpdate($query);
}
foreach ($workflow->nodes as $node) {
foreach ($node->getOutNodes() as $outNode) {
$incomingNodeId = null;
$outgoingNodeId = null;
foreach ($nodeMap as $_id => $_node) {
if ($_node === $node) {
$incomingNodeId = $_id;
} else {
if ($_node === $outNode) {
$outgoingNodeId = $_id;
}
}
if ($incomingNodeId !== NULL && $outgoingNodeId !== NULL) {
break;
}
}
$data = array('incoming_node_id' => $incomingNodeId, 'outgoing_node_id' => $outgoingNodeId);
if ($platform->prefersSequences()) {
$id = (int) $this->conn->fetchColumn($platform->getSequenceNextValSQL($this->options->nodeConnectionSequence()));
$data['id'] = $id;
}
$this->conn->insert($this->options->nodeConnectionTable(), $data);
}
}
unset($nodeMap);
if ($canBeUpdate) {
$this->conn->delete($this->options->variableHandlerTable(), array('workflow_id' => (int) $workflow->id));
}
foreach ($workflow->getVariableHandlers() as $variable => $class) {
$this->conn->insert($this->options->variableHandlerTable(), array('workflow_id' => (int) $workflow->id, 'variable' => $variable, 'class' => $class));
}
$this->conn->commit();
} catch (\Exception $e) {
$this->conn->rollBack();
throw new \ezcWorkflowDefinitionStorageException("Error while persisting workflow: " . $e->getMessage());
}
}