howto_create_an_action.md 9.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
# Howto create a update participant status action

In this tutorial I will explain how you could develop an action which does update an existing participant record.
It is also possible to create an action in your own extensions but that would be dealt with in another tutorial.

## Contents

* [Required functionality](#required-functionality)
* [Create an action class](#create-an-action-class)
  * [Create the class](#create-the-class)
  * [Set a title](#set-a-title)

## Required functionality

The action should update an existing participant record. 
Based on the provided event_id and contact_id the participant record is updated to the status configured by the site administrator.

In _action-provider_ terminology this means we need _status_ as a _configuration option_ and _event_id_ and _contact_id_ as _parameter options_.

The action would not return anything.  

## Create an action class

Start with a file in the directory _Civi\ActionProvider\Actions\Event\UpdateParticipantStatus.php_
This file will contain the action class which is extended from the abstract action.

### Creating the class

```php

  <?php
  
  namespace Civi\ActionProvider\Action\Event;
  
  use \Civi\ActionProvider\Action\AbstractAction;
  use \Civi\ActionProvider\Parameter\ParameterBagInterface;
  use \Civi\ActionProvider\Parameter\SpecificationBag;
  use \Civi\ActionProvider\Parameter\Specification;
  
  use CRM_ActionProvider_ExtensionUtil as E;
  
  class UpdateParticipantStatus extends AbstractAction {
  }

```

Above code will create the class, every action has to extend the _AbstractAction_ class. As you can see we use namespace and use statements. 
The use statement are required for the next step and are a kind of _include_ or _require_ statement. 

### Set a title

In this step we make sure our action returns a human readable title.

```php

  class UpdateParticipantStatus extends AbstractAction {
    
    /**
     * Returns the human readable title of this action
     */
    public function getTitle() {
      return E::ts('Update participant status'); 
    }
    
  } 

```

### Specify the configuration options

In this step we define the configuration option of the action. A configuration option is something which should be set by the
site administrator.

We do this by returning a _specification bag_ in which all configuration fields are specified. 

In our case we have only one configuration option and that is the new status.

```php

  class UpdateParticipantStatus extends AbstractAction {
    
    //...
    
    /**
     * Returns the specification of the configuration options for the actual action.
     * 
     * @return SpecificationBag
     */
    public function getConfigurationSpecification() { 
      return new SpecificationBag(array(
        /**
         * The parameters given to the Specification object are:
         * @param string $name
         * @param string $dataType
         * @param string $title
         * @param bool $required
         * @param mixed $defaultValue
         * @param string|null $fkEntity
         * @param array $options
         * @param bool $multiple
         */
        new Specification('status', 'Integer', E::ts('Status'), true, null, 'ParticipantStatusType', null, FALSE),
      ));
    }
    
  } 

``` 

In the code above we define a field which has the name _status_ an Integer Type, the human title is a translated string, it is a reuiqred option, no default value, and the list of options are retrieved from the _ParticipantStatus_ entity.

### Specify the parameter options

In this step we specify which parameters the action has. This is similair to the configuration specification. 
The difference between a parameter and a configuration is that a configuration is set by a site administrator 
whilst a parameter is coming from an other action, an input etc. Depending on the situation in which the action is used
e.g. when used in the [form-processor](https://lab.civicrm.org/extensions/form-processor) a parameter could be mapped to an input of a
form processor or to an output of previous action.

In our specific example we have the event_id and the contact_id as parameters.

```php

  class UpdateParticipantStatus extends AbstractAction {
    
    //...
    
    /**
     * Returns the specification of the configuration options for the actual action.
     * 
     * @return SpecificationBag
     */
    public function getParameterSpecification() { 
      return new SpecificationBag(array(
        /**
         * The parameters given to the Specification object are:
         * @param string $name
         * @param string $dataType
         * @param string $title
         * @param bool $required
         * @param mixed $defaultValue
         * @param string|null $fkEntity
         * @param array $options
         * @param bool $multiple
         */
        new Specification('event_id', 'Integer', E::ts('Event ID'), true, null, null, null, FALSE),
        new Specification('contact_id', 'Integer', E::ts('Contact ID'), true, null, null, null, FALSE),
      ));
    }
    
  } 

``` 

### Specify the output of the action

In this step we specify which fields an action outputs.
This is similair to the specification of configuration and parameters. 
In this particilair example we dont output anything.

```php

  class UpdateParticipantStatus extends AbstractAction {
    
    //...

    /**
     * Returns the specification of the output parameters of this action.
     * 
     * This function could be overriden by child classes.
     * 
     * @return SpecificationBag
     */
    public function getOutputSpecification() {
      return new SpecificationBag();
    }
      
  }

```

### Develop the actual action

In this step the actual action is developed. So what we have to do here is to look up 
a participant record for certain event and contact and set the status to configured status.

The first thing which is important here is to note that the action-provider does validate the incoming parameters and configuration
options. So we can assume those are valid.

Also note that we throw an Exception when no participant record could be found. 

```php

  class UpdateParticipantStatus extends AbstractAction {
    
    // ...
    
    /**
     * Run the action
     * 
     * @param ParameterInterface $parameters
     *   The parameters to this action.
     * @param ParameterBagInterface $output
     *   The parameters this action can send back 
     * @return void
     */
    protected function doAction(ParameterBagInterface $parameters, ParameterBagInterface $output) {
      // Get the contact and the event.
      $contact_id = $parameters->getParameter('contact_id');
      $event_id = $parameters->getParameter('event_id');
      
      // Find the participant record for this contact and event. 
      // This assumes that the contact has already been registered for the event.
      $participant = civicrm_api3('Participant', 'get', array(
        'contact_id' => $contact_id,
        'event_id' => $event_id,
        'options' => array('limit' => 1),
      ));
      if ($participant['count'] < 1) {
        // No record is found. 
        throw new \Civi\ActionProvider\Action\Exception\ExecutionException(E::ts('Could not find a participant record'));
      }
      
      // Get the participant record and the status id from the configuration.
      $participant = reset($participant['values']);
      $new_status_id = $this->configuration->getParameter('status');
      
      // Update the participant record through an API call.
      try {
        civicrm_api3('Participant', 'create', array(
          'id' => $participant['id'],
          'status_id' => $new_status_id,
        ));
      } catch (Exception $e) {
        throw new \Civi\ActionProvider\Action\Exception\ExecutionException(E::ts('Could not update participant status'));
      }
    }
  }

```

### Add a tag to this action

Each action could have several tags indicating wherefor this action could be used. So that other extension implementing the 
action provider could filter out only actions which works in their context.

The available tags could be found in the _AbstractAction_ class. You could also add your own tags if your want, each tag is a string.

In our example the tags we want to use are `AbstractAction::DATA_MANIPULATION` and `AbstractAction::SINGLE_CONTACT_ACTION`.

```php

  class UpdateParticipantStatus extends AbstractAction {
    
    // ...
    
    /**
     * Returns the tags for this action.
     */
    public function getTags() {
      return array(
        AbstractAction::SINGLE_CONTACT_ACTION_TAG,
        AbstractAction::DATA_MANIPULATION_TAG,
      );
    }
  }

```

## Make it available to the action provider

Now we have defined our action class. The last thing we have to do is to make it known to the action provider. 

What we have to do to is to add our class to the action provider class in _Civi\ActionProvider\Provider.php_:

```php

  namespace Civi\ActionProvider;
  // ...
    
  class Provider {

    // ...
    
    public function __construct() {
      $actions = array(
        new \Civi\ActionProvider\Action\AddToGroup(),
        new \Civi\ActionProvider\Action\Contact\ContactDataById(),
        new \Civi\ActionProvider\Action\Contact\FindOrCreateContactByEmail(),
        new \Civi\ActionProvider\Action\Event\UpdateParticipantStatus(),
      );
      
      // ...
    }
    
    // ...
    
  }

```

The only thing we do is add our action to the __construct method. 
If you implement actions in your own extensions this would be done in a different way.