Skip to content

Commit

Permalink
[FEATURE] Field Type SelectNumber (#312)
Browse files Browse the repository at this point in the history
Content Blocks now has a field type "SelectNumber".
This is a more specific version of type "Select" which only
allows integer values. The benefit of this type is that
the database column is created as type "int", thus saving
precious db row size.

Fixes: #311
  • Loading branch information
nhovratov authored Dec 6, 2024
1 parent 0b23feb commit d926843
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 4 deletions.
100 changes: 100 additions & 0 deletions Classes/FieldType/SelectNumberFieldType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace TYPO3\CMS\ContentBlocks\FieldType;

/**
* @internal Not part of TYPO3's public API.
*/
#[FieldType(name: 'SelectNumber', tcaType: 'select')]
final class SelectNumberFieldType extends AbstractFieldType
{
use WithCommonProperties;

private string|int $default = '';
private bool $readOnly = false;
private int $size = 0;
private string $authMode = '';
private bool $disableNoMatchingValueElement = false;
private array $itemGroups = [];
private array $items = [];
private array $sortItems = [];

public function createFromArray(array $settings): SelectNumberFieldType
{
$self = clone $this;
$self->setCommonProperties($settings);
$default = $settings['default'] ?? $self->default;
if (is_string($default) || is_int($default)) {
$self->default = $default;
}
$self->readOnly = (bool)($settings['readOnly'] ?? $self->readOnly);
$self->size = (int)($settings['size'] ?? $self->size);
$self->authMode = (string)($settings['authMode'] ?? $self->authMode);
$self->disableNoMatchingValueElement = (bool)($settings['disableNoMatchingValueElement'] ?? $self->disableNoMatchingValueElement);
$self->itemGroups = (array)($settings['itemGroups'] ?? $self->itemGroups);
$self->items = (array)($settings['items'] ?? $self->items);
foreach ($self->items as $item) {
$value = $item['value'] ?? null;
if (!is_int($value)) {
throw new \InvalidArgumentException(
'Item values for Content Blocks field type "SelectNumber" must be integers. Got "' . $value . '" (' . gettype($value) . ').',
1733310237
);
}
}
$self->sortItems = (array)($settings['sortItems'] ?? $self->sortItems);

return $self;
}

public function getTca(): array
{
$tca = $this->toTca();
$config['type'] = $this->getTcaType();
$config['renderType'] = 'selectSingle';
if ($this->default !== '') {
$config['default'] = $this->default;
}
if ($this->readOnly) {
$config['readOnly'] = true;
}
if ($this->size > 0) {
$config['size'] = $this->size;
}
if ($this->authMode !== '') {
$config['authMode'] = $this->authMode;
}
if ($this->disableNoMatchingValueElement) {
$config['disableNoMatchingValueElement'] = true;
}
if ($this->itemGroups !== []) {
$config['itemGroups'] = $this->itemGroups;
}
$config['items'] = $this->items;
if ($this->sortItems !== []) {
$config['sortItems'] = $this->sortItems;
}
$tca['config'] = array_replace($tca['config'] ?? [], $config);
return $tca;
}

public function getNonOverridableOptions(): array
{
return ['renderType', 'items'];
}
}
19 changes: 15 additions & 4 deletions Classes/Generator/TcaGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use TYPO3\CMS\ContentBlocks\Definition\TCA\LinebreakDefinition;
use TYPO3\CMS\ContentBlocks\Definition\TCA\TabDefinition;
use TYPO3\CMS\ContentBlocks\Definition\TcaFieldDefinition;
use TYPO3\CMS\ContentBlocks\FieldType\FieldTypeInterface;
use TYPO3\CMS\ContentBlocks\FieldType\FlexFormFieldType;
use TYPO3\CMS\ContentBlocks\Registry\LanguageFileRegistry;
use TYPO3\CMS\ContentBlocks\Schema\SimpleTcaSchemaFactory;
Expand Down Expand Up @@ -380,9 +381,9 @@ protected function processShowItem(array $showItemInput): string
*
* @return string[]|array{type: string, option: string}
*/
protected static function getNonOverridableOptions(): array
protected static function getNonOverridableOptions(FieldTypeInterface $fieldType): array
{
return [
$nonOverridableOptions = [
'type',
'relationship',
'dbType',
Expand Down Expand Up @@ -416,14 +417,23 @@ protected static function getNonOverridableOptions(): array
'l10n_mode',
'dbFieldLength',
];
$fieldNonOverridableOptions = [];
// @todo experimental. Could be added as interface method later.
if (method_exists($fieldType, 'getNonOverridableOptions')) {
$fieldNonOverridableOptions = $fieldType->getNonOverridableOptions();
}
$mergedNonOverridableOptions = array_merge($nonOverridableOptions, $fieldNonOverridableOptions);
return $mergedNonOverridableOptions;
}

protected function getColumnsOverrides(ContentTypeInterface $typeDefinition, TableDefinition $tableDefinition): array
{
$columnsOverrides = [];
foreach ($typeDefinition->getOverrideColumns() as $overrideColumn) {
$overrideTca = $overrideColumn->getTca();
foreach (self::getNonOverridableOptions() as $option) {
$fieldType = $overrideColumn->getFieldType();
$nonOverridableOptions = self::getNonOverridableOptions($fieldType);
foreach ($nonOverridableOptions as $option) {
$optionKey = $this->getOptionKey($option, $overrideColumn);
if ($optionKey === null) {
continue;
Expand Down Expand Up @@ -490,7 +500,8 @@ protected function getColumnTcaForTableWithTypeField(TableDefinition $tableDefin
// FlexForm "ds" can be extended without columnsOverrides.
$extensibleOptions = ['ds'];
$columnTca = [];
$iterateOptions = $column->useExistingField() ? $extensibleOptions : self::getNonOverridableOptions();
$fieldType = $column->getFieldType();
$iterateOptions = $column->useExistingField() ? $extensibleOptions : self::getNonOverridableOptions($fieldType);
foreach ($iterateOptions as $option) {
$optionKey = $this->getOptionKey($option, $column);
if ($optionKey === null) {
Expand Down
21 changes: 21 additions & 0 deletions Documentation/ChangeLog/1.1/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
1.1
===

.. contents::

Features
========

Expand Down Expand Up @@ -79,6 +81,25 @@ else, you can override the default folder by providing the option
vendor/bin/typo3 make:content-block --skeleton-path="my-alternative-skeleton-path"
Field Type SelectNumber
-----------------------

A new field type :ref:`SelectNumber <field_type_select-number>` is added. This
new type allows to have a select field with exclusively integer values. The
database column will also have type :sql:`int`, which saves precious row size.

.. code-block:: yaml
name: example/select-number
fields:
- identifier: select_number
type: SelectNumber
items:
- label: 'The first'
value: 1
- label: 'The second'
value: 2
Deprecations
============

Expand Down
1 change: 1 addition & 0 deletions Documentation/YamlReference/FieldTypes/Index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Simple Field Types:
* :ref:`Number <field_type_number>`
* :ref:`Password <field_type_password>`
* :ref:`Radio <field_type_radio>`
* :ref:`SelectNumber <field_type_select-number>`
* :ref:`Slug <field_type_slug>`
* :ref:`Text <field_type_text>`
* :ref:`Textarea <field_type_textarea>`
Expand Down
144 changes: 144 additions & 0 deletions Documentation/YamlReference/FieldTypes/SelectNumber/Index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
.. include:: /Includes.rst.txt
.. _field_type_select-number:

============
SelectNumber
============

The :yaml:`SelectNumber` type generates a simple select field, which only allows
numbers / integers.

Settings
========

.. confval-menu::
:name: confval-select-number-options
:display: table
:type:
:default:
:required:

.. confval:: default
:name: select-number-default
:required: false
:type: integer

Default value set if a new record is created.

.. confval:: items
:name: select-number-items
:required: false
:type: array

Contains the elements for the selector box. Each item is an array. An item
consists of a :yaml:`label` and a :yaml:`value`.

Example:

.. code-block:: yaml
items:
- label: 'The first'
value: 1
- label: 'The second'
value: 2
- label: 'The third'
value: 3
.. tip::

You can omit the label, if you have the translation already in your
labels.xlf file.

.. code-block:: yaml
items:
- value: 1
- value: 2
- value: 3
.. tip::

You can also use icons so they are displayed in the backend.
See :ref:`select-number-icons` for a full example.

.. code-block:: yaml
items:
- value: 1
icon: content-beside-text-img-left
- value: 2
icon: content-beside-text-img-right
- value: 3
icon: content-beside-text-img-above-center
For this you need the following setting according to the :ref:`TCA documentation <t3tca:tca_property_fieldWizard_selectIcons>`.

.. code-block:: yaml
fieldWizard:
selectIcons:
disabled: false
XLF translation keys for items have the following convention:

.. code-block:: xml
<body>
<trans-unit id="FIELD_IDENTIFIER.items.1.label">
<source>Label for item with value one</source>
</trans-unit>
<trans-unit id="FIELD_IDENTIFIER.items.2.label">
<source>Label for item with value two</source>
</trans-unit>
<trans-unit id="FIELD_IDENTIFIER.items.VALUE.label">
<source>Label for item with value VALUE</source>
</trans-unit>
</body>
Example
=======

Minimal
-------

.. code-block:: yaml
name: example/select-number
fields:
- identifier: select_number
type: SelectNumber
items:
- label: 'The first'
value: 1
- label: 'The second'
value: 2
Advanced / use case
-------------------

.. _select-number-icons:

Select with icons:

.. code-block:: yaml
name: example/select-number
fields:
- identifier: select_number_icons
type: SelectNumber
fieldWizard:
selectIcons:
disabled: false
default: 2
items:
- label: 'Image beside text (left)'
value: 1
icon: content-beside-text-img-left
- label: 'Image beside text (right)'
value: 2
icon: content-beside-text-img-right
- label: 'Image above text (center)'
value: 3
icon: content-beside-text-img-above-cent
Loading

0 comments on commit d926843

Please sign in to comment.