blob: c47c58c107c702c7d98d1c51ba1e406b1c20ba2f [file] [log] [blame]
<?php
/**
* File containing the ezcWorkflowDatabaseDefinitionStorage class.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* @package WorkflowDatabaseTiein
* @version //autogen//
* @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
*/
/**
* Workflow definition storage handler that saves and loads workflow
* definitions to and from a database.
*
* @package WorkflowDatabaseTiein
* @version //autogen//
*/
class ezcWorkflowDatabaseDefinitionStorage implements ezcWorkflowDefinitionStorage
{
/**
* ezcDbHandler instance to be used.
*
* @var ezcDbHandler
*/
protected $db;
/**
* Container to hold the properties
*
* @var array(string=>mixed)
*/
protected $properties = array(
'options' => null
);
/**
* Construct a new database definition handler.
*
* This constructor is a tie-in.
*
* @param ezcDbHandler $db
*/
public function __construct( ezcDbHandler $db )
{
$this->db = $db;
$this->properties['options'] = new ezcWorkflowDatabaseOptions;
}
/**
* Property get access.
*
* @param string $propertyName
* @return mixed
* @throws ezcBasePropertyNotFoundException
* If the given property could not be found.
* @ignore
*/
public function __get( $propertyName )
{
switch ( $propertyName )
{
case 'options':
return $this->properties[$propertyName];
}
throw new ezcBasePropertyNotFoundException( $propertyName );
}
/**
* Property set access.
*
* @param string $propertyName
* @param string $propertyValue
* @throws ezcBasePropertyNotFoundException
* If the given property could not be found.
* @throws ezcBaseValueException
* If the value for the property options is not an ezcWorkflowDatabaseOptions object.
* @ignore
*/
public function __set( $propertyName, $propertyValue )
{
switch ( $propertyName )
{
case 'options':
if ( !( $propertyValue instanceof ezcWorkflowDatabaseOptions ) )
{
throw new ezcBaseValueException(
$propertyName,
$propertyValue,
'ezcWorkflowDatabaseOptions'
);
}
break;
default:
throw new ezcBasePropertyNotFoundException( $propertyName );
}
$this->properties[$propertyName] = $propertyValue;
}
/**
* Property isset access.
*
* @param string $propertyName
* @return bool
* @ignore
*/
public function __isset( $propertyName )
{
switch ( $propertyName )
{
case 'options':
return true;
}
return false;
}
/**
* Load a workflow definition by ID.
*
* Providing the name of the workflow that is to be loaded as the
* optional second parameter saves a database query.
*
* @param int $workflowId
* @param string $workflowName
* @param int $workflowVersion
* @return ezcWorkflow
* @throws ezcWorkflowDefinitionStorageException
* @throws ezcDbException
*/
public function loadById( $workflowId, $workflowName = '', $workflowVersion = 0 )
{
// Query the database for the name and version of the workflow.
if ( empty( $workflowName ) || $workflowVersion == 0 )
{
$query = $this->db->createSelectQuery();
$query->select( $this->db->quoteIdentifier( 'workflow_name' ) )
->select( $this->db->quoteIdentifier( 'workflow_version' ) )
->from( $this->db->quoteIdentifier( $this->options['prefix'] . 'workflow' ) )
->where( $query->expr->eq( $this->db->quoteIdentifier( 'workflow_id' ),
$query->bindValue( (int)$workflowId ) ) );
$stmt = $query->prepare();
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
if ( $result !== false && isset( $result[0] ) )
{
$workflowName = $result[0]['workflow_name'];
$workflowVersion = $result[0]['workflow_version'];
}
else
{
throw new ezcWorkflowDefinitionStorageException(
'Could not load workflow definition.'
);
}
}
// Query the database for the nodes of the workflow to be loaded.
$query = $this->db->createSelectQuery();
$query->select( $this->db->quoteIdentifier( 'node_id' ) )
->select( $this->db->quoteIdentifier( 'node_class' ) )
->select( $this->db->quoteIdentifier( 'node_configuration' ) )
->from( $this->db->quoteIdentifier( $this->options['prefix'] . 'node' ) )
->where( $query->expr->eq( $this->db->quoteIdentifier( 'workflow_id' ),
$query->bindValue( (int)$workflowId ) ) );
$stmt = $query->prepare();
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
$nodes = array();
// Create node objects.
foreach ( $result as $node )
{
$configuration = ezcWorkflowDatabaseUtil::unserialize(
$node['node_configuration'], null
);
if ( is_null( $configuration ) )
{
$configuration = ezcWorkflowUtil::getDefaultConfiguration( $node['node_class'] );
}
$nodes[$node['node_id']] = new $node['node_class'](
$configuration
);
if ($nodes[$node['node_id']] instanceof ezcWorkflowNodeFinally &&
!isset( $finallyNode ) )
{
$finallyNode = $nodes[$node['node_id']];
}
else if ($nodes[$node['node_id']] instanceof ezcWorkflowNodeEnd &&
!isset( $defaultEndNode ) )
{
$defaultEndNode = $nodes[$node['node_id']];
}
else if ($nodes[$node['node_id']] instanceof ezcWorkflowNodeStart &&
!isset( $startNode ) )
{
$startNode = $nodes[$node['node_id']];
}
}
if ( !isset( $startNode ) || !isset( $defaultEndNode ) )
{
throw new ezcWorkflowDefinitionStorageException(
'Could not load workflow definition.'
);
}
// Connect node objects.
$query = $this->db->createSelectQuery();
$query->select( $query->alias( 'node_connection.incoming_node_id',
$this->db->quoteIdentifier( 'incoming_node_id' ) ) )
->select( $query->alias( 'node_connection.outgoing_node_id',
$this->db->quoteIdentifier( 'outgoing_node_id' ) ) )
->from( $query->innerJoin( $this->db->quoteIdentifier( $this->options['prefix'] . 'node_connection' ),
$this->db->quoteIdentifier( $this->options['prefix'] . 'node' ),
'node_connection.incoming_node_id',
'node.node_id' ) )
->where( $query->expr->eq( 'node.workflow_id',
$query->bindValue( (int)$workflowId ) ) )
->orderBy( $this->db->quoteIdentifier( 'node_connection_id' ) );
$stmt = $query->prepare();
$stmt->execute();
$connections = $stmt->fetchAll( PDO::FETCH_ASSOC );
foreach ( $connections as $connection )
{
$nodes[$connection['incoming_node_id']]->addOutNode(
$nodes[$connection['outgoing_node_id']]
);
}
if ( !isset( $finallyNode ) ||
count( $finallyNode->getInNodes() ) > 0 )
{
$finallyNode = null;
}
// Create workflow object and add the node objects to it.
$workflow = new ezcWorkflow( $workflowName, $startNode, $defaultEndNode, $finallyNode );
$workflow->definitionStorage = $this;
$workflow->id = (int)$workflowId;
$workflow->version = (int)$workflowVersion;
// Query the database for the variable handlers.
$query = $this->db->createSelectQuery();
$query->select( $this->db->quoteIdentifier( 'variable' ) )
->select( $this->db->quoteIdentifier( 'class' ) )
->from( $this->db->quoteIdentifier( $this->options['prefix'] . 'variable_handler' ) )
->where( $query->expr->eq( $this->db->quoteIdentifier( 'workflow_id' ),
$query->bindValue( (int)$workflowId ) ) );
$stmt = $query->prepare();
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
$nodes = array();
if ( $result !== false )
{
foreach ( $result as $variableHandler )
{
$workflow->addVariableHandler(
$variableHandler['variable'],
$variableHandler['class']
);
}
}
// Verify the loaded workflow.
$workflow->verify();
return $workflow;
}
/**
* Load a workflow definition by name.
*
* @param string $workflowName
* @param int $workflowVersion
* @return ezcWorkflow
* @throws ezcWorkflowDefinitionStorageException
* @throws ezcDbException
*/
public function loadByName( $workflowName, $workflowVersion = 0 )
{
// Query the database for the workflow ID.
$query = $this->db->createSelectQuery();
// Load the current version of the workflow.
if ( $workflowVersion == 0 )
{
$workflowVersion = $this->getCurrentVersionNumber( $workflowName );
}
// Query for the workflow_id.
$query->select( $this->db->quoteIdentifier( 'workflow_id' ) )
->from( $this->db->quoteIdentifier( $this->options['prefix'] . 'workflow' ) )
->where( $query->expr->eq( $this->db->quoteIdentifier( 'workflow_name' ),
$query->bindValue( $workflowName ) ) )
->where( $query->expr->eq( $this->db->quoteIdentifier( 'workflow_version' ),
$query->bindValue( (int)$workflowVersion ) ) );
$stmt = $query->prepare();
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
if ( $result !== false && isset( $result[0] ) )
{
return $this->loadById(
$result[0]['workflow_id'],
$workflowName,
$workflowVersion
);
}
else
{
throw new ezcWorkflowDefinitionStorageException(
'Could not load workflow definition.'
);
}
}
/**
* Save a workflow definition to the database.
*
* @param ezcWorkflow $workflow
* @throws ezcWorkflowDefinitionStorageException
* @throws ezcDbException
*/
public function save( ezcWorkflow $workflow )
{
// Verify the workflow.
$workflow->verify();
$this->db->beginTransaction();
// Calculate new version number.
$workflowVersion = $this->getCurrentVersionNumber( $workflow->name ) + 1;
// Write workflow table row.
$query = $this->db->createInsertQuery();
$query->insertInto( $this->db->quoteIdentifier( $this->options['prefix'] . 'workflow' ) )
->set( $this->db->quoteIdentifier( 'workflow_name' ), $query->bindValue( $workflow->name ) )
->set( $this->db->quoteIdentifier( 'workflow_version' ), $query->bindValue( (int)$workflowVersion ) )
->set( $this->db->quoteIdentifier( 'workflow_created' ), $query->bindValue( time() ) );
$statement = $query->prepare();
$statement->execute();
$workflow->definitionStorage = $this;
$workflow->id = (int)$this->db->lastInsertId( 'workflow_workflow_id_seq' );
$workflow->version = (int)$workflowVersion;
// Write node table rows.
$nodeMap = array();
foreach ( $workflow->nodes as $node )
{
$query = $this->db->createInsertQuery();
$query->insertInto( $this->db->quoteIdentifier( $this->options['prefix'] . 'node' ) )
->set( $this->db->quoteIdentifier( 'workflow_id' ), $query->bindValue( (int)$workflow->id ) )
->set( $this->db->quoteIdentifier( 'node_class' ), $query->bindValue( get_class( $node ) ) )
->set( $this->db->quoteIdentifier( 'node_configuration' ), $query->bindValue(
ezcWorkflowDatabaseUtil::serialize( $node->getConfiguration() ) )
);
$statement = $query->prepare();
$statement->execute();
$nodeMap[$this->db->lastInsertId( $this->db->quoteIdentifier( 'node_node_id_seq' ) )] = $node;
}
// Connect node table rows.
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;
}
}
$query = $this->db->createInsertQuery();
$query->insertInto( $this->db->quoteIdentifier( $this->options['prefix'] . 'node_connection' ) )
->set( $this->db->quoteIdentifier( 'incoming_node_id' ), $query->bindValue( $incomingNodeId ) )
->set( $this->db->quoteIdentifier( 'outgoing_node_id' ), $query->bindValue( $outgoingNodeId ) );
$statement = $query->prepare();
$statement->execute();
}
}
unset( $nodeMap );
// Write variable handler rows.
foreach ( $workflow->getVariableHandlers() as $variable => $class )
{
$query = $this->db->createInsertQuery();
$query->insertInto( $this->db->quoteIdentifier( $this->options['prefix'] . 'variable_handler' ) )
->set( $this->db->quoteIdentifier( 'workflow_id' ), $query->bindValue( (int)$workflow->id ) )
->set( $this->db->quoteIdentifier( 'variable' ), $query->bindValue( $variable ) )
->set( $this->db->quoteIdentifier( 'class' ), $query->bindValue( $class ) );
$statement = $query->prepare();
$statement->execute();
}
$this->db->commit();
}
/**
* Returns the current version number for a given workflow name.
*
* @param string $workflowName
* @return int
* @throws ezcDbException
*/
protected function getCurrentVersionNumber( $workflowName )
{
$query = $this->db->createSelectQuery();
$query->select( $query->alias( $query->expr->max( $this->db->quoteIdentifier( 'workflow_version' ) ),
'version' ) )
->from( $this->db->quoteIdentifier( $this->options['prefix'] . 'workflow' ) )
->where( $query->expr->eq( $this->db->quoteIdentifier( 'workflow_name' ),
$query->bindValue( $workflowName ) ) );
$stmt = $query->prepare();
$stmt->execute();
$result = $stmt->fetchAll( PDO::FETCH_ASSOC );
if ( $result !== false && isset( $result[0]['version'] ) && $result[0]['version'] !== null )
{
return $result[0]['version'];
}
else
{
return 0;
}
}
}
?>