Skip to content

Latest commit

 

History

History
284 lines (220 loc) · 10.4 KB

register_a_new_bulk_action.rst

File metadata and controls

284 lines (220 loc) · 10.4 KB

How to register a new bulk action

In this cookbook, you will learn how to create a new bulk action. We will create a bulk action for products to add a comment for a set of products. Following this cookbook, you will be able to create any bulk action for any entity of the PIM.

To create a new bulk action on a product, you need to

  • create a new processor to process a product,
  • create a job with this processor,
  • create the page for the new bulk action.

Create the processor

A processor inherits from Akeneo\Pim\Enrichment\Component\Product\Connector\Processor\MassEdit\AbstractProcessor. Any processor should have a process($item) method to process an entity. Here, the process method will add a comment for a product.

Note

Each bulk action has a set of actions to do. Here, we register in the actions array a unique action containing the comment and the username.

// src/Acme/Bundle/AppBundle/Connector/Processor/MassEdit/Product/AddCommentProcessor.php
<?php

declare(strict_types=1);

namespace Acme\Bundle\AppBundle\Connector\Processor\MassEdit\Product;

use Akeneo\Tool\Component\StorageUtils\Saver\SaverInterface;
use Akeneo\Pim\Enrichment\Component\Comment\Builder\CommentBuilder;
use Akeneo\Pim\Enrichment\Component\Comment\Model\CommentInterface;
use Akeneo\Pim\Enrichment\Component\Product\Connector\Processor\MassEdit\AbstractProcessor;
use Akeneo\UserManagement\Component\Repository\UserRepositoryInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;

class AddCommentProcessor extends AbstractProcessor
{
    protected $commentBuilder;
    protected $commentSaver;
    protected $userRepository;

    public function __construct(
        CommentBuilder $commentBuilder,
        SaverInterface $commentSaver,
        UserRepositoryInterface $userRepository
    ) {
        $this->commentBuilder = $commentBuilder;
        $this->commentSaver = $commentSaver;
        $this->userRepository = $userRepository;
    }

    public function process($product): void
    {
        $actions = $this->getConfiguredActions();

        $comment = $this->commentBuilder->buildComment(
            $product,
            $this->userRepository->findOneByIdentifier($actions[0]['username'])
        )->setBody($actions[0]['value']);
        $this->commentSaver->save($comment);

        return $product;
    }
}

Then, declare a service in a processor configuration file:

# src/Acme/Bundle/AppBundle/Resources/config/processors.yml
parameters:
    acme.connector.processor.mass_edit.product.add_comment.class: Acme\Bundle\AppBundle\Connector\Processor\MassEdit\Product\AddCommentProcessor

services:
    acme.connector.processor.mass_edit.product.add_comment:
        class: '%acme.connector.processor.mass_edit.product.add_comment.class%'
        arguments:
            - '@pim_comment.builder.comment'
            - '@pim_comment.saver.comment'
            - '@pim_user.repository.user'

Don't forget to load this new configuration file in your dependency injection:

// src/Acme/Bundle/AppBundle/DependencyInjection/AcmeAppExtension.php
<?php

namespace Acme\Bundle\AppBundle\DependencyInjection;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

class AcmeAppExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('processors.yml');
    }
}

Create the job

The job will be run in background to process the entities with the processor defined above. The job that we'll create has one single step, and this step is the default step. The default step is composed of a reader (reading the products from the database, already exists), a processor and a writer (writing products to the database, already exists). As there is no need to redefine any class for this job, we simply add configuration files:

# src/Acme/Bundle/AppBundle/Resources/config/steps.yml
services:
    acme.step.add_comment.mass_edit:
        class: '%pim_connector.step.item_step.class%'
        arguments:
            - 'perform'
            - '@event_dispatcher'
            - '@akeneo_batch.job_repository'
            - '@pim_enrich.reader.database.product_and_product_model'
            - '@acme.connector.processor.mass_edit.product.add_comment'
            - '@pim_connector.writer.database.product'

Note

Each job needs to define a default value provider and a constraint collection provider. In this very simple case, we will use the default product mass edit ones, so we added it without any additional parameter.

# src/Acme/Bundle/AppBundle/Resources/config/jobs.yml
services:
    acme.job.add_comment:
        class: '%pim_connector.job.simple_job.class%'
        arguments:
            - 'add_comment'
            - '@event_dispatcher'
            - '@akeneo_batch.job_repository'
            - [ '@acme.step.add_comment.mass_edit' ]
        tags:
            -
                name: akeneo_batch.job
                connector: '%pim_enrich.connector_name.mass_edit%'
                type: '%pim_enrich.job.mass_edit_type%'

    acme.job.default_values_provider.add_comment:
        class: '%pim_enrich.connector.job.job_parameters.default_values_provider.product_mass_edit.class%'
        arguments:
            - [ 'add_comment' ]
        tags:
            - { name: akeneo_batch.job.job_parameters.default_values_provider }

    acme.job.constraint_collection_provider.add_comment:
        class: '%pim_enrich.connector.job.job_parameters.constraint_collection_provider.product_mass_edit.class%'
        arguments:
            - [ 'add_comment' ]
        tags:
            - { name: akeneo_batch.job.job_parameters.constraint_collection_provider }

Just like we did above, load these files to the dependency injection:

// src/Acme/Bundle/AppBundle/DependencyInjection/AcmeAppExtension.php
[...]
$loader->load('jobs.yml');
$loader->load('steps.yml');
[...]

Finally, add this new job instance to the database to be able to run it:

bin/console akeneo:batch:create-job internal add_comment mass_edit add_comment '{}' 'Add comment' --env=prod

Create the UI

The UI of this mass action is simple: we just have to create a textarea field. When a user updates this textarea, the data will be put in the form data. The new module is composed of a template and a form extension.

<!-- src/Acme/Bundle/AppBundle/Resources/public/templates/add-comment.html -->
<div class="AknFieldContainer">
    <div class="AknFieldContainer-header">
        <label class="AknFieldContainer-label">Comment</label>
    </div>
    <div class="AknFieldContainer-inputContainer">
        <textarea class="AknTextareaField comment-field" <% if (readOnly) { %> disabled="disabled"<% } %>><%- value %></textarea>
    </div>
</div>
// src/Acme/Bundle/AppBundle/Resources/public/js/add-comment.js
'use strict';
define(['underscore', 'pim/mass-edit-form/product/operation', 'acme/template/add-comment', 'pim/user-context'],
    function (_, BaseOperation, template, UserContext) {
        return BaseOperation.extend({
            template: _.template(template),
            events: {
                'change .comment-field': 'updateModel'
            },

            render: function () {
                this.$el.html(this.template({
                    value: this.getValue(),
                    readOnly: this.readOnly
                }));
                return this;
            },

            updateModel: function (event) {
                this.setValue(event.target.value);
            },

            setValue: function (comment) {
                let data = this.getFormData();
                data.actions = [{
                    field: 'comment',
                    value: comment,
                    username: UserContext.get('username')
                }];
                this.setData(data);
            },

            getValue: function () {
                const action = _.findWhere(this.getFormData().actions, { field: 'comment' });
                return action ? action.value : null;
            }
        });
    }
);

Then, register these new modules and add a new bulk action to the current list of bulk actions:

# src/Acme/Bundle/AppBundle/Resources/config/requirejs.yml
config:
    paths:
        acme/add-comment: acmeapp/js/add-comment
        acme/template/add-comment: acmeapp/templates/add-comment.html
# src/Acme/Bundle/AppBundle/Resources/config/form_extensions/mass_edit/product.yml
extensions:
    acme-mass-product-edit-configure-add-comment:
        module: acme/add-comment
        parent: pim-mass-product-edit
        position: 500
        config:
            title: pim_enrich.mass_edit.product.title
            label: 'Add comment'
            labelCount: "{1}Add comment to <span class=\"AknFullPage-title--highlight\">1 product</span>|]1, Inf[Add comment to <span class=\"AknFullPage-title--highlight\">{{ itemsCount }} products</span>"
            description: 'Add a comment for a set of products'
            code: add_comment
            jobInstanceCode: add_comment
            icon: icon-template

Recompute the assets

Finally, you have to reinstall your assets:

rm -rf var/cache/
bin/console pim:install:assets
bin/console assets:install --symlink
yarn run less
yarn run webpack

That's it! If you select several products then click "Bulk actions", your will be able to use your new feature.