How to Register a New Mass Edit Action on Products¶
The Akeneo PIM comes with a number of mass edit actions. It also comes with a flexible way to define your own mass edit actions on selected products.
Prerequisite¶
The mass edit action uses the BatchBundle in order to run mass edit in the background. Readers and Writers are already created so in this cookbook we will focus on how to create a Mass Edit Action and create a Processor. For more information on how to create Jobs, Readers, Processors, or Writers please see How to Customize Import / Export.
Phase 1: Create the Operation¶
Tip
Operations are designed to build and transport the configuration (eventually via a form) that will be sent to the background job. No item is updated from here!
The first step is to create a new class in the Operation folder that extends AbstractMassEditOperation
and declare this new class as a service in the mass_actions.yml file.
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 | // /src/Acme/Bundle/CustomMassActionBundle/MassEditAction/Operation/CapitalizeValues.php
<?php
namespace Acme\Bundle\CustomMassActionBundle\MassEditAction\Operation;
use Pim\Bundle\EnrichBundle\MassEditAction\Operation\AbstractMassEditOperation;
class CapitalizeValues extends AbstractMassEditOperation
{
/**
* {@inheritdoc}
*/
public function getOperationAlias()
{
return 'capitalize-values';
}
/**
* {@inheritdoc}
*/
public function getFormType()
{
return 'acme_custom_mass_action_operation_capitalize_values';
}
/**
* {@inheritdoc}
*/
public function getFormOptions()
{
return [];
}
/**
* {@inheritdoc}
*/
public function getActions()
{
return [
'field' => 'name',
'options' => ['locale' => null, 'scope' => null]
];
}
}
|
- 2 things will be sent to the Job:
actions
: the raw configuration actions, you define what you want here. It will be available within your Job. That’s whatgetActions()
is used for. Here actions are hard-coded, but it could be generated by another method or something else.filters
: the selection filter to tell the job which items it will work on. It’s used by the Reader.
Once the Operation is created, you must register it as a service in the DI with the pim_enrich.mass_edit_action
tag:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/config/mass_actions.yml
services:
acme_custom_mass_action.mass_edit_action.capitalize_values:
public: false
class: Acme\Bundle\CustomMassActionBundle\MassEditAction\Operation\CapitalizeValues
arguments:
- 'mass_edit_capitalize_values'
tags:
-
name: pim_enrich.mass_edit_action
alias: capitalize-values
acl: pim_enrich_product_edit_attributes
datagrid: product-grid
operation_group: mass-edit
form_type: acme_custom_mass_action_operation_capitalize_values
|
As you can see, the tag needs several parameters:
name
: Tag name to identify all mass edit operations in the PIMalias
: Alias of the operation, should be unique among your operationsacl
: The ACL the operation is linked todatagrid
: The datagrid name this operation appears onform_type
: The FormType name this operation uses to be configuredoperation_group
: The group the operation belongs to (to regroup operations, see below screenshot)
Note
The alias will be used in the URL (/enrich/mass-edit-action/capitalize-values/configure
)
Phase 2: Create the FormType¶
Tip
As the Operation is used to build configuration, we could need a FormType
that will be shown during the configuration step in the UI. Use it to show whatever is useful to you: Select, Input...
For this cookbook, we do not need the user to configure this action, so we’ll use an empty FormType
:
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 | // /src/Acme/Bundle/CustomMassActionBundle/Form/Type/MassEditAction/CapitalizeValuesType.php
<?php
namespace Acme\Bundle\CustomMassActionBundle\Form\Type\MassEditAction;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CapitalizeValuesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Build your form here
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
[
'data_class' => 'Acme\\Bundle\\CustomMassActionBundle\\MassEditAction\\Operation\\CapitalizeValues'
]
);
}
public function getName()
{
return 'acme_custom_mass_action_operation_capitalize_values';
}
}
|
Don’t forget to register it as a service in the DI:
1 2 3 4 5 6 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/config/form_types.yml
services:
acme_custom_mass_action.form.type.capitalize_values:
class: Acme\Bundle\CustomMassActionBundle\Form\Type\MassEditAction\CapitalizeValuesType
tags:
- { name: form.type, alias: acme_custom_mass_action_operation_capitalize_values }
|
The FormType
is now linked to the Operation with its name.
You need to create a template to render your Mass Edit Action form:
1 2 3 4 5 6 7 8 9 10 11 12 | {# /app/Resources/PimEnrichBundle/views/MassEditAction/product/configure/capitalize-values.html.twig #}
{#
You should extend the configure twig template relative to the entities you want to edit.
The configure template contains all translations & routes.
#}
{% extends 'PimEnrichBundle:MassEditAction:product/configure.html.twig' %}
{% block formContent %}
My Form is here !
But we don't have any field for this case.
{% endblock %}
|
Note
It will be visible in the configure step of the mass edit.
Phase 3: Create the Processor¶
Well! Now the user can select the Operation to launch it. The Operation will send its config (filters
, and actions
) to a background job process. Now we have to write the Processor that will handle product modifications.
The Processor
receives products one by one, given by the Reader
:
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 | // /src/Acme/Bundle/CustomMassActionBundle/Connector/Processor/MassEdit/Product/CapitalizeValuesProcessor.php
<?php
namespace Acme\Bundle\CustomMassActionBundle\Connector\Processor\MassEdit\Product;
use Akeneo\Component\StorageUtils\Updater\PropertySetterInterface;
use Pim\Bundle\EnrichBundle\Connector\Processor\AbstractProcessor;
use Pim\Component\Catalog\Exception\InvalidArgumentException;
use Pim\Component\Catalog\Model\ProductInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class CapitalizeValuesProcessor extends AbstractProcessor
{
/** @var PropertySetterInterface */
protected $propertySetter;
/** @var ValidatorInterface */
protected $validator;
/**
* @param PropertySetterInterface $propertySetter
* @param ValidatorInterface $validator
*/
public function __construct(PropertySetterInterface $propertySetter, ValidatorInterface $validator)
{
$this->propertySetter = $propertySetter;
$this->validator = $validator;
}
/**
* {@inheritdoc}
*/
public function process($product)
{
/** @var ProductInterface $product */
// This is where you put your custom logic. Here we work on a
// $product the Reader gave us.
// This is the configuration we receive from our Operation
$actions = $this->getConfiguredActions();
// Retrieve custom config from the action
$field = $actions['field'];
$options = $actions['options'];
// Capitalize the attribute value of the product
$originalValue = $product->getValue($field)->getData();
$capitalizedValue = strtoupper($originalValue);
// Use the property setter to update the product
$newData = ['field' => $field, 'value' => $capitalizedValue, 'options' => $options];
$this->setData($product, [$newData]);
// Validate the product
if (null === $product || (null !== $product && !$this->isProductValid($product))) {
$this->stepExecution->incrementSummaryInfo('skipped_products');
return null; // By returning null, the product won't be saved by the Writer
}
// Used on the Reporting Screen to have a summary on the Mass Edit execution
$this->stepExecution->incrementSummaryInfo('mass_edited');
return $product; // Send the product to the Writer to be saved
}
/**
* Validate the product and raise a warning if not
*
* @param ProductInterface $product
*
* @return bool
*/
protected function isProductValid(ProductInterface $product)
{
$violations = $this->validator->validate($product);
$this->addWarningMessage($violations, $product);
return 0 === $violations->count();
}
/**
* Set data from $actions to the given $product
*
* @param ProductInterface $product
* @param array $actions
*
* @return CapitalizeValuesProcessor
*/
protected function setData(ProductInterface $product, array $actions)
{
foreach ($actions as $action) {
$this->propertySetter->setData($product, $action['field'], $action['value'], $action['options']);
}
return $this;
}
}
|
Again, register the newly created class:
1 2 3 4 5 6 7 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/config/processors.yml
services:
acme_custom_mass_action.mass_edit.capitalize_values.processor:
class: Acme\Bundle\CustomMassActionBundle\Connector\Processor\MassEdit\Product\CapitalizeValuesProcessor
arguments:
- '@pim_catalog.updater.product_property_setter'
- '@pim_catalog.validator.product'
|
Phase 4: Create the background Job¶
Tip
The Step will run 3 steps: Read, Process & Write. In this cookbook, we use existing Reader and Writer.
We just wrote the Processor in the previous phase, so let’s tell the Job which services to use!
First of all you need to define a new job as follows:
1 2 3 4 5 6 7 8 9 10 11 12 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/config/jobs.yml
services:
acme_custom_mass_action.job.capitalize_values:
class: '%pim_connector.job.simple_job.class%'
arguments:
- 'mass_edit_capitalize_values'
- '@event_dispatcher'
- '@akeneo_batch.job_repository'
-
- '@acme_custom_mass_action.step.capitalize_values.mass_edit'
tags:
- { name: akeneo_batch.job, connector: '%pim_enrich.connector_name.mass_edit%', type: '%pim_enrich.job.mass_edit_type%' }
|
Then you need to define your step with the proper Reader, Processor and Writer.
1 2 3 4 5 6 7 8 9 10 11 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/config/steps.yml
services:
acme_custom_mass_action.step.capitalize_values.mass_edit:
class: '%pim_connector.step.item_step.class%'
arguments:
- 'mass_edit_capitalize_values'
- '@event_dispatcher'
- '@akeneo_batch.job_repository'
- '@pim_connector.reader.database.product'
- '@acme_custom_mass_action.mass_edit.capitalize_values.processor'
- '@pim_connector.writer.database.product'
|
You also need to set job parameters:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/config/job_parameters.yml
services:
acme_custom_mass_action.connector.job.job_parameters.default_values_provider.product_mass_edit:
class: '%pim_enrich.connector.job.job_parameters.default_values_provider.product_mass_edit.class%'
arguments:
- ['mass_edit_capitalize_values']
tags:
- { name: akeneo_batch.job.job_parameters.default_values_provider }
acme_custom_mass_action.connector.job.job_parameters.constraint_collection_provider.product_mass_edit:
class: '%pim_enrich.connector.job.job_parameters.constraint_collection_provider.product_mass_edit.class%'
arguments:
- ['mass_edit_capitalize_values']
tags:
- { name: akeneo_batch.job.job_parameters.constraint_collection_provider }
|
The Job has to be in your database, so add it to your fixtures:
1 2 3 4 5 6 7 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/fixtures/jobs.yml
jobs:
mass_edit_capitalize_values:
connector: Akeneo Mass Edit Connector
alias: mass_edit_capitalize_values
label: Mass capitalize products value
type: mass_edit
|
Note
To better understand how to handle this, you can read this chapter: Add your Own Data
If your installation is already set up, use the akeneo:batch:create-job command:
php app/console akeneo:batch:create-job "Akeneo Mass Edit Connector" "mass_edit_capitalize_values" "mass_edit" "mass_edit_capitalize_values" '{}' "Mass capitalize product values"
Warning
For Enterprise Edition version, see Phase 6 to add job profile permissions in pimee_security_job_profile_access table.
Phase 5: Translating the Mass Edit Action Choice¶
Once you have realized the previous operations (and eventually cleared your cache), you should see a new option on the /enrich/mass-edit-action/choose
page.
Akeneo will generate for you a translation key following this pattern:
pim_enrich.mass_edit_action.%alias%.label
.
You may now define some translation keys (label, description, success_flash...
) in your translations catalog(s).
1 2 3 4 5 6 7 8 9 10 11 12 | # /src/Acme/Bundle/CustomMassActionBundle/Resources/translations/messages.en.yml
pim_enrich.mass_edit_action:
capitalize-values:
label: Capitalize values
description: The selected product(s) will have capitalized values
launched_flash: The bulk action "capitalize values" has been launched. You will be notified when it is done.
success_flash: Product(s) values have been updated
batch_jobs:
mass_edit_capitalize_values:
capitalize_values:
label: Capitalize values
|
Phase 6: Add user groups permissions to job profiles (ENTERPRISE EDITION)¶
In Enterprise Edition version, job profiles are managed with user groups permissions, so you need to add these permissions. To deal with these permissions, you have 3 tables:
akeneo_batch_job_instance
: which stores the job profilesoro_access_group
: which stored the user groupspimee_security_job_profile_access
: which stores the permissions (this table only exists in Enterprise Edition)
You have to get your job instance (job profile) code from the first table, get the user groups from your second table and then execute an insert SQL query to add these permissions. It will be something like:
INSERT INTO pimee_security_job_profile_access VALUES ('', <job_profile_id>, <user_group_id>, 1, 1);
The two last numbers means you give respectively ‘edit’ and ‘execution’ permissions. Otherwise add ‘0’.