How to add a new field type¶
If you want a custom rendering for one of your attributes, attribute types or reference data you need to create a new field type. In this cookbook, we will go through each step needed to create a custom field type.
Let’s say we want to have a slider to represent each “number attribute” having a minimum and a maximum value limit.
Before diving into code, we need to understand what’s going on under the hood:
- In akeneo, we have attributes with properties and an attribute type. In 1.3 the rendering of an attribute was driven by its attribute type. In 1.4 we introduced a field provider.
- This field provider gets an attribute and returns a field type
- In the form_extensions.yml or in the form_extensions folder of your Resources/config bundle’s folder you can map this field to the actual requirejs module
- The requirejs module contains the field’s logic
Here is a representation of this architecture:
Create a field provider¶
To create a custom field, first we need to create a FieldProvider for our new field type:
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 | <?php
namespace Acme\Bundle\CustomBundle\Enrich\Provider\Field;
use Pim\Component\Catalog\Model\AttributeInterface;
use Pim\Bundle\EnrichBundle\Provider\Field\FieldProviderInterface;
class RangeFieldProvider implements FieldProviderInterface
{
/**
* {@inheritdoc}
*/
public function getField($attribute)
{
return 'acme-range-field';
}
/**
* {@inheritdoc}
*/
public function supports($element)
{
//We only support number fields that have a number min and max property
return $element instanceof AttributeInterface &&
$element->getAttributeType() === 'pim_catalog_number' &&
null !== $element->getNumberMin() &&
null !== $element->getNumberMax();
}
}
|
Next, you need to register it in your services.yml file:
1 2 3 4 5 6 7 8 | parameters:
acme.custom.provider.field.range.class: Acme\Bundle\CustomBundle\Enrich\Provider\Field\RangeFieldProvider
services:
acme.custom.provider.field.range:
class: '%acme.custom.provider.field.range.class%'
tags:
- { name: pim_enrich.provider.field, priority: 90 }
|
Your field provider is now registered, congrats!
Create the form field¶
Now that we have a field provider, we can create the field itself:
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 | 'use strict';
/*
* src/Acme/Bundle/CustomBundle/Resources/public/js/product/field/range-field.js
*/
define([
'pim/field',
'underscore',
'text!acme/template/product/field/range'
], function (
Field,
_,
fieldTemplate
) {
return Field.extend({
fieldTemplate: _.template(fieldTemplate),
events: {
'change .field-input:first input[type="range"]': 'updateModel'
},
renderInput: function (context) {
return this.fieldTemplate(context);
},
updateModel: function () {
var data = this.$('.field-input:first input[type="range"]').val();
this.setCurrentValue(data);
}
});
}
);
|
And its template:
1 2 | <!-- src/Acme/Bundle/CustomBundle/Resources/public/templates/product/field/range.html -->
<input type="range" data-locale="<%= value.locale %>" data-scope="<%= value.scope %>" value="<%= value.data %>" <%= editMode === 'view' ? 'disabled' : '' %> min="<%= attribute.number_min %>" max="<%= attribute.number_max %>"/>
|
You can now register this module into your requirejs configuration:
1 2 3 4 5 6 7 | # Acme/Bundle/CustomBundle/Resources/config/requirejs.yml
config:
paths:
acme/range-field: acmecustom/js/product/field/range-field
acme/template/product/field/range: acmecustom/templates/product/field/range.html
|
Then, last operation, match the field type (acme-range-field) with the requirejs module (acme/range-field):
1 2 3 4 | # Acme/Bundle/CustomBundle/Resources/config/form_extensions.yml
attribute_fields:
acme-range-field: acme/range-field
|
After a cache clear, you can set the min and max value of any number attribute to start to use this new custom field!