How to Create a New Connector¶
We’ll implement here a very minimalist Connector, it will do nothing but allow us to understand the main concepts and the overall architecture.
Create our Bundle¶
Create a new Symfony bundle:
1 2 3 4 5 6 7 8 9 | <?php
namespace Acme\Bundle\DummyConnectorBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AcmeDummyConnectorBundle extends Bundle
{
}
|
Register the bundle in AppKernel:
public function registerBundles()
{
// ...
new Acme\Bundle\DummyConnectorBundle\AcmeDummyConnectorBundle(),
// ...
}
Configure our Job service¶
Create a file Resources/config/jobs.yml
in our Bundle to configure a new job:
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 | parameters: ~
services:
# defines the job
acme_dummy_connector.job.dummy_job:
class: '%pim_connector.job.simple_job.class%' # we use existing class for the job
arguments:
- 'dummy_job' # this is the job name
- '@event_dispatcher'
- '@akeneo_batch.job_repository'
-
- '@acme_dummy_connector.step.dummy_step'
tags:
- { name: akeneo_batch.job, connector: 'Dummy Connector', type: '%pim_connector.job.export_type%' }
# defines the step
acme_dummy_connector.step.dummy_step:
class: '%pim_connector.step.item_step.class%' # we use existing class for the step
arguments:
- 'dummy_step' # this is the step name
- '@event_dispatcher'
- '@akeneo_batch.job_repository'
- '@pim_connector.reader.dummy_item'
- '@pim_connector.processor.dummy_item'
- '@pim_connector.writer.dummy_item'
|
Load this file in the bundle extension:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?php
namespace Acme\Bundle\DummyConnectorBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader;
class AcmeDummyConnectorExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('jobs.yml');
}
}
|
Here we use an existing dummy reader, a processor and a writer (they implement relevant interfaces and are usable but they do nothing with data).
The reader is implemented in the class Pim\Component\Connector\Reader\DummyItemReader
which is defined as a service in the ConnectorBundle with the alias pim_connector.reader.dummy_item
in the file Resources\config\readers.yml
.
The processor is implemented in the class Pim\Component\Connector\Processor\DummyItemProcessor
which is defined as a service in the ConnectorBundle with the alias pim_connector.processor.dummy_item
in the file Resources\config\processors.yml
.
The writer is implemented in the class Pim\Component\Connector\Writer\DummyItemWriter
, which is defined as a service in the ConnectorBundle with the alias pim_connector.writer.dummy_item
in the file Resources\config\writers.yml
.
We’ll explain in next cookbook chapters how to create your own elements with real logic inside.
Warning
Please note that in versions < 1.6, the file was always named “batch_jobs.yml” and was automatically loaded. The file content was very strict, was less standard and upgradeable than it is now.
Configure our JobParameters¶
To be executed, a Job is launched with a JobParameters which contains runtime parameters.
In our example, let’s assume that our job needs a file path where to write data. This path must be provided and the directory has to be writable in order to execute the job.
We have to define a couple of services implementing Akeneo\Component\Batch\Job\JobParameters\DefaultValuesProviderInterface
and Akeneo\Component\Batch\Job\JobParameters\ConstraintCollectionProviderInterface
.
The first service provides the default values used to create a JobParameters instance,
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 | <?php
namespace Acme\Bundle\DummyConnectorBundle\Job\JobParameters\DefaultValuesProvider;
use Akeneo\Component\Batch\Job\JobInterface;
use Akeneo\Component\Batch\Job\JobParameters\DefaultValuesProviderInterface;
class DummyExport implements DefaultValuesProviderInterface
{
protected $supportedJobNames;
public function __construct(array $supportedJobNames)
{
$this->supportedJobNames = $supportedJobNames;
}
public function getDefaultValues()
{
return [
'filePath' => '/tmp/dummy.txt',
];
}
public function supports(JobInterface $job)
{
return in_array($job->getName(), $this->supportedJobNames);
}
}
|
Tip
If the job doesn’t need any particular parameters, it’s possible to use directly the class Akeneo\Component\Batch\Job\JobParameters\EmptyDefaultValuesProvider
.
The second service provides the constraints used to validate each parameter of a JobParameters instance,
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 | <?php
namespace Acme\Bundle\DummyConnectorBundle\Job\JobParameters\ConstraintCollectionProvider;
use Akeneo\Component\Batch\Job\JobInterface;
use Akeneo\Component\Batch\Job\JobParameters\ConstraintCollectionProviderInterface;
use Pim\Bundle\ImportExportBundle\Validator\Constraints\WritableDirectory;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\NotBlank;
class DummyExport implements ConstraintCollectionProviderInterface
{
protected $supportedJobNames;
public function __construct(array $supportedJobNames)
{
$this->supportedJobNames = $supportedJobNames;
}
public function getConstraintCollection()
{
return new Collection(
[
'fields' => [
'filePath' => [
new NotBlank(['groups' => 'Execution']),
new WritableDirectory(['groups' => 'Execution'])
],
]
]
);
}
public function supports(JobInterface $job)
{
return in_array($job->getName(), $this->supportedJobNames);
}
}
|
Tip
If the job doesn’t need any particular parameters, it’s possible to use directly the class Akeneo\Component\Batch\Job\JobParameters\EmptyConstraintCollectionProvider
.
These services use tags and implement supports()
method so they can only be used for our job.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | parameters:
acme_dummy_connector.job.job_parameters.default_values_provider.dummy_export.class: Acme\Bundle\DummyConnectorBundle\Job\JobParameters\DefaultValuesProvider\DummyExport
acme_dummy_connector.job.job_parameters.constraint_collection_provider.dummy_export.class: Acme\Bundle\DummyConnectorBundle\Job\JobParameters\ConstraintCollectionProvider\DummyExport
services:
# Default values for our JobParameters
acme_dummy_connector.job.job_parameters.default_values_provider.dummy_export:
class: '%acme_dummy_connector.job.job_parameters.default_values_provider.dummy_export.class%'
arguments:
-
- 'dummy_job' # the job name
tags:
- { name: akeneo_batch.job.job_parameters.default_values_provider }
# Validation constraints for our JobParameters
acme_dummy_connector.job.job_parameters.constraint_collection_provider.dummy_export:
class: '%acme_dummy_connector.job.job_parameters.constraint_collection_provider.dummy_export.class%'
arguments:
-
- 'dummy_job' # the job name
tags:
- { name: akeneo_batch.job.job_parameters.constraint_collection_provider }
|
As for the jobs.yml
, this service file job_parameters.yml
must be loaded in our AcmeDummyConnectorExtension
.
Note
We could implement a single class implementing the 2 interfaces and define a single service on both tags.
Create a Job Instance¶
Each Job can be configured through a JobInstance, an instance of the Job.
It means we can define a job and several instances of it, with different configurations.
Please note that this job instance does not take any configuration.
We can create an instance with the following command:
# akeneo:batch:create-job <connector> <job> <type> <code> <config> [<label>]
php app/console akeneo:batch:create-job 'Dummy Connector' dummy_job export my_job_instance '[]'
You can also list the existing job instances with the following command:
php app/console akeneo:batch:list-jobs
Execute our new Job Instance¶
You can run the job with the following command:
php app/console akeneo:batch:job my_job_instance
[2016-07-07 16:48:35] batch.DEBUG: Job execution starting: startTime=, endTime=, updatedTime=, status=2, exitStatus=[UNKNOWN] , exitDescription=[], job=[my_job_instance] [] []
[2016-07-07 16:48:35] batch.INFO: Step execution starting: id=0, name=[dummy_step], status=[2], exitCode=[EXECUTING], exitDescription=[] [] []
[2016-07-07 16:48:35] batch.DEBUG: Step execution success: id= 1 [] []
[2016-07-07 16:48:35] batch.DEBUG: Step execution complete: id=1, name=[dummy_step], status=[1], exitCode=[EXECUTING], exitDescription=[] [] []
[2016-07-07 16:48:35] batch.DEBUG: Upgrading JobExecution status: startTime=2016-07-07T14:48:35+00:00, endTime=, updatedTime=, status=3, exitStatus=[UNKNOWN] , exitDescription=[], job=[my_job_instance] [] []
Export my_job_instance has been successfully executed.
The --config
option can be used to override the default values parameters, for instance, to change the file path.
php app/console akeneo:batch:job my_job_instance --config='{"filePath":"\/tmp\/new_path.txt"}'
Configure the UI for our JobParameters¶
At this point, the job is usable in command line though it cannot be configured via the UI.
We need to write a service providing the form type configuration for each parameter of our JobParameters instance.
In our case we want to display a a text field where to fill in the path. The validation constraint defined earlier will also apply here.
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 | <?php
namespace Acme\Bundle\DummyConnectorBundle\Job\JobParameters\FormConfigurationProvider;
use Akeneo\Component\Batch\Job\JobInterface;
use Akeneo\Component\Batch\Model\JobInstance;
use Pim\Bundle\ImportExportBundle\JobParameters\FormConfigurationProviderInterface;
class DummyExport implements FormConfigurationProviderInterface
{
protected $supportedJobNames;
public function __construct(array $supportedJobNames)
{
$this->supportedJobNames = $supportedJobNames;
}
public function getFormConfiguration()
{
return [
'filePath' => [
'type' => 'text',
'options' => [
'label' => 'pim_connector.export.filePath.label', // label to use in the form
'help' => 'pim_connector.export.filePath.help' // tooltip text to use in the form
]
],
];
}
public function supports(JobInterface $job)
{
return in_array($job->getName(), $this->supportedJobNames);
}
}
|
This service is configured using a tag and it implements supports()
method to be used for our job only.
1 2 3 4 5 6 7 8 9 10 11 12 | parameters:
acme_dummy_connector.job.job_parameters.form_configuration_provider.dummy_export.class: Acme\Bundle\DummyConnectorBundle\Job\JobParameters\FormConfigurationProvider\DummyExport
services:
# Form configuration for our JobParameters
acme_dummy_connector.job.job_parameters.form_configuration_provider.dummy_export:
class: '%acme_dummy_connector.job.job_parameters.form_configuration_provider.dummy_export.class%'
arguments:
-
- 'dummy_job' # the job name
tags:
- { name: pim_import_export.job_parameters.form_configuration_provider }
|
Translate Job and Step labels in the UI¶
Behind the scene, the service Pim\Bundle\ImportExportBundle\JobLabel\TranslatedLabelProvider
provides translated Job and Step labels to be used in the UI.
- This service uses following conventions:
- for a job label, given a $jobName, “batch_jobs.$jobName.label”
- for a step label, given a $jobName and a $stepName, “batch_jobs.$jobName.$stepName.label”
Create a file Resources/translations/messages.en.yml
in our Bundle to translate label keys.
1 2 | batch_jobs.dummy_job.label: Dummy Job
batch_jobs.dummy_job.dummy_step.label: Dummy Step
|
We can now create a new configuration for our Job from the UI, using the menu “spread > export profiles” then “create export profile” button.