Akeneo PIM for developers: a guide to the application stack and extension points

By Andrey Astakhov

Poor product descriptions are one of the highest causes of cart abandonment, and so the quality of your product information can make the difference between converting a customer or losing them. That’s why we’ve seen the rise of sophisticated product information management (PIM) tools such as Akeneo.

Here we explore everything you need to know about Akeneo PIM from a developer’s perspective with a focus on the application stack and extension points.

Firstly, a quick word on PIM

For anyone new to product information management, I’d recommend you start with this handy introduction to PIM.

One thing to remember is that a PIM solution is not a panacea for all your data problems; it’s only a tool. That said, if you know how to use it effectively, a PIM platform can help you centralise and improve your product data while hugely simplifying product management and maintenance.

One of the leading enterprise PIM solutions is Akeneo-a web-based, open-source software platform. Akeneo has a slick, intuitive UI and plenty of features that enable companies to import, process, translate, and distribute product inventories to multiple channels. 

PIM solutions are most effective when fully customised and tailored to suit a retailer’s specific needs. Akeneo is based on the Symfony framework and so has inherited Symfony’s modularity and flexibility. This means that Akeneo allows you to implement almost any individual requirements your organisation may have. 

Now let’s take a closer look at the application stack.

An overview of the Akeneo application stack

Platforms like DrupalSpryker, and Magento 2 each use Symfony components. Akeneo uses these components too, but also relies on the Symfony full-stack framework. 

" "

Above: a high-level view of the application stack 

Developers who will need to customise Akeneo can look forward to a well-organised application structure, an enabled HTTP foundation, a service container, and the choice of using a bundle system.

When working with Akeneo, you can easily create your own controllers and console commands using existing Akeneo services to manipulate product data, implement custom connectors to external systems, and extend the user interface.

Akeneo PIM relies on Doctrine ORM (object-relational mapper) which is well-known amongst PHP developers. The different types of data stored inside a PIM system (such as product models, product groups, associations, and categories) are all Doctrine entities. And thanks to Doctrine, Akeneo allows you to override mappings for an entity field so that you can extend standard models using your own fields.

Another element of the application stack to highlight is Elasticsearch. Akeneo uses the popular search engine to ensure optimal performance of the UI and REST API. It does this by using the elasticsearch-php library to query and update data stored in Elasticsearch indexes. Developers can not only manipulate this data, but also implement their own search filters and sorters.

Just like Symfony, Akeneo PIM introduces its own components – or reusable decoupled libraries – that implement specific business or technical logic. Each component has a corresponding bundle that contains the glue code to embed the component into an application. Components and bundles belong to large feature sets (domain-driven, design-bounded contexts) for the likes of User Management, Channel, Tool, Platform, Pim/Enrichment, Pim/Structure etc.

Akeneo extension points

Tagged services

Akeneo core developers have implemented more than 70 hooks that make it possible to add your custom logic to different parts of the core system. To use such a hook you just need to implement your own service and tag it in your service configuration accordingly.

Here are some examples of tagged services:

Service typeTag
Attribute typepim_catalog.attribute_type
Search filterpim_catalog.elasticsearch.query.product_filter
Collector of system informationpim_analytics.data_collector
Batch jobakeneo_batch.job
UI dashboard widgetpim_dashboard.widget
File transformation plugin (Enterprise Edition only)akeneo_file_transformer.transformation

Now, to demonstrate how to extend Akeneo with tagged services, let's create a very simple dashboard widget that displays a link to a page about raising Akeno’s mascot Ziggy, a three-headed hydra!

" "

Above: Akeneo's mascot, Ziggy

Step 1. Create a class ZiggyWidget implementing WidgetInterface - the contract that applies to all Akeneo dashboard widgets:


// src/Acme/Bundle/AppBundle/Widget/ZiggyWidget.php

namespace Acme\Bundle\AppBundle\Widget;

use Pim\Bundle\DashboardBundle\Widget\WidgetInterface;

class ZiggyWidget implements WidgetInterface
    public function getAlias()
        return 'ziggy';

    public function getTemplate()
        return 'AcmeAppBundle:Widget:ziggy.html.twig';

    public function getParameters()
        return [
            'text' => 'How To Raise A Ziggy',
            'link' => 'https://ziggy.akeneo.com/'

    // No real data needed for our simple example
    public function getData()
        return [];

Step 2. Register this class as a service with the pim_dashboard.widget tag to inform Akeneo about our new widget:

# src/Acme/Bundle/AppBundle/Resources/config/services.yml
        class: Acme\Bundle\AppBundle\Widget\ZiggyWidget
            # Set negative value for the position to make your widget above others
            - { name: pim_dashboard.widget, position: -200 }

Step 3. Create the template:

{# src/Acme/Bundle/AppBundle/Resources/views/Widget/ziggy.html.twig #}

{% extends 'PimDashboardBundle:Widget:base.html.twig' %}

{% set widgetTitle = 'Ziggy The Hydra' %}

{% set widgetContent %}
            <a href="{{ link }}">{{ text }}</a>
{% endset %}

Step 4. Clear cache and find your widget on the Activity > Dashboard page:

" "

Extending functionality with an event system

Events are a common means of extending the functionality of a Symfony application. Akeneo triggers lots of event notifications and you can listen to them and respond by executing your own code.

This table shows some of the most important types of events:

Event typeWhat happens when dispatched
Storage events
  • product is saved to the database
Workflow events (Enterprise Edition only)
  • product draft is sent for approval
  • product draft change is approved
  • product draft is approved, rejected, or deleted 
Grid events
  • data grid is rendered

To demonstrate the usage of the event system, let's implement a service that assigns all red products from a gift category to a special product group called 'Xmas Gift Ideas'.

Step 1. Create the event subscriber class AddProductsToXmasGiftIdeasGroupSubscriber which listens for StorageEvents::PRE_SAVE events:


// src/Acme/Bundle/AppBundle/EventSubscriber/AddProductsToXmasGiftIdeasGroupSubscriber.php

namespace Acme\Bundle\AppBundle\EventSubscriber;

use Akeneo\Pim\Enrichment\Component\Product\Model\ProductInterface;
use Akeneo\Tool\Component\StorageUtils\Repository\IdentifiableObjectRepositoryInterface;
use Akeneo\Tool\Component\StorageUtils\StorageEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\GenericEvent;

class AddProductsToXmasGiftIdeasGroupSubscriber implements EventSubscriberInterface
     * @var IdentifiableObjectRepositoryInterface
    private $groupRepository;

    public function __construct(IdentifiableObjectRepositoryInterface $groupRepository)
        $this->groupRepository = $groupRepository;

     * {@inheritdoc}
    public static function getSubscribedEvents(): array
        return [
            StorageEvents::PRE_SAVE => 'addProductsToXmasGiftIdeasGroup'

    public function addProductsToXmasGiftIdeasGroup(GenericEvent $event): void
        // Get the product from the event subject
        $product = $event->getSubject();

        if (!$product instanceof ProductInterface) {

        // Get the value of the product color
        $colorValue = $product->getValue('color');
        $color = $colorValue->getData()->getCode();
        // Get the category code
        $categories = $product->getCategoryCodes();

        // If the product has red color and assigned to the Gifts category
        // add it to the group Xmas Gift Ideas
        if ('red' === $color && in_array('gifts', $categories)) {
            $group = $this->groupRepository->findOneByIdentifier('xmas_gift_ideas');

Step 2. Register the subscriber:

# src/Acme/Bundle/AppBundle/Resources/config/services.yml
        class: Acme\Bundle\AppBundle\EventSubscriber\AddProductsToXmasGiftIdeasGroupSubscriber
            - '@pim_catalog.repository.group'
            - { name: kernel.event_subscriber }

Step 3. Create or edit a product in the category with the identifier 'gifts'. Change its colour to red, save it, and check whether it’s assigned to the 'Xmas Gift Ideas' product group.

" "

Important note: the above code has been created for demonstration purposes only.

In the real application please consider using the Akeneo rules engine which allows you to enrich product data based on predefined rules (note that this is only available in the Enterprise Edition).

A final word

I hope you’ve enjoyed this overview of the application stack and extension points in Akeneo. 

A final point to make is that tagged services and events are not the only ways of extending an Akeneo application, and Akeneo has plenty of useful guides on how to implement your features within the PIM – from adding new types of reference data and implementing custom API endpoints, to programmatically adding new units of measures and many other things.

Since it’s a Symfony application, Akeneo offers all the advantages of Symfony as standard, from being easy to customise, to the ease of use and debugging. Developers working with Akeneo have access to excellent documentation, plus thousands of existing Symfony bundles and technical blog posts.

For these reasons, working with Akeneo is a rewarding experience, and for any developers getting started with Akeneo my message is: enjoy it!