• v3.2
    • Versions
    • v3.2

 

  • Install Akeneo PIM
    • Install Akeneo PIM with Docker
    • Install Akeneo PIM manually
      • System Requirements
      • System installation on Debian 9 (Stretch)
      • System installation on Ubuntu 16.04 (Xenial Xerus)
      • Installing Akeneo PIM Community Edition (CE) with the Archive
      • Installing Akeneo PIM Enterprise Edition (EE) with the Archive
      • Setting up the job queue daemon
    • How to customize the Dataset
    • How to Add Translation Packs
  • Migrate Akeneo PIM projects
    • How to apply a patch?
      • How to apply a patch - Community Edition
      • How to apply a patch - Enterprise Edition - Flexibility Cloud offer
      • How to apply a patch - Enterprise Edition - On Premise offer
    • Where is the Changelog?
    • How to upgrade to a minor version?
    • How to upgrade to a major version?
  • Import and Export data
    • How import works
    • Understanding the Product Import
    • Understanding the Product Export
    • Formats
      • Localized labels
      • Scopable labels
      • Association types data structure
      • Attribute data structure
      • Category data structure
      • Family data structure
      • Family variant data structure
      • Group data structure
      • Options data structure
      • Product data structure
      • Product model data structure
    • Akeneo Connectors
    • How to Customize Import / Export
      • How to create a new Connector
      • How to import Products from a XML file
      • How to clean a CSV file during a Product import
      • How to automate imports/exports
  • Manipulate the Akeneo PIM data
    • How to Customize Mass Edit Operations
      • How to register a new bulk action
      • How to Register a New Mass Edit Action on Products
      • How to Avoid Rules Execution on Mass Edit Actions
    • How to Manipulate Products
      • How to Query Products
      • How to Create Products
      • How to Update Products
      • How to Validate Products
      • How to Save Products
      • How to Remove Products
    • How to Manipulate Non-Product Objects
      • How to Query Non-Product Objects
      • How to Create Non-Product Objects
      • How to Update Non-Product Objects
      • How to Validate Non-Product Objects
      • How to Save Non-Product Objects
      • How to Remove Non-Product Objects
    • How to add a custom action rule
      • General information about rule format
      • How to add a custom action in the rule engine
    • How to Define Access Control List
    • How to Customize the Catalog Structure
      • How to Create a Reference Data
      • How to add a custom unit of measure
      • How to Add New Properties to a Category
    • How to Customize Product Assets
      • How to Add a New Transformation
      • How to Add a Default Thumbnail For Unknown File Types
      • How to connect to an external server for storage
      • How to change the validation rule to match a reference file to an asset
      • How to Mass Import Assets
    • How To Customize Teamwork Assistant (Enterprise Edition)
      • Customize notifications
      • Add a calculation step
      • How to log calculation step
      • Remove projects impacted by a custom catalog update
  • Design the user interfaces
    • How to customize any frontend part of the application
    • How to add an action button or meta data to the product edit form
    • How to add a tab to a form
    • How to add a new tab in System / Configuration
    • How to add custom information to a field
    • How to add a new field type
    • Create a custom product export builder filter
    • How to create the UI to manage a Reference Data
    • How to add a new page
    • How to customize the main menu
    • Styleguide
  • Maintain Akeneo PIM projects
    • First aid kit
    • Bug qualification
    • Common issues
    • Scalability Guide
      • Audit with 3 Representative Catalogs
      • More than 10k attributes?
      • More than 10k families?
      • More than 10k categories?
      • More than 500 attributes usable in the product grids?
      • More than 100k products to export?
      • More than 1GB of product media to export?
    • How to purge history
      • How to Purge jobs executions
      • How to adapt the version purger to your needs
  • Contribute to Akeneo PIM
    • How to report an issue?
    • How to translate the user interface?
    • How to enhance the documentation?
    • How to contribute to a Connector?
    • How to submit a patch to the PIM?
    • How to contribute to the frontend part of the application
    • How behavior tests are architectured in the PIM?
      • Establishing Decorator Pattern
      • Using Spin In Behat
  • Use SSO authentication locally
  • Reference Entities (beta)
    • Configure Entity Limits
    • Create a new Reference Entity Attribute type
    • Enrich Records with a new Reference Entity Attribute type
    • Add a Custom Property to Your Custom Attribute Type
    • Refresh records completeness
  • Technical overview
    • Product Information
    • Teamwork Assistant (Enterprise Edition)
      • Project creation
      • Project completeness
      • Project Completeness widget
      • Catalog update impact
      • Scalability guide
      • Users permission summary for Behat tests
    • Collaborative workflow
      • Simple workflow
      • Partial workflow
  • Technical architecture
    • Best Practices
      • Create a project
      • Create a reusable bundle
      • Setup Behat
      • Code Conventions
      • Coding Standards
    • How to implement your business logic using the event system
    • Events
      • Storage events
      • Workflow events (Enterprise Edition only)
    • How to Localize your data
      • How to change the PIM locale
      • How to Use Localizers
      • How to use Presenters
    • How to Add a Notification
    • Performances Guide
      • Memory usage of rules execution (Enterprise Edition)
      • Memory leak fix in Rules Engine (ORM)
      • More than 100 WYSIWYG editors in a page
      • PHP7 and HHVM Compatibility?
      • Job product batch size
    • How to Use the Web REST API
    • Standard format
      • Products
      • Other entities
      • Usage
    • Application Technical Information
      • Application Technical Dependencies
      • Server side set up for hosting
      • System Requirements
      • Recommended configuration
      • Client side configuration and compatibilities
      • Operation processes
      • Flow Matrix
  • Akeneo Cloud Edition
    • Flexibility Mode
      • Overview
      • Partners Starterkit
      • Environments Access
      • Composer settings
      • Periodic tasks / Crontab settings
      • PIM Application
      • PIM Updates and Migrations
      • File Transfer (SFTP)
      • Backups management
      • Partners
      • Queue management
      • System Components
      • Disk Usage
      • Ensure the Onboarder worker is always running
    • Serenity Mode
      • Overview
      • PIM Updates and Migrations
  • Akeneo Onboarder
    • Prerequisites
    • Installation
    • Migrate Akeneo Onboarder projects
      • How to apply a patch?
        • How to apply an Onboarder patch - Enterprise Edition - Flexibility Cloud offer
        • How to apply an Onboarder patch - Enterprise Edition - On Premise offer
      • How to upgrade to a minor version?
      • How to upgrade to a major version?
    • Synchronization
    • Troubleshooting
    • Environment variables
      • Using the DotEnv file
      • Using environment variables

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 Import and Export data.

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 what getActions() 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 PIM
  • alias: Alias of the operation, should be unique among your operations
  • acl: The ACL the operation is linked to
  • datagrid: The datagrid name this operation appears on
  • form_type: The FormType name this operation uses to be configured
  • operation_group: The group the operation belongs to (to regroup operations, see below screenshot)
Operation group dropdown

Note

The alias will be used in the URL (/enrich/mass-edit-action/capitalize-values/configure)

Phase 2: Create the Form extension¶

For this step, you’ll need to register a new form extension in /src/Acme/Bundle/CustomMassActionBundle/Ressources/config/form_extensions/mass_edit/product.yml:

extensions:
    pim-mass-product-edit-product-custom:
        module: pim/mass-edit-form/product/custom
        parent: pim-mass-product-edit
        position: 210
        targetZone: custom
        config:
            title: pim_enrich.mass_edit.product.step.custom.title
            label: pim_enrich.mass_edit.product.operation.custom.label
            labelCount: pim_enrich.mass_edit.product.custom.label_count
            description: pim_enrich.mass_edit.product.operation.custom.description
            code: custom
            jobInstanceCode: custom
            icon: icon-custom

The Mass Edit should be defined with at least code, label, icon and jobInstanceCode in the config array. The combination of the code, label and icon define the Operation. The jobInstanceCode is the code of the background job.

Then, you will have to create a requirejs module for this extension (/src/Acme/Bundle/CustomMassActionBundle/Ressources/public/js/mass-edit/form/product/custom.js) :

'use strict';

define(
    [
        'underscore',
        'pim/mass-edit-form/product/operation',
        'pim/template/mass-edit/product/change-status'
    ],
    function (
        _,
        BaseOperation,
        template
    ) {
        return BaseOperation.extend({
            template: _.template(template),

            /**
             * {@inheritdoc}
             */
            render: function () {
                this.$el.html(this.template());

                return this;
            },
        });
    }
);

Finally, you will have to require your custom module into the /src/Acme/Bundle/CustomMassActionBundle/Ressources/config/requirejs.yml.

config:
    paths:
        pim/mass-edit-form/product/custom: pimacme/js/mass-edit/form/product/custom

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\Tool\Component\StorageUtils\Updater\PropertySetterInterface;
use Akeneo\Pim\Enrichment\Component\Product\Connector\Processor\MassEdit\AbstractProcessor;
use Akeneo\Pim\Enrichment\Component\Product\Exception\InvalidArgumentException;
use Akeneo\Pim\Enrichment\Component\Product\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 bin/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 profiles
  • oro_access_group: which stored the user groups
  • pimee_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’.


Found a typo or a hole in the documentation and feel like contributing?
Join us on Github!