/** * This method associates a flowing item with certain workflow and creates * an active 'flow' for it, unless there's already another active 'flow' * for such combination. * * @param \TooBasic\Workflows\Item $item Flowing item to be injected. * @param string $workflowName Name of the workflow to associate it with. * @param string $error Returns an error message when something went * wrong. * @return boolean Returns TRUE if the items was injectected successfully. */ public function inject(Item $item, $workflowName, &$error = false) { // // Default values. $out = true; // // Logging operation. $this->_log->log(LGGR_LOG_LEVEL_INFO, "Injecting into workflow '{$workflowName}'. Item: " . json_encode($item->toArray())); // // Loading and checking workflow. $workflow = $this->getWorkflow($workflowName); if ($workflow) { // // Basic query prefixes. $prefixes = array(GC_DBQUERY_PREFIX_TABLE => $this->_dbprefix, GC_DBQUERY_PREFIX_COLUMN => 'ifl_'); // // Creating a query to search for active flows. $query = $this->_db->queryAdapter()->select('wkfl_item_flows', array('workflow' => $workflowName, 'type' => $item->type(), 'item' => $item->id(), 'status' => 'tmp'), $prefixes); $stmt = $this->_db->prepare($query[GC_AFIELD_QUERY]); // // Checking active flows. $injected = false; foreach (array(WKFL_ITEM_FLOW_STATUS_OK, WKFL_ITEM_FLOW_STATUS_WAIT) as $status) { $query[GC_AFIELD_PARAMS]['status'] = $status; $stmt->execute($query[GC_AFIELD_PARAMS]); if ($stmt->rowCount() > 0) { $injected = true; break; } } // // Checking if the new flow can be injected. if ($injected) { $error = 'Item already injected and running'; $out = false; } else { // // Creating a query to inject a flow. $query = $this->_db->queryAdapter()->insert('wkfl_item_flows', array('workflow' => $workflowName, 'type' => $item->type(), 'item' => $item->id()), $prefixes); $stmt = $this->_db->prepare($query[GC_AFIELD_QUERY]); $out = boolval($stmt->execute($query[GC_AFIELD_PARAMS])); } } else { $out = false; $error = "Unknown workflow '{$workflowName}'"; } // // Logging errors if any. if (!$out) { $this->_log->log(LGGR_LOG_LEVEL_ERROR, $error); } return $out; }
/** * This method is the one in charge of executing a step of a specific * item, analyze results and set the next step or errors. * * @param \TooBasic\Workflows\ItemFlowRepresentation $flow Item flow to be * executed. * @param \TooBasic\Workflows\Item $item Item to be analyzed * @return boolean Returns TRUE the current step pointed the execution of * a next step and the flow is not in a stopping status. * @throws \TooBasic\Workflows\WorkflowsException */ public function runFor(ItemFlowRepresentation $flow, Item $item) { // // Default values. $continue = true; $logParams = array('workflow' => $flow->workflow); // // Loading all required settings. $this->load(); // // Global dependencies. global $WKFLDefaults; // // Guessing the current step name. $currentStep = $flow->step; if (!$currentStep) { // // Getting the fist step from workflow when there's none. $currentStep = $this->_config->startsAt; $flow->step = $currentStep; $flow->persist(); } $this->_log->log(LGGR_LOG_LEVEL_INFO, "Current step: '{$currentStep}'", $logParams); // // Shortcuts. $stepConfig = $this->_config->steps->{$currentStep}; $stepPath = Paths::Instance()->customPaths($WKFLDefaults[WKFL_DEFAULTS_PATHS][WKFL_DEFAULTS_PATH_STEPS], Names::SnakeFilename($stepConfig->manager), Paths::ExtensionPHP); $stepClass = Names::ClassNameWithSuffix($stepConfig->manager, WKFL_CLASS_SUFFIX_STEP); $this->_log->log(LGGR_LOG_LEVEL_DEBUG, " path: '{$stepPath}'", $logParams); $this->_log->log(LGGR_LOG_LEVEL_DEBUG, " class: '{$stepClass}'", $logParams); $this->_log->log(LGGR_LOG_LEVEL_DEBUG, ' config: ' . json_encode($stepConfig), $logParams); // // Checking if the step's class is already loaded. if (!class_exists($stepClass)) { // // Checking class code file. if (!$stepPath || !is_readable($stepPath)) { $msg = "Unable to access class for step '{$currentStep}'."; $this->_log->log(LGGR_LOG_LEVEL_FATAL, $msg, $logParams); throw new WorkflowsException($msg); } require_once $stepPath; } // // Checking class existence. if (!class_exists($stepClass)) { $msg = "Class '{$stepClass}' not defined."; $this->_log->log(LGGR_LOG_LEVEL_FATAL, $msg, $logParams); throw new WorkflowsException($msg); } // // Getting a step execution class. $step = new $stepClass($item); // // Sharing log pointer. $step->setLog($this->_log); // // Step execution $step->execute(); // // Reloading item to avoid outdated data. $item->reload(); // // Checking that thers a connection configuration for the current // item status. $connection = false; if (isset($stepConfig->connections->{$item->status()})) { $connection = $stepConfig->connections->{$item->status()}; } else { $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Unkwnon connection for item status '{$item->status()}'.", $logParams); $continue = false; $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED; $flow->persist(); } // // Checking if there's a connection configuration to analyze. if ($connection) { // // Checking what action should be taken. if (isset($connection->step) || isset($connection->status)) { // // Setting the next step to take. if (isset($connection->step)) { $flow->step = $connection->step; } // // Setting new flows status. if (isset($connection->status)) { $flow->status = $connection->status; $continue = false; } } else { $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Neither step not status configuration for connection on status '{$item->status()}'.", $logParams); $continue = false; $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED; } // // If the flow has been set to wait, it should check if // the waiting has conditions. if ($flow->status == WKFL_ITEM_FLOW_STATUS_WAIT && isset($connection->wait)) { // // Checking configuration. if (!isset($connection->wait->attempts) || !isset($connection->wait->status)) { $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Waiting configuration is incorrect.", $logParams); $continue = false; $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED; } else { // // Incrising the attempts count. $flow->attempts++; // // Checking it already tried to many // times. if ($flow->attempts > $connection->wait->attempts) { // // Changing flow status and // resetting counts. $flow->status = $connection->wait->status; $flow->attempts = 0; $this->_log->log(LGGR_LOG_LEVEL_INFO, "Maximum attempts reached ({$connection->wait->attempts} attempts), changing flow status to '{$connection->wait->status}'.", $logParams); } else { $this->_log->log(LGGR_LOG_LEVEL_INFO, "Increasing attempts to {$flow->attempts}.", $logParams); } } } else { // // The rest of the status keep the attempts to // zero. $flow->attempts = 0; } // // Saving flow status. $flow->persist(); $this->_log->log(LGGR_LOG_LEVEL_INFO, "Next step: '{$flow->step}'. Next flow status: '{$flow->status}'.", $logParams); } return $continue; }