Skip to main content

A more complex action

In the (previous example)[actions.md], we discussed how to build a simple mapped action to submit data to a third party system, ReceiptingSystem. The endpoint for this expected three fixed pieces of information - fields A, B & C.

Consider if ReceiptingSystem had a more complex endpoint. It may take in a requestType field, and other fields required may differ depending on how this is set.

Our simple mapped action is no longer appropriate for this, because the administrator needs to map different fields based on how requestType is set. In such cases, we need to build a progress mapped action.

Example: Sending simple data

As the fields we need to map may change, we need a Progress Mapped action. To use this style of template, we need to create a class at CustomXFormsProBundle/Rules/Actions/ReceiptingSystemActionType.php which extends ProgressMappedActionType.

namespace Jadu\Custom\CustomXFormsProBundle\Rules\Actions;

use Jadu\XFormsPro\Form\Rules\Actions\ProgressMappedActionType;

class ReceiptingSystemActionType extends ProgressMappedActionType
{
}

Next, we define some of the abstract's functions:

    /**
* Should return an array of step labels to display in the UI
* E.g. ['Case Type', 'Mappings'].
*
* @return string[]
*/
public function getSteps()
{
return [
'Request Type',
'Mappings'
];
}

/**
* Should return an 2d array, of similar structure to this:
* [
* 'current' => [
* {{ fieldName }} => [
* 'options' => [],
* 'label' => {{ label }},
* 'help' => {{ help text }},
* 'required' => {{ is it required? }}
* ]
* ],
* 'hidden' => []
* ].
*
* The current array should contain all the fields to at the current stage, indexed by field
* name
* The hidden array should contains fields from previous steps, indexed by field name
*
* @param int $step The current step in the progress list
* @param array $data data populated at previous steps, indexed by field name, if available
*
* @return array
*/
public function getFieldsForStep($step, $data)
{
if ($step == 2) {
return [
'current' => $this->getMappingFields(isset($data['requestType']) ? $data['requestType'] : ''),
'hidden' => $this->getRequestTypeFields()
];
} else {
return [
'current' => $this->getRequestTypeFields(),
'hidden' => []
];
}
}

/**
* Should return an array, indexed by field name, of values by the user so far, e.g. in previous steps.
*
* @param Request $request The request object
* @param int $step The current step in the progress list
*
* @return array
*/
public function populateData($request, $step)
{
$output = [
'requestType' => trim($request->request->get('requestType', '')),
];

return $output;
}

/**
* Should return an array of errors, indexed by field name, for the current step in the list.
*
* @param int $step The current step in the progress list
* @param array $data data populated at previous steps, indexed by field name, if available
*
* @return string[]
*/
public function validateStep($step, $data)
{
if ($step == 2) {
$errors = [];
$mappingFields = $this->getMappingFields($data['requestType']);

foreach ($mappingFields as $name => $field) {
if ($field['required'] && empty($data[$name])) {
$errors[$name] = 'This is a required field';
}
}

return $errors;
} else {
$errors = [];

if (empty($data['requestType']) || $data['requestType'] == -1) {
$errors['requestType'] = 'This is a required field';
}

return $errors;
}
}

getSteps defines the labels in each step of the wizard.

getFieldsForStep uses $step to work out which fields to display. It returns current fields (ones to display on this step), and hidden fields (ones from previous steps, that become hidden inputs).

populateData builds an array of data we need for each step - in this case, this is the requestType field from step 1. This method ensures that step 2 can still access this data.

validateStep is responsible for ensuring each field in the current step is completed satisfactorily. In step 1, we must provide a Request Type, whilst all required mappings in step 2 must be set.

We need to define a couple of methods that the above functions call:

    /**
* @var array
*/
protected $mappingFields;

/**
* Get fields to map in step 2
* @param string $requestType
* @return array
*/
public function getMappingFields($requestType)
{
if (!is_array($this->mappingFields)) {
$output = [
'fieldA' => [
'options' => self::MAPPING_TYPE_CORE,
'label' => 'Field A',
'help' => 'Data to send in Field A',
'required' => true,
],
'fieldB' => [
'options' => self::MAPPING_TYPE_CORE,
'label' => 'Field B',
'help' => 'Data to send in Field B',
'required' => false,
],
'fieldC' => [
'options' => self::MAPPING_TYPE_CORE,
'label' => 'Field C',
'help' => 'Data to send in Field C',
'required' => false,
],
];

if ($requestType == 'cheque') {
$output['chequeNumber'] = [
'options' => self::MAPPING_TYPE_CORE,
'label' => 'Cheque number',
'help' => 'The number of the cheque to receipt',
'required' => true,
];
}

$this->mappingFields = $output;
}

return $this->mappingFields;
}

/**
* Define the fields at the Request Type step
*
* @return array
*/
protected function getRequestTypeFields()
{
return [
'requestType' => [
'options' => [
'cash' => 'Cash',
'cheque' => 'Cheque'
],
'label' => 'Request Type',
'help' => 'Choose a Request Type to send',
'required' => true,
]
];
}

Individual field definitions are identical to (the previous example)[actions.md].

The execute() method

The final method to declare in our class is execute, which will pass data to ReceiptingSystem. This can be written in a very similar way to (the previous example)[actions.md]. The only difference is what is passed to the method in the $mappings array.

Firstly, we'll have a requestType key, which will have a value of cash or cheque. The array will also contain keys fieldA, fieldB and fieldC with associated mapping values. If requestType was set to cheque, we will also have a chequeNumber key, again with associated mapping value.

Registering the action

We then need to register the action with Jadu Central. To do this, add the following at CustomXFormsProBundle/Resources/config/services.yml:

services:
custom_xfp.rules.action.receipting_system:
class: Jadu\Custom\CustomXFormsProBundle\Rules\Actions\ReceiptingSystemActionType
arguments:
- "@doctrine.orm.entity_manager"
tags:
- { name: xfp.rule_action, system: 'ReceiptingSystem', label: 'Submit Data' }
note

The services: line is only needed once, at the beginning of the services.yml file.

Actions are grouped by system. This means it is possible to create multiple different actions for the same system, by specifying different values in label.

The use of the tag is what tells Jadu Central that this class can be used as an action. Clear the Symfony cache of your development environment.