How to Create a Custom Step

Previously we discussed about How to Create a New Connector

Let’s see how to go further by creating a custom step which sends a notification to an URL when a product export is finished.

Create our Step

We will begin by creating a NotifyStep with its configuration and a doExecute method:

 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
<?php

namespace Acme\Bundle\DemoConnectorBundle\Step;

class NotifyStep extends AbstractStep
{
    // here, the handler is a step element
    protected $handler;

    protected function doExecute(StepExecution $stepExecution)
    {
        // inject the step execution in the step item to be able to log summary info during execution
        $this->handler->setStepExecution($stepExecution);
        $this->handler->execute();
    }

    // as step configuration, we merge the step items configuration
    public function getConfiguration()
    {
        $configuration = array();
        foreach ($this->getConfigurableStepElements() as $stepElement) {
            if ($stepElement instanceof AbstractConfigurableStepElement) {
                foreach ($stepElement->getConfiguration() as $key => $value) {
                    if (!isset($configuration[$key]) || $value) {
                        $configuration[$key] = $value;
                    }
                }
            }
        }

        return $configuration;
    }

    // we inject the configuration in each step item
    public function setConfiguration(array $config)
    {
        foreach ($this->getConfigurableStepElements() as $stepElement) {
            if ($stepElement instanceof AbstractConfigurableStepElement) {
                $stepElement->setConfiguration($config);
            }
        }
    }

    // these getter / setter are required to allow to configure from form and execute
    public function getHandler()
    {
        return $this->handler;
    }

    public function setHandler(CurlHandler $handler)
    {
        $this->handler= $handler;
    }

    // step items which are configurable with the job edit form
    public function getConfigurableStepElements()
    {
        return array('handler' => $this->getHandler());
    }
}

Create our Step Element

Then we implement a Step Element, in our case, a handler responsible to send a ping request:

 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
<?php

namespace Acme\Bundle\DemoConnectorBundle\Handler;
// This element is configurable and knows the step execution
class CurlHandler extends AbstractConfigurableStepElement implements StepExecutionAwareInterface
{
    protected $stepExecution;

    protected $url;

    protected $filePath;

    // execute method uses configuration to do a curl exec
    public function execute()
    {
        $filepath = $this->filePath;
        $fields = sprintf("filepath=%s", urlencode($filepath));
        $url = $this->url;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $fields);
        $result = curl_exec($ch);
        if ($result === false) {
            // we stop the whole job
            throw new \Exception('Curl fail');
        }
        // we add custom details in summary
        $this->stepExecution->addSummaryInfo('notified', 'yes');
        curl_close($ch);
    }

    public function setStepExecution(StepExecution $stepExecution)
    {
        $this->stepExecution = $stepExecution;
    }

    // Getter and setter are required to be able to configure the Element
    public function setFilePath($filePath)
    {
        $this->filePath = $filePath;

        return $this;
    }

    public function getFilePath()
    {
        return $this->filePath;
    }

    public function setUrl($url)
    {
        $this->url = $url;

        return $this;
    }

    public function getUrl()
    {
        return $this->url;
    }

    // Here, we define the form fields to use
    public function getConfigurationFields()
    {
        return array(
            'filePath' => array(
                'options' => array(
                    'label' => 'pim_base_connector.export.filePath.label',
                    'help'  => 'pim_base_connector.export.filePath.help'
                )
            ),
            'url' => array(
                'options' => array(
                    'label' => 'pim_base_connector.export.url.label',
                    'help'  => 'pim_base_connector.export.url.help'
                )
            )
        );
    }
}

Define the Step Element as Service

Add your step element to services.yml to ensure that it is loaded and processed by DependencyInjection/AcmeDemoConnectorExtension:

1
2
3
4
5
6
parameters:
    acme_democonnector.step.notify.class:  Acme\Bundle\DemoConnectorBundle\Step\NotifyStep
    acme_democonnector.handler.curl.class: Acme\Bundle\DemoConnectorBundle\Handler\CurlHandler
services:
    acme_democonnector.handler.curl:
        class: "%acme_democonnector.handler.curl.class%"

Configure our new Job

In Resources/config/batch_jobs.yml, we use a first step to export products in csv and we configure the second one to send a notification:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
connector:
    name: Demo Connector
    jobs:
        demo_custom_export:
            title: pim_base_connector.jobs.demo_custom_export.title
            type:  export
            steps:
                export:
                    title: pim_base_connector.jobs.demo_custom_export.export.title
                    services:
                        reader:    pim_base_connector.reader.doctrine.bulk_product
                        processor: pim_base_connector.processor.csv_serializer.product
                        writer:    pim_base_connector.writer.product_file
                notify:
                    title: pim_base_connector.jobs.demo_custom_export.notify.title
                    class: "%acme_democonnector.step.notify.class%"
                    services:
                        handler: acme_democonnector.handler.curl

You may have noticed that we use a custom class for the notification step and we define the handler as a step element.

That’s it, you can now connect to the PIM and begin to configure and use your new export!