How to Create a Custom Notification Step

The default ItemStep covers the majority of cases but sometimes you need to create more custom logic with no need for a reader, processor or writer.

For instance, at the end of an export you may want to send a custom email, copy the result to a FTP server or call a specific URL to report the result.

Let’s see how to go further by creating a custom step which sends a notification to a 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
<?php

namespace Acme\Bundle\NotifyConnectorBundle\Step;

use Akeneo\Component\Batch\Step\AbstractStep;
use Akeneo\Component\Batch\Model\StepExecution;

class NotifyStep extends AbstractStep
{
    protected function doExecute(StepExecution $stepExecution)
    {
        // inject the step execution in the step item to be able to log summary info during execution
        $jobParameters = $stepExecution->getJobParameters();
        $filepath = $jobParameters->get('filePath');
        $fields = sprintf("filepath=%s", urlencode($filepath));
        $url = $jobParameters->get('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 the summary
        $stepExecution->addSummaryInfo('notified', 'yes');
        curl_close($ch);
    }
}

Define the Step as a service

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
parameters:
    acme_notifyconnector.step.notify.class: Acme\Bundle\NotifyConnectorBundle\Step\NotifyStep

services:
    acme_notifyconnector.step.notify:
        class: '%acme_notifyconnector.step.notify.class%'
        arguments:
            - 'notify'
            - '@event_dispatcher'
            - '@akeneo_batch.job_repository'

Configure our new Job

In Resources/config/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
parameters:
    acme_notifyconnector.job_name.csv_product_export_notify: 'csv_product_export_notify'

services:
    acme_notifyconnector.notify_product_export:
        class: '%pim_connector.job.simple_job.class%'
        arguments:
            - '%acme_notifyconnector.job_name.csv_product_export_notify%'
            - '@event_dispatcher'
            - '@akeneo_batch.job_repository'
            -
                - '@pim_connector.step.csv_product.export'
                - '@acme_notifyconnector.step.notify'
        tags:
            - { name: akeneo_batch.job, connector: '%pim_connector.connector_name.csv%', type: '%pim_connector.job.export_type%' }

Configure the job parameters

In order to setup the form we use a form configuration provider.

First of all you need to create a new class that will contain the job parameters. This class should implement the following interfaces: - ConstraintCollectionProviderInterface - DefaultValuesProviderInterface - FormConfigurationProviderInterface

Here we decorate the ProductCsvExport classes in order to retrieve the proper form export configuration.

1
2
3
4
5
6
7
8
9
    public function __construct(
        DefaultValuesProviderInterface $productCsvDefaultValues,
        FormConfigurationProviderInterface $productCsvFormProvider,
        ConstraintCollectionProviderInterface $productCsvConstraint
    ) {
        $this->productCsvDefaultValues = $productCsvDefaultValues;
        $this->productCsvFormProvider = $productCsvFormProvider;
        $this->productCsvConstraint = $productCsvConstraint;
    }

Then we add the form configuration for our new url field.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    public function getFormConfiguration()
    {
        $csvFormOptions = array_merge($this->productCsvFormProvider->getFormConfiguration(), [
            'url' => [
                'options' => [
                    'required' => true,
                    'label'    => 'pim_connector.export.dateFormat.label',
                    'help'     => 'pim_connector.export.dateFormat.help',
                ]
            ],
        ]);

        return $csvFormOptions;
    }

In order to validate the job parameters we need to define some constraints. Here we want a valid url as input.

1
2
3
4
5
6
7
8
    public function getConstraintCollection()
    {
        $baseConstraint = $this->productCsvConstraint->getConstraintCollection();
        $constraintFields = $baseConstraint->fields;
        $constraintFields['url'] = new Url();

        return new Collection(['fields' => $constraintFields]);
    }

We also need to add the default values to our job form.

1
2
3
4
5
6
    public function getDefaultValues()
    {
        $parameters = $this->productCsvDefaultValues->getDefaultValues();
        $parameters['url'] = 'http://';

        return $parameters;

Add the support method with your job name csv_product_export_notify.

1
2
3
4
    public function supports(JobInterface $job)
    {
        return $job->getName() === 'csv_product_export_notify';
    }

Declare this class as a service, with the proper job name as parameter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
parameters:
    acme_notifyconnector.job.job_parameters.constraint_collection_provider.csv_product_export_notify.class: Acme\Bundle\NotifyConnectorBundle\JobParameters\ProductCsvExportNotify

services:
    acme_notifyconnector.job.job_parameters.constraint_collection_provider.csv_product_export_notify:
        class: '%acme_notifyconnector.job.job_parameters.constraint_collection_provider.csv_product_export_notify.class%'
        arguments:
            - '@pim_connector.job.job_parameters.default_values_provider.product_csv_export'
            - '@pim_import_export.job_parameters.form_configuration_provider.product_csv_export'
            - '@pim_connector.job.job_parameters.constraint_collection_provider.product_csv_export'
        tags:
            - { name: akeneo_batch.job.job_parameters.constraint_collection_provider }
            - { name: akeneo_batch.job.job_parameters.default_values_provider }
            - { name: pim_import_export.job_parameters.form_configuration_provider }

Configure the job profile

As it is a job profile based on products export, we need to display the “content” tab on the UI. To do this, we have to register the view element as visible for our job name:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace Acme\Bundle\NotifyConnectorBundle;

use Pim\Bundle\ImportExportBundle\DependencyInjection\Compiler\RegisterJobNameVisibilityCheckerPass;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class AcmeNotifyConnectorBundle extends Bundle
{
    /**
     * {@inheritdoc}
     */
    public function build(ContainerBuilder $container)
    {
        $container
            ->addCompilerPass(new RegisterJobNameVisibilityCheckerPass(
                ['acme_notifyconnector.job_name.csv_product_export_notify']
            ));
    }
}

Note

You could also add directly the string of your job name if you have not defined any parameter for it!

Translations

Add a translation for our brand new step:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
acme_notify_connector:
    jobs:
        notify_product_import:
            title: Product Import With Notification
            export:
                title: Product Export Step
            notify:
                title: Notification Step
    url:
        label: Notify url to call
        help: The url to call at the end of the export

job_execution.summary.notified: Notification sent

batch_jobs:
    csv_product_export_notify:
        label: Product Import Notify Csv
        export.label: Product import notify
        validation.label:  File encoding validation
        notify.label: Notify step

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