Understanding the Types API
Multiple parts of the bundle, such as columns, filters etc. are described using the type classes. The type classes are similar to the Symfony Form Types, with small differences, making them tailored for the usage in the data tables.
Following parts of the bundle are defined the Types API:
- data tables
- columns
- filters
- actions
- exporters
Type definition
The type classes work as a blueprint that defines a configuration how its feature should work. They implement their own, feature-specific interface. For easier usage, there's also an abstract classes, which already implements the interface and provides some utilities.
| Context | Interface | Abstract class |
|---|---|---|
| Data tables | DataTableTypeInterface | AbstractDataTableType |
| Columns | ColumnTypeInterface | AbstractColumnType |
| Filters | FilterTypeInterface | AbstractFilterType |
| Actions | ActionTypeInterface | AbstractActionType |
| Exporters | ExporterTypeInterface | AbstractExporterType |
Type options
Each type class contains its own option, that can be used to configure the type according to a specific need.
The type-specific options are defined in the configureOptions() method, using the Symfony's OptionsResolver component.
Type inheritance
Similar to the form types, the type inheritance should be handled using the getParent() method.
For example, while a PhoneColumnType technically extends the TextColumnType, it should NOT extend its class:
Do NOT use PHP class inheritance!
class PhoneColumnType extends TextColumnType
{
}
Instead, it should return the FQCN of the parent type in the getParent() method:
Extend abstract type and use getParent() method instead!
class PhoneColumnType implements AbstractColumnType
{
public function getParent(): string
{
return TextColumnType::class;
}
}
The difference is about the extensions - considering the example above, while using the PHP inheritance,
a column type extensions that extend the TextColumnType won't be applied to the PhoneColumnType.
Type extension definition
The type extensions allow to easily extend existing types without creating custom classes. Those classes contain methods similar as their corresponding feature type classes. They implement their own, feature-specific interface. For easier usage, there's also an abstract classes, which already implements the interface and provides some utilities.
| Context | Interface | Abstract class |
|---|---|---|
| Data tables | DataTableTypeExtensionInterface | AbstractDataTableTypeExtension |
| Columns | ColumnTypeExtensionInterface | AbstractColumnTypeExtension |
| Filters | FilterTypeExtensionInterface | AbstractFilterTypeExtension |
| Actions | ActionTypeExtensionInterface | AbstractFilterTypeExtension |
| Exporters | ExporterTypeExtensionInterface | AbstractExporterTypeExtension |
Type extension targets
Each type extension class defines a list of types that it extends inside its static getExtendedTypes() method.
For example, if you wish to create an extension that extends a PhoneColumnType, consider following configuration:
use App\DataTable\Column\Type\PhoneColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Extension\ColumnTypeExtension;
class PhoneColumnTypeExtension extends AbstractColumnTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [PhoneColumnType::class];
}
}
To apply extension to every type in the system, use the base type of each part of the bundle, for example, in case of the column types:
use Kreyu\Bundle\DataTableBundle\Column\Type\ColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Extension\ColumnTypeExtension;
class ColumnTypeExtension extends AbstractColumnTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [ColumnType::class];
}
}
For reference, a list of each feature base type class:
| Context | Base type class |
|---|---|
| Data tables | DataTableType |
| Columns | ColumnType |
| Filters | FilterType |
| Actions | ActionType |
| Exporters | ExporterType |
Tip
Returned value is iterable type, therefore it supports returning a generator using a yield keyword:
use Kreyu\Bundle\DataTableBundle\Column\Type\ColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Extension\ColumnTypeExtension;
class ColumnTypeExtension extends AbstractColumnTypeExtension
{
public static function getExtendedTypes(): iterable
{
yield ColumnType::class;
}
}
Type resolving
Because types support inheritance & extensions, they have to be resolved before usage.
Each part of the bundle that supports the Types API contains a resolved type class:
| Context | Resolved type class |
|---|---|
| Data tables | ResolvedDataTableType |
| Columns | ResolvedColumnType |
| Filters | ResolvedFilterType |
| Actions | ResolvedActionType |
| Exporters | ResolvedExporterType |
Resolved type classes contain same methods as a non-resolved types, and handles both inheritance & extensions.
For example, take a look at implementation of the resolved data table type's buildDataTable() method:
public function buildDataTable(DataTableBuilderInterface $builder, array $options): void
{
$this->parent?->buildDataTable($builder, $options);
$this->innerType->buildDataTable($builder, $options);
foreach ($this->typeExtensions as $extension) {
$extension->buildDataTable($builder, $options);
}
}
First, the type's parent method is called, followed by the type itself, then comes the extensions.
Same flow applies to every resolved type in the bundle.
Type registry
The registry stores all the types and extensions registered in the system. Those classes can be used to retrieve a specific type or extension using their FQCN.
Each part of the bundle that supports the Types API contains its own registry:
| Context | Resolved type class |
|---|---|
| Data tables | DataTableRegistry |
| Columns | ColumnRegistry |
| Filters | FilterRegistry |
| Actions | ActionRegistry |
| Exporters | ExporterRegistry |
By default, the container is passing all the types & extensions to the registry, thanks to the Tagged Services. For reference, here's a list of each feature's tags:
| Context | Type tag | Type extension tag |
|---|---|---|
| Data tables | kreyu_data_table.type |
kreyu_data_table.type_extension |
| Columns | kreyu_data_table.column.type |
kreyu_data_table.column.type_extension |
| Filters | kreyu_data_table.filter.type |
kreyu_data_table.filter.type_extension |
| Actions | kreyu_data_table.action.type |
kreyu_data_table.action.type_extension |
| Exporters | kreyu_data_table.exporter.type |
kreyu_data_table.exporter.type_extension |
Note
Tagged services can have priority, therefore you can define the order the extensions will get loaded:
# config/services.yaml
services:
App\DataTable\Extension\ExtensionA:
tags:
- { name: kreyu_data_table.type_extension, priority: 9 }
App\DataTable\Extension\ExtensionB:
tags:
- { name: kreyu_data_table.type_extension, priority: 10 }
In the example above, the ExtensionB will be applied before the Extension A.
Without a priority specified, the extensions would be applied in the order they are registered.
For more details, see Tagged Services with Priority.