Colin Atkins

December 29, 2023

How to calculate if a checkbox is set with TYPO3 TCA type 'check'

TYPO3 offers the feature to save checkbox values into bitmasks with the TCA type 'check'. For most developers this is confusing so they switch to saving checkboxes with the TCA type 'select' instead.

However, the math behind the bitmask is actually very easy. You can find the calculation for it in the JavaScript file form-engine.js It says:

t.set("typo3-backend-form-update-bitmask", ((e,t)=>{
        const a = t.target
          , o = n.formElement[e.elementName]
          , i = a.checked !== e.invert
          , r = Math.pow(2, e.position)
          , l = Math.pow(2, e.total) - r - 1;
        o.value = i ? o.value | r : o.value & l,
        [...]
    }
));

The code is minified however its easy to understand if you break it into parts.

When someone presses a checkbox the above function is run, which effectively does the following:

  1. Check if the current checkbox which was pressed isn't equal to the invert of the currently stored checkbox value
  2. Positions index power of 2 (2^p)
  3. Total number of checkboxes power of 2 (2^t) minus the second calculation minus 1

Depending if the checkbox activation value compared to its previously saved invert value the total bitmask value is calculated with the current o.value | r or removed from the bitmask with o.value & l.

Now this is all confusing I know, but you don't have to touch the above calculation. You simple check if any of the checkboxes was set in the Fluid template. You will need a custom TYPO3 viewhelper for this, but first you'll need this fluid code:

<f:if condition="{pagedoctor.checkbox}">
        <f:if condition="{p:math.bitmask(position: '0', bitmask: '{pagedoctor.checkbox}')}">
            'Label A' was checked
            <br/>
        </f:if>
        <f:if condition="{p:math.bitmask(position: '1', bitmask: '{pagedoctor.checkbox}')}">
            'Label B' was checked
            <br/>
        </f:if>
        <f:if condition="{p:math.bitmask(position: '2', bitmask: '{pagedoctor.checkbox}')}">
            'Label C' was checked
            <br/>
        </f:if>
        <f:if condition="{p:math.bitmask(position: '3', bitmask: '{pagedoctor.checkbox}')}">
            'Label D' was checked
            <br/>
        </f:if>
        <f:if condition="{p:math.bitmask(position: '4', bitmask: '{pagedoctor.checkbox}')}">
            'Label F' was checked
            <br/>
        </f:if>
    </f:if>

You'll notice that the bitmask value stored within "pagedoctor.checked" is injected into the viewhelper with the index position of the checkbox (0 to 4).

Then you'll register the following viewhelper to see if the checkbox is saved within the bitmask value like so:

<?php declare(strict_types=1);

/*
 * Copyright 2008-2023 by Colin Atkins.
 *
 * 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 Atkins\Pagedoctor\ViewHelpers\Math;

use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;

# Helps to calculate if the current checkbox in question was set inside bitmask value.
final class BitmaskViewHelper extends AbstractViewHelper
{
    use CompileWithRenderStatic;

    protected $escapeOutput = false;

    public function initializeArguments()
    {
        $this->registerArgument('position', 'int', 'Position in question', true);
        $this->registerArgument('bitmask', 'int', 'Current bitmask value', true);
    }

    public static function renderStatic(
        array $arguments,
        \Closure $renderChildrenClosure,
        RenderingContextInterface $renderingContext
    ) {
        $position = intval($arguments['position']);
        $bitmask = intval($arguments['bitmask']);
        $positionBit = pow(2, $position);
        return ($bitmask & $positionBit) > 0;
    }
}

The above code is part of the next release of the Pagedoctor extension code so you can use it out of the box there.

— Colin Atkins