Adding support for a new field type to eZ Platform UI

by Filip Božanović -

Currently, Platform UI is still in its early versions, but it's nearing release. This article deals with integrating custom field types into the new Admin interface. I'm using our TagsBundle as an example.

As there is no documentation yet, some of the following steps could be done in a simpler way. Feel free to share any corrections or better practices in the comments.

Assumptions

  1. You know your way around JavaScript and have worked with YUI, because the missing last step involves working with Handlebars and YUI modules.
  2. You want to make the bundle standalone, essentially extending Platform UI.
  3. This post only deals with a skeleton implementation of the Tags field type, because currently there is no interface for editing tags in the content edit view, not to mention REST or YUI plugins. However, field types utilising standard content storage should be straightforward to implement using the existing field types as a reference and making use of existing plugins.

What needs to be done

If the custom field type has settings, such as 'Subtree Limit' or 'Max Length', we must define them in the edit form.

TagsBundle happens to have the following additional fields:

  • Subtree Limit
  • Show dropdown
  • Hide root tag
  • Max tags

Whether the custom field type has settings or not, we must implement the content view and content edit view, which are currently Handlebars templates with accompanying JavaScript files. But, first things first.

Adding form fields to the content type edit view

EzSystemsRepositoryFormsBundle handles the field types present when editing a content type, so we must add a template with a new Twig block specific to your field type used for form rendering. As an example, I’m using kernel.yml (the original configuration can be found in the RepositoryForms bundle).

  1. system:
        default:
            fielddefinition_edit_templates:
                - { template: "NetgenTagsBundle:ContentType:field_types.html.twig", priority: 0 }
  2. Add the kernel.yml to your Bundle using extension prepend (see Symfony documentation)

    $kernelConfigFile = __DIR__ . '/../Resources/config/kernel.yml';
    $kernelConfig = Yaml::parse(file_get_contents($kernelConfigFile));
    $container->prependExtensionConfig('ezpublish', $kernelConfig);
  3. Your template needs to have a block specific to your field type:

    {% block eztags_field %}
    {% spaceless %}
       {% for tag in field.value.tags %}
           <a href="{{ path( tag ) }}">{{ netgen_tags_tag_keyword( tag ) }}</a>{% if not loop.last %}, {% endif %}
       {% endfor %}
    {% endspaceless %}
    {% endblock %}

    For the above, you can also look at how it's implemented in the RepositoryFormsBundle.

  4. Add a form mapper service for your field type in services.yml:

    parameters:
       eztags.field_type.form_mapper.eztags.class: Netgen\TagsBundle\Core\FieldType\Tags\FormMapper
    
    services:
       eztags.field_type.form_mapper.eztags:
          class: %eztags.field_type.form_mapper.eztags.class%
          arguments: [@translator]
          tags:
              - { name: ez.fieldType.formMapper, fieldType: eztags }

    I'm also injecting the @translator service for a later step.

  5. Implement the FormMapper we've just defined. I've omitted the use statements, injection of the translator service, the other fields and validation. You are encouraged to look at the other FormMapper classes for guidance.

     namespace Netgen\TagsBundle\Core\FieldType\Tags;
    
    class FormMapper implements FieldTypeFormMapperInterface
    {
    
       public function mapFieldDefinitionForm(FormInterface $fieldDefinitionForm, FieldDefinitionData $data)
       {
           $fieldDefinitionForm
               ->add('subTreeLimit', 'integer', [
                   'required' => false,
                   'label' => $this->translator->trans('field_definition.eztags.subtree_limit', [], 'ezrepoforms_content_type'),
                   'property_path' => 'fieldSettings[subTreeLimit]',
               ])
           ;
       }
    }
  6. Add translation files for the labels. In step 5 we're using the translator service, so you can create i.e. ezrepoforms_content_type.en.yml under Resources/translations:

     field_definition.eztags.subtree_limit: 'Subtree limit'

    Last, but not least, add a label for the field type to be displayed in the 'Add new Field' dropdown, i.e. Resources/translations/fieldtypes.en.yml:

     eztags: { name: "Tags" }

The content view and edit views

These are located in the public directory and installed as assets, so you will probably want to use the assets:install command with the --symlink --relative switches.

Currently, the canonical paths for the js files are:

  • public/js/views/fields/ez-{yourfieldtype}-editview.js
  • public/js/views/fields/ez-{yourfieldtype}-view.js

The canonical paths for their respective template files are :

  • public/templates/fields/edit/{yourfieldtype}.hbt
  • public/templates/fields/view/{yourfieldtype}.hbt

Of course, these need to be mapped somewhere. Platform UI uses yui.yml to define the modules, and I've extracted the relevant appends for our field type:

system:
  default:
    yui:
      modules:
        ez-contenteditformview:
          requires:
            - 'ez-tags-editview'
        ez-rawcontentview:
          requires:
            - 'ez-tags-view'
         tagseditview-ez-template:
           type: 'template'
           path: bundles/netgentags/templates/fields/edit/tag.hbt
         ez-tags-editview:
           requires: ['ez-fieldeditview', 'event-valuechange', 'tagseditview-ez-template']
           path: bundles/netgentags/js/views/fields/ng-tag-editview.js
           
          tagsview-ez-template:
            type: 'template'
            path: bundles/netgentags/templates/fields/view/tag.hbt
          ez-tags-view:
            requires: ['ez-fieldview', 'tagsview-ez-template', 'ez-asynchronousview']
            path: bundles/netgentags/js/views/fields/ng-tag-view.js

Some of the above 'requires' refer to YUI modules and working with them is outside the scope of this article.

The next step is to actually include our yui.yml in the extension. I've adopted the convention used in Platform UI:

public function prepend(ContainerBuilder $container)
{
     // some configuration
     $this->prependYui( $container );
}

private function prependYui(ContainerBuilder $container)
{
   $yuiConfigFile = __DIR__ . '/../Resources/config/yui.yml';
   $config = Yaml::parse(file_get_contents($yuiConfigFile));
   $container->prependExtensionConfig('ez_platformui', $config);
   $container->addResource(new FileResource($yuiConfigFile));
}

This concludes most of the modifications needed from a backend perspective. What remains is implementing the Handlebars templates and their Javascript modules. This is left as an exercise for the reader.

The basic view implemented for the Tags field type is just a plain HTML list, while the edit view looks like this (adapted from the template of the RelationList field type).

Tag edit view

Closing thoughts

As of this writing, Platform UI is still in alpha, so there might be some significant changes to the way things work. Adding custom field types in Platform UI is doable. However, I’ve found the process somewhat more involved than I expected.

In any event, I hope this article will help you get started with upgrading their custom field types to the new UI.

Cheers!

Comments

This site uses cookies. Some of these cookies are essential, while others help us improve your experience by providing insights into how the site is being used.

For more detailed information on the cookies we use, please check our Privacy Policy.

  • Necessary cookies enable core functionality. The website cannot function properly without these cookies, and can only be disabled by changing your browser preferences.