Skip to content

Create custom generic Field Type

The Generic Field Type is an abstract implementation of Field Types holding structured data for example, address. You can use it as a base for custom Field Types. The Generic Field Type comes with the implementation of basic methods, reduces the number of classes which must be created, and simplifies the tagging process.

Tip

You should not use the Generic Field Type when you need a very specific implementation or complete control over the way data is stored.

Simple hash values

A simple hash value always means an array of scalar values and/or nested arrays of scalar values. To avoid issues with format conversion, don't use objects inside the simple hash values.

Define value object

First, create Value.php in the src/FieldType/HelloWorld directory. The Value class of a Field Type contains only the basic logic of a Field Type, the rest of it is handled by the Type class. For more information about Field Type Value see Value handling.

The HelloWorld Value class should contain:

  • public properties that retrieve name
  • an implementation of the __toString() method
 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 declare(strict_types=1);

namespace App\FieldType\HelloWorld;

use Ibexa\Contracts\Core\FieldType\Value as ValueInterface;
use Symfony\Component\Validator\Constraints as Assert;

final class Value implements ValueInterface
{
    /**
     * @Assert\NotBlank()
     */
    private $name;

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(?string $name): void
    {
        $this->name = $name;
    }

    public function __toString()
    {
        return "Hello {$this->name}!";
    }
}

Define fields and configuration

Next, implement a definition of a Field Type extending the Generic Field Type in the src/FieldType/HelloWorld/Type.php class. It provides settings for the Field Type and an implementation of the Ibexa\Contracts\Core\FieldType\FieldType abstract class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php declare(strict_types=1);

namespace App\FieldType\HelloWorld;

use App\Form\Type\HelloWorldType;
use Ibexa\Contracts\ContentForms\Data\Content\FieldData;

final class Type extends GenericType implements FieldValueFormMapperInterface
{
    public function getFieldTypeIdentifier(): string
    {
        return 'hello_world';
    }
}

Tip

For more information about the Type class of a Field Type, see Type class.

Next, register the Field Type as a service and tag it with ibexa.field_type:

1
2
3
4
5
services:
    App\FieldType\HelloWorld\Type:
        public: true
        tags:
            - { name: ibexa.field_type, alias: hello_world }

Define form for value object

Create a src/Form/Type/HelloWorldType.php form. It enables you to edit the 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
<?php

declare(strict_types=1);

namespace App\Form\Type;

use App\FieldType\HelloWorld\Value;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class HelloWorldType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('name', TextType::class);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Value::class,
        ]);
    }
}

Now you can map Field definitions into Symfony forms with FormMapper. Add the mapFieldValueForm() method required by FieldValueFormMapperInterface and the required use statements to src/FieldType/HelloWorld/Type.php:

 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
<?php declare(strict_types=1);

namespace App\FieldType\HelloWorld;

use App\Form\Type\HelloWorldType;
use Ibexa\Contracts\ContentForms\Data\Content\FieldData;
use Ibexa\Contracts\ContentForms\FieldType\FieldValueFormMapperInterface;
use Ibexa\Contracts\Core\FieldType\Generic\Type as GenericType;
use Symfony\Component\Form\FormInterface;

final class Type extends GenericType implements FieldValueFormMapperInterface
{
    public function getFieldTypeIdentifier(): string
    {
        return 'hello_world';
    }

    public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data): void
    {
        $definition = $data->fieldDefinition;

        $fieldForm->add('value', HelloWorldType::class, [
            'required' => $definition->isRequired,
            'label' => $definition->getName(),
        ]);
    }
}

Tip

For more information about the FormMappers see Field Type form and template.

Next, add the ibexa.admin_ui.field_type.form.mapper.value tag to the service definition:

1
2
3
4
5
6
services:
    App\FieldType\HelloWorld\Type:
        public: true
        tags:
            - { name: ibexa.field_type, alias: hello_world }
            - { name: ibexa.admin_ui.field_type.form.mapper.value, fieldType: hello_world }

Render fields

Create a template

Create a template for the new Field Type. It defines the default rendering of the HelloWorld field. In the templates/themes/standard/field_types directory create a field_type.html.twig file:

1
2
3
{% block hello_world_field %}
    Hello <b>{{ field.value.getName() }}!</b>
{% endblock %}

Template mapping

Provide the template mapping under the ibexa.system.<scope>.field_templates configuration key:

1
2
3
4
5
ibexa:
    system:
        default:
            field_templates:
                - { template: '@ibexadesign/field_types/field_type.html.twig', priority: 0 }

Final results

Finally, you should be able to add a new content type in the Back Office interface. Navigate to Content types tab and under Content category create a new content type:

Creating new content type

Next, define a Hello World field:

Defining Hello World

After saving, your Hello World content type should be available under Content in the sidebar menu.

Creating Hello World

For more detailed tutorial on Generic Field Type follow Creating a Point 2D Field Type.