DEPRECATION WARNING

This documentation is not using the current rendering mechanism and is probably outdated. The extension maintainer should switch to the new system. Details on how to use the rendering mechanism can be found here.

EXT: Dynamic FlexForms

Author:Thomas Hempel
Created:2005-07-14T21:31:03
Changed:2007-08-17T20:27:23
Email:thomas@work.de
Info 2:
Info 3:
Info 4:

EXT: Dynamic FlexForms

Extension Key: dynaflex

Copyright 2005 - 2007, Thomas Hempel, <thomas@work.de>

This document is published under the Open Content License

available from http://www.opencontent.org/opl.shtml

The content of this document is related to TYPO3

- a GNU/GPL CMS/Framework available from www.typo3.com

Table of contents

EXT: Dynamic FlexForms 1

Introduction 1

What does it do? 1

Basics 1

DCA 2

Paths 2

Where to instantiate 2

The recommend way 2

The old fashioned way 3

How configuration is processed 4

Cleanup the data 4

Using hooks to alter configurations 4

Configuration 5

Where the DCA comes from 5

$DCA 5

Single Configuration Array (SCA) 5

Modification Array (MA) 5

-> method 6

-> type 7

-> conditions 7

-> if 7

-> source 8

-> source_type 8

-> source_config 8

-> field_config 8

-> sheet_config 9

-> getLabel 9

HOW-TO 10

Basics (an introduction) 10

Adding a simple field 10

Adding multiple fields 11

Adding fields depending on sources 11

Adding some sheets from an input field 11

Moving fields to a sheet 12

Load DCA from Tsconfig 12

Configure DynaFlex to load TS and no class 12

Write TypoScript 12

Known problems 13

To-Do list 13

Changelog 13

Introduction

What does it do?

Dynaflex gives you the possibility to change an array defined by a configuration. That doesn't sounds very cool but it can handle XML data structures inside of such an array. That makes it possible to change the TCA on the fly. The result is, that you can change the TCA before it's displayed in the backend. This modifications are configurable and can depend on entries in database or formfields inside a FlexForm. For example, you can realize that a tab divider is only visible if at least one fe_user exists.

You can insert fields into a FlexForm data structure depending on the number of entries in a specific table. For example, create an input field for every entry in the table be_users that are not hidden.

Actually you can do anything you want, if a functionality is not implemented in the dynaflex extension, you can call a userfunction, that returns a TCA configuration for example.

Basics

Here I will explain some basic things that are used in the context of this extension manual. You should read this carefully to understand what I'm talking about in the rest of this document.

The dynaflex extension is not for end users but for developers!

DCA

DCA stands for D ynaflex C onfiguration A rray. It's very similar to the TCA. If you understood the scheme of the TCA, understanding the DCA shouldn't be a problem. :-)

Paths

When I'm talking about paths, I always mean a path inside of an array. In most cases this will be inside the TCA, but it also can be inside a FlexForm data structure (which is also an array after all).

If you specify a path, you do it the same way as you'll do it on *NIX systems. Every key inside the array is like a folder in a file system. There is only one difference to a file system, the path doesn't start with a slash! For example, if you have the following array:

TCA

a

TCA

b

element1

c

subelement1

d

a

sub1sub1field1

b

subsub1value1

a

sub1sub1field2

b

subsub1value2

subelement2

a

subelement2

b

subsub2field1

c

subsub2value1

element2

a

element2

b

sub2field1

c

sub2value1

d

sub2field2

a

sub2field2

b

sub2value2

sub2field3

a

sub2field3

b

sub2value3

sub2field4

a

sub2field4

b

sub2value4

If you want to edit the field “subsub1value1” the path to this field will be:

TCA/element1/subelement1/sub1sub1field1
Where to instantiate

Before the dynaflex extension will do anything, you have to configure it (section “Configuration”) and than you have to instantiate and call it at the right position of the TYPO3 TCA processing.

Since version 1.7.0 this has be improved dramatically!

Since that version, DynaFlex implements all needed hooks by itself. This has various advantages. On the one hand, you don't have to register the hooks by your own. And the second one is, that through the way of implementation, you don't have to think about how DynaFlex has to be called. And last but not least, with version 1.7.0 I introduced hooks for config loading. That means, that you can alter the DCA of another extension by using a hook that is called while the configuration is loaded.

The old way of calling will work as usual of course, but it is highly recommend to use the new way of calling DynaFlex!!!

The recommend way

Since version 1.7.2

You have to create a new class for your configuration. Just create file that can be called like class.tx_dynaflextut_dfconfig.php and place a class in it that fits the usual naming conventions. Inside of this class you just have to define to variables.

  • $rowChecks – Defines some rules when the DCA should be executed. (since version 1.9.0!!!)
  • $DCA – Contains the DCA for your table.
  • $cleanUpField – This defines the table for which the flexform is defined. This field is cleaned up at the end of the modifying process.
  • $hooks – May contain hooks for altering the DCA for a table

Such a file can look like this:

class tx_dynaflextut_dfconfig {
  var $rowChecks = array (
            // add your checks here
  );

    var $DCA = array (
            0 => array (
                    'path' => 'tx_dynaflextut_test/columns/flexdata/config/ds/default',
                  'modifications' => array (
                           --- snip ---
                  )
          )
  );

        var $cleanUpField = 'flexdata';

    var $hooks = array(
            'EXT:dynaflex_tut/class.tx_dynaflextut_dcahooks.php:tx_dynaflextut_dcahooks'
  );
}

You now may ask, where the table is defined that DynaFlex knows when it should call itself. This is defined in the registration. Open the ext_localconf.php of your extension and register your config class like this:

$GLOBALS['T3_VAR']['ext']['dynaflex']['tx_dynaflextut_test'][] = 'EXT:dynaflex_tut/class.tx_dynaflextut_dfconfig.php:tx_dynaflextut_dfconfig';

As you can see, in the registration path you have to define for which table you want to register the configuration. In this case we're registering it for the table “tx_dynaflextut_test”.

If you don't submit a path to a class but the simple string “TS”, DynaFlex will load the DCA not from a class but from the Tsconfig of the page. See section configuration.

The old fashioned way

I recommend to use DynaFlex with TYPO3 version 3.8.0 and higher. There is one hook available where the flexform processing should be started. This hook can be found in the befunc class and is called “getFlexFormDS_postProcessDS”. This is processed whenever a flexform field in the backend is processed. It's highly recommend to use this hook because only with this hook it's possible to create file upload fields (type “group”). I will not try to explain why it is so but it takes me a few hours to find it out. ;-)

Anyway, at first you have to register your hook method. This is normally done in the ext_localconf.php of your extension. The code should look similar to this snippet:

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['getFlexFormDSClass'][] = 'EXT:dynaflex_tut/class.tx_dynaflextut_befunc_hooks.php:tx_dynaflextut_befunc_hooks';

This code comes from the dynaflex_tut extension that is availble from the TER. The hook code looks like this:

class tx_dynaflextut_befunc_hooks  {
        function getFlexFormDS_postProcessDS(&$dataStructArray, $conf, $row, $table, $fieldName)    {
                global $counter;
                $counter++;

                if ($table == 'tx_dynaflextut_test'     &&
                        t3lib_extMgm::isLoaded('dynaflex') &&
                        $counter <= 1
                )       {
                                // Load the configuration from a file
                        require_once(t3lib_extMgm::extPath('dynaflex_tut') .'df_config.php');

                                // And start the dynaflex processing
                        require_once(t3lib_extMgm::extPath('dynaflex') .'class.dynaflex.php');
                        $dynaflex = t3lib_div::makeInstance('dynaflex');

                                // debug($GLOBALS['TCA']['tx_dynaflextut_test']);
                        $dynaflex->init($GLOBALS['TCA'], $GLOBALS['T3_VAR']['dynaflex']['dca']);

                                // process DCA and read dataStructArray from index 0
                        $GLOBALS['TCA'] = $dynaflex->getDynamicTCA();
                        $dataStructArray = $dynaflex->dataStructArray[0];

                                // at last cleanup the XML structure in the database
                        $dynaflex->doCleanup('flexdata');
                }
        }
}

This code works only with version 1.5.0 and higher!!!

Most things should be clear in this code. Maybe you wonder about the $counter. This workaround is needed to avoid that dynaflex processes the config more than one time. This is needed, because the code is called every time a single flexform field is processed. So we count the number fields that where already processed and call dynaflex only if we are in the first cycle. That is enough to build up the dynamic flexform structure.

But to be honest, there is one bid disadvantage of this call. If you use a fieldvalue a source for one or more modifications, the changes take effect after second save and not after the first click on “save”. To avoid this, you can use another hook in the tceforms class. This hook is called “getMainFields_preProcess”. The registration and call of this hook is likely the same than the befunc hook. But keep in mind that if you use this tceforms hook that dynaflex is processing the modifications twice. I will work on a proper solution for this odd behavior.

Register tceform hook:

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tceforms.php']['getMainFieldsClass'][] = 'EXT:dynaflex_tut/class.tx_dynaflextut_tceforms_hooks.php:tx_dynaflextut_tceforms_hooks';

Tceforms hook:

function getMainFields_preProcess($table, $row, $pObj)     {
        if ($table == 'tx_dynaflextut_test'     &&
                t3lib_extMgm::isLoaded('dynaflex')
        )       {
                        // Load the configuration from a file
                require_once(t3lib_extMgm::extPath('dynaflex_tut') .'df_config.php');
                                // And start the dynaflex processing
                require_once(t3lib_extMgm::extPath('dynaflex') .'class.dynaflex.php');
                $dynaflex = t3lib_div::makeInstance('dynaflex');

                $dynaflex->init($GLOBALS['TCA'], $GLOBALS['T3_VAR']['dynaflex']['dca']);
                $GLOBALS['TCA'] = $dynaflex->getDynamicTCA();

                $dynaflex->doCleanup('flexdata');
        }
}
How configuration is processed

The configuration of dynaflex is processed in the same order as it's keys! That means that you have to keep track of the keys you give your configurations. This is good to know, because you can reference to field you have dynamically created in a modfication step before. I know that sounds a little bit coated but believe me, it can be useful. :-)

Regulate execution

In some cases you might register DynaFlex for general table like tt_content. This is the case if you want to modify the flexform of a frontend plugin. The problem is, that DynaFlex will execute the modification every time tt_content is loaded. This means for every content element!

Since version 1.9 you can define some restriction to make sure that DynaFlex will only start if the dataset we're working on accomplishes some rules. These rules or conditions can be defined in the configuration class in a variable called $rowChecks.

$rowChecks is an array of key value pairs where the keys are the fieldnames in the table we are working on and the values are the values the tablefield should have. If any of the tablefields does not have the value that is set in the $rowChecks array, the complete DCA won't be executed!

Here is an example. This checks if the CType of the tt_content element is 'list' (Insert Plugin) and that the selected plugin is 'dynaflex_tut_pi1':

var $rowChecks = array (
    'list_type' => 'dynaflex_tut_pi1',
    'CType' => 'list'
);
Cleanup the data

Since version 1.4.0 you can force a cleanup of your data in the database. The problem is, that the values of a flexform are stored in a XML structure which holds each field you have ever had in your flexform. That is not a problem if the flexforms are static. If you use DynaFlex, fields can disappear from a form and so the data in the database is holding data for a field that doesn't exist.

Since version 4.0 of TYPO there is a class called “flexformtools” which provides a method that removes all flexform fields from the datastructure that are not present in the TCA. In DynaFlex this method is used to provide a method that cleans up the database. This method also works for pre 4.0 version of TYPO3. DynaFlex ships with a copy of this flexformtools class.

All you have to do is, to call the method “doCleanup” after you have written back the TCA. The following code snippet shows this:

1: $dynaflex->init($GLOBALS['TCA'], $dca);
2:
3:     // write back the modified TCA
4: $GLOBALS['TCA'] = $dynaflex->getDynamicTCA();
5:
6: $dynaflex->doCleanup('flexdata');

The argument you pass to this method is the field of the content element that is used to store the data XML. In this case the field is called “flexdata”. This is also the field for which the flexform structure is defined in the TCA.

This is obsolete with version 1.7.0! The cleanup is done automatically by DynaFlex if you use the recommend way of calling it.

Using hooks to alter configurations

Since the new way of calling DynaFlex was introduced it's possible to register hooks that can alter the DCA of other extensions. The only condition is, that the author of the orginal extension has used the new way. If he has not, the hooks are normally not available.

The way of registering a hook is the same as you might know it from TYPO3 itself. Only the location is a different one. If you take a look at the section before (Where to instanciate – the recommend way) you'll find an example config class for DynaFlex. There you can see that the hooks are registered inside that config classes in the variable $hooks. Just enter the path to your hook class in such a config class as new array element and register the class for the targeted table.

Now everytime time, DynaFlex is processing this table, it will load the regsitered DCA and will look for all hooks that might be registered for it.

Keep in mind that it makes in which order the extensions are installed. The extension that provides the orginal DCA has to be installed before any extension that provides altering hooks!!!

Inside the hook class you have to define a methd called alterDCA_onLoad . This method will receive two arguments. The first one is the DCA with all modifications at the time the hook is called. This variable is called by reference! So you your method don't have to return anything. Just change the DCA.

The second parameter is the name of the table we are working on. This is not really needed because you should know for which table you have regsitered your hook but it makes it easier to change the tablename later on if you use it in the alter method. An example hook could look like this:

function alterDCA_onLoad($currentDCA, $table)       {
          // store the last modification (the move)
  $lastIndex = count($currentDCA[0]['modifications']) -1;
  $lastModification = $currentDCA[0]['modifications'][$lastIndex];

          // write the new one
  $currentDCA[0]['modifications'][$lastIndex] = array (
          'method' => 'add',
          'path' => 'ROOT/el',
          'type' => 'field',
          'field_config' => array (
                  'name' => 'df_field_fromHook',
                  'label' => 'A field from a hook',
                  'config' => array (
                          'type' => 'input'
                        )
                )
        );

          // and append the former last one at the end again
  $currentDCA[0]['modifications'][] = $lastModification;
}

Configuration

As I mentioned above, the dynaflex extension is configurable through an array called DCA. These DCA can beconfigured on various places in your extension or the TYPO3 installation.

Where the DCA comes from

The first possibility is to write the DCA directly into the tca.php of your extension. This is was is obsolete and strongly recommended to not do it this way!

The second way to provide a DCA is from within a DCA class. This is described in section “Where to instantiate” earlier in this document. This is the most common way to configure DynaFlex.

A third way is to provide the DCA via TypoScript. If you don't provide a class in the ext_localconf.php file but a simple “TS”, DynaFlex will not try to load a class but fetches the Tsconfig from the current page. DynaFlex tries to load the path “dynaflex.[tablename]”.

$DCA

The DCA itself consists of an unlimited number of subarrays. Every single one of this arrays stands for modifications on a single element inside of an array. A field inside of a backend form for example.

We (I) call this a single configuration array (SCA).

There is only one special Key in the DCA:

workingTable

Key

workingTable

Datatype

string

Description

With this option you explicit define on which table you want to work. This is useful if you have a special multi table view like columnsOnly. If you don't set this, the workingTable is detected automatically.

Single Configuration Array (SCA)

The SCA contains some meta information of what should be done and an array of modifications. This table is an overview of the keys in the SCA:

path

Key

path

Datatype

path (string)

Description

The path inside the array that should be modified. See section “Basics > Paths”

parseXML

Key

parseXML

Datatype

boolean

Description

If this is true, the data that is located inside the path will be passed through the xml2array method of TYPO3 before anything is done with the fielddata. If it is false, nothing is done with the data, and it's passed as is to through the rest of the processing.

The default value is true.

This is needed for some kinds of modification, for example if you want to add simple text in the “showitem” field of the TCA which is not XML at all.

modifications

Key

modifications

Datatype

array of ->MA

Description

An array of modification arrays. See section “$DCA > Modification Array”

uid

Key

uid

Datatype

integer

Description

OBSOLETE!

This is not needed anymore! Dynaflex gets this UID from the runtime environment when it is called.

Modification Array (MA)

The modification array (MA) is the array where all the configuration is stored how the array should be modified. This table is an overview of the keys in the MA:

method

Key

method

Datatype

-> method

Description

This string names the method that is used for modifying the current element.

Valid for method

type

Key

type

Datatype

-> type

Description

A detailed description of the method. (see -> type)

Valid for method

add, move

condition

Key

condition

Datatype

-> conditions

Description

DEPRECATED! If you only have a single condition that should be checked, you can use this option. It's recommend to use the key “conditions” instead!

Valid for method

all

conditions

Key

conditions

Datatype

-> conditions

Description

Defines in witch cases a modification is performed or not.

For a detailed description of the possible keys for a condition (see -> conditions).

Valid for method

all

source

Key

source

Datatype

-> source

Description

Defines where the base data, for creating the fields, is fetched from. (see -> source)

Valid for method

add

source_type

Key

source_type

Datatype

-> source_type

Description

The type of the source. (see -> source_type)

Valid for method

add

source_config

Key

source_config

Datatype

-> source_config

Description

The configuration how the source is fetched. (see -> source_config)

Valid for method

add

field_config

Key

field_config

Datatype

array of -> field_config

Description

An array of field configurations. That means that you can configure as much fields as you want, and for every element of the source ALL of this configurations will be performed.

For example you can add two fields for every entity inside of an arbitrary table.

Valid for method

add

sheet_config

Key

sheet_config

Datatype

-> sheet_config

Description

Configures how a sheet is inserted into the FlexForm

Valid for method

add

allUserFunc

Key

allUserFunc

Datatype

string

Description

This user function is called when all data was fetched. The data that was fetched is passed to the user function.

Dynaflex expects an array with configurations that fit the definition of “field_config” as result from the userfunction.

Valid for method

add (types: fields)

path

Key

path

Datatype

path (string)

Description

The path inside the FlexForm XML structure where the fields should be added.

Valid for method

add

inside

Key

inside

Datatype

path (string)

Description

The path inside the FlexForm, where the routines for adding staticXML are working in. For a simple FlexForm this would be:

ROOT/el

If you want insert staticXML inside of an existing sheet (which can be created by modification before this one!) the path could be something like:

sheets/mySheet/ROOT/el

Valid for method

add (types: staticXML)

beforeAll

Key

beforeAll

Datatype

integer

Description

If this is set, the data is inserted before all other elements inside the path that was configured by “inside”.

Valid for method

add (types: staticXML)

after

Key

after

Datatype

string

Description

The name of the element, after which the data should be inserted. The frame in which this is performed (FlexForm or single sheet) has to be configured by “inside”.

Valid for method

add (types: staticXML)

data

Key

data

Datatype

string

Description

The XML data that insert if the type is staticXML

Valid for method

add (types: staticXML)

label

Key

label

Datatype

->getLabel

Description

Defines the label for an inserted sheet.

Valid for method

add (types: sheet, sheets)

name

Key

name

Datatype

string

Description

Defines the name for an inserted sheet.

Valid for method

add (types: sheet)

element

Key

element

Datatype

string

Description

The name of the element (field) for which an action should be performed.

Valid for method

remove

elements

Key

elements

Datatype

array

Description

If you want to perform more than one modification for a single condition you can use this key to collect all modifications here. This is just for performance issues because the condition is only checked one time.

Valid for method

all

config

Key

config

Datatype

array

Description

Normal field configuration like it's used in TCA and -> field_config -> config.

If it used for type “append” this array looks like:

$config = array ( text => 'my text to append' );

Valid for method

add (types: field, append)

table

Key

table

Datatype

string

Description

Identifies a section inside the TYPO3_CONF_VARS array.

Valid for method

move (types: extraFields)

-> method

Type of method that is used for modification

add

Type

add

Description

Instructs the dynaflex extension to add something somewhere.

move

Type

move

Description

Moves an element from one position to another.Since version 0.2.0 it also can move fields inside of the showitem section of the TCA.

remove

Type

remove

Description

Removes (deletes) an element from the array

function_call

Type

function_call

Description

Calls a user defined method. The dynaflex extension passes the uid of the dataset the class is processing. (If it is set in the SCA) and the complete data of the field the class is processing. (The field is defined in the SCA and is normally an array with a FlexForm data structure.)

The format how the function has to be defined is the same than in the TCA (see user defined fields). It's something like:

tx_myext_myclass->myUserFunction

It's up to you, that the class file is included as soon as the function is instantiated and the method is called.

-> type

The type defines what method should be performed exactly. This table is an overview of the possible types for every method (Currently only types for method “add” are implemented!):

staticXML

Type

staticXML

Description

Inserts a static XML at a specific position inside the source. The position can be configured by MA->beforeAll and MA->after.

The data that inserted is fetched from MA->data.

Method

add

sheet

Type

sheet

Description

Inserts a new sheet inside of an FlexForm. The label is fetched from MA->label and the name from MA->name.

Method

add

sheets

Type

sheets

Description

Inserts a number of sheets depending on source_config.

Method

field

Type

field

Description

Adds a single field. It's really simple. All you have to configure is MA->config and MA->label.

Method

add

fields

Type

fields

Description

Adds more fields. This can be used to create fields in dependency to database contents. See:

  • MA->source
  • MA->source_type
  • MA->source_config
  • MA->field_config

Method

add

extraFields

Type

extraFields

Description

If the type “extraFields” is set with method “move”. Dynaflex will fetch a list of fieldnames from the TYPO3_CONF_VARS array a parses them to modify the showitems section in the TCA.

Method

move

append

Type

append

Description

Appends some text after the content that is defined in DCA->path.

An exampe for this can be adding additional tab dividers to a FlexForm. Such a configuration could look like:

$DCA = array (
  array (
    'path' => 'tx_myext/types/0/showitem',
    'parseXML' => false,
    'modifications' => array (
      array (
        'method' => 'add',
        'type' => 'append',
        'config' => array (
          'text' => ',--div--;divider 1,myField;;;;1-1-1',
        ),
      ),
    ),
  ),
);

Method

add

-> conditions

The condition decides if a modification should be performed or not.

if

Key

if

Datatype

->if

Description

the kind of condition that is checked.

source

Key

source

Datatype

string

Description

This defines what should be used as base for the condition. Possible values are:

  • language (The language of the current content element e.g. DEF, DE, DK etc.)
  • pid (The uid of the page the content element is located on)
  • db (The default if nothing is set, some data from the database). This requires “table”, “select” and “where” This only fetches the first row from the result set! Keep that in mind if you want to use the condition data as source for further operations.
  • cce (Current Content Element, requires cefield. This method has the big advantage, that you can use values from the dataset right away after saving twice. Fixes an annoying issue from older versions. Since version 1.10.0 )
table

Key

table

Datatype

string (SQL)

Description

The table where the compare value is selected from.

select

Key

select

Datatype

string (SQL)

Description

The fields that are selected for comparison.

For some kinds of “if”, this have to be a SINGLE field in the table.

where

Key

where

Datatype

string (SQL)

Description

The condition for selection. You can use the marker “###uid###” here wich is substituted with the UID of the current content element.

cefield

Key

cefield

Datatype

string

Description

Defines the field from which data is fetched if source is “cce”.

isXML

Key

isXML

Datatype

boolean

Description

If this is true, the result from the database is handled as XML. That means, that the result is converted into an array. Otherwise the data is handled as it is.

path

Key

path

Datatype

path (string)

Description

If isXML is true and the data value for the comparison is fetched from this path inside of the array that was converted from the XML.

Important! This path is something different than the flexform structure. If you fetch data from a flexform data field you'll get a flexform data-structure which is pretty different. A path could look like this for example:

data/sheet_0/lDEF/df_field_0/vDEF
compareTo

Key

compareTo

Datatype

mixed

Description

This is the value the value from the database is compared to or a regular expression.

This key is used for the “if” types:

  • isLess
  • isEqual
  • isGreater
  • notEqual
  • regex
-> if

You have to configure when a condition is true or false. Normally you fetch some data from a database and compare the result of that query with a fixed value. How this comparison is done, is decided by if .

hasValues

Type

hasValues

Description

Returns true if the select query returns anything.

isLess

Type

isLess

Description

Returns true if the result from the database is less than the “compareTo” value. This only works, if the result of the database is an integer!!!

isEqual

Type

isEqual

Description

Returns true if the result from the database equals the “compareTo” value. This should work with strings and integers.

notEqual

Type

notEqual

Description

Returns true if the result does NOT equal the “compareTo” value.

isGreater

Type

isGreater

Description

Returns true if the result from the database is bigger than the “compareTo” value. This only works, if the result of the database is an integer!!!

regex

Type

regex

Description

Returns true if the regular expression in “compareTo” matches. This can be useful if you want to check for a substring for example.

-> source

If you're adding fields, you have various possibilities what is used as base for creating that fields. This table gives you an overview about the possible sources for creating the fields.

db

Type

db

Description

The data is fetched from the database. The detailed configuration how the data database query is built configured by “source_config”. (see -> source_config)

conditions

Type

conditions

Description

The data is the same as that result that is fetched from the database that was needed to check the condition. Make sure, that you have a proper configured condition! (see -> condition)

field

Type

field

Description

The data is fetched from any field inside the form. It doesn't matter if the field is defined in the TCA and has it's own field in the database or if the field is defined inside a flexform datastructure. (see -> source_config)

*

Type

*

Description

A free text. In most cases this should be a comma separated list. (see -> source_type)

-> source_type

With the source_type you can configure how the basedata for creating the fields is pre-processed. This is needed to make sure that the routines for creating fields are getting the right data format.

csl

Type

csl

Description

Comma Separated List. Mostly used if the value of -> source is a manual entered text. (see -> source)

int

Type

int

Description

Treats the source data as integer and converts into a integer.

db_row

Type

db_row

Description

This is needed if you fetch more than a single value as source and fetch the data from a condition. It takes the data and creates a one dimensional array from it.

entry_count

Type

entry_count

Description

The data is used as they come in. This is the default, so you don't have to set “source_type” if you want to use this type.

-> source_config

Configuration, how the data is fetched from a source. This depends on the type of source (Key “source” in MA). This table is an overview of the keys that can be used in this configuration:

table

Key

table

Datatype

string (SQL)

Description

The table where the data is fetched from. If this is not set, the data will be fetched directly from the current dataset. This is also the case if the configured table is the same as the table, DynaFlex is working on.

usable for source

db, field

select

Key

select

Datatype

string (SQL)

Description

The select statement. Which fields should be fetched from the table.

usable for source

db

where

Key

where

Datatype

string (SQL)

Description

The condition for selecting the datasets.

usable for source

db

db_field

Key

db_field

Datatype

string

Description

The field inside the db if you want to use a field outside of flexforms.

usable for source

field

path

Key

path

Datatype

path (string)

Description

The path to the field inside of the flexform structure. Without fieldname!

usable for source

field

xml_field

Key

xml_field

Datatype

string

Description

The name of the field inside the flexform structure. Only needed if path is set

usable for source

field

-> field_config

The field_config describes what kind of field should be created.

In the keys “name” and “label” some replacements will be performed

path

Key

path

Datatype

path (string)

Description

The path inside the FlexForm where the new field should be placed in. If no sheets are defined, the normal path would be “ROOT/el”.

name

Key

name

Datatype

string

Description

The name of the field. This has to be unique on the FlexForm.

If you have a source defined you can use some markers in the name string that will be replaced before the form is rendered.

There two static markers ###FINDEX### which is replaced with the index of the fieldconfig starting with 0. If you have defined more than one field in the field_config (add => fields) the ###FINDEX### is the same for each field.

The other static marker is the ###DATA### marker. This thing is replaced if you have a field as source. E.g. If you create a number of fields depending on a value of a field, the ###DATA### marker is replaced with that value. If the source data is a dataset from the database. ###DATA### will also be replaced with the same value as ###FINDEX###

The two static markers are implemented since version 1.2.0!!!

Additionally to this this two static markers you can set each field of the source data as marker. For example if you have tt_address datasets as source, you can use markers like ###uid###, ###name###, ###city### and so on. Please keep in mind, that you should use any other fields than the uid as identifier for your fields.

label

Key

label

Datatype

->getLabel

Description

The label of the field. This can be a simple string, or a reference to locallang element. It's the same as the field label in the TCA.

For information which markers can be used here, see above in the description for “name”.

config

Key

config

Datatype

-> TCA[“config”]

Description

The configuration of the field. It's the same as in TCA and described in the Core APIs!

singleUserFunc

Key

singleUserFunc

Datatype

string

Description

Calls a user function for every dataset that is provided by the source. As arguments, dynaflex passes two arrays:

  • $dataArray: An array with two keys “row” and “dfConfig”
    • row: The data that was provided from the source
    • dfConfig: the MA
  • $aConfig: the field_config (at this time it contains the path)

The user function has to return a field_config with “path”, “name”, “label” and “config”!

-> sheet_config
name

Key

name

Datatype

string

Description

The name of the sheet that will be created. The index of the sheet (beginning with 0) is added to the name. Example: iIf the name is “sheet” the name of the sheet in the resulting flexform will be “sheet_X” where X is the index of the sheet.

label

Key

label

Datatype

string

Description

The label of the sheet.

You can use the marker ###SINDEX### which is replaced with the index of the sheet starting with 0. The markers are replaced before the value is passed to TYPO3. So if you have a reference to a language file, look for correct indexes in that locallang file.

fields

Key

fields

Datatype

Array of -> field_config

Description

An array of fields that will be created on that sheet. See “field_config” for the config of the fields.

modifications

Key

modifications

Datatype

Array of MA

Description

For each sheet you can tell DynaFlex to process a subset of modifications.

-> getLabel

If you create fields you have to set a label for them. In most cases it should be enough to reference a locallang value but in some cases you might need more.

A label definition can be a simple string or an array. Before the label is inserted into the TCA it's allways parsed for some markers (see -> markers). For example to make labels independet from each other through appending the uid or whatever.

There are two special markers that are always available!

  • ###ce_uid### - Is replaced with the UID of the current content element
  • ###ce_pid### - Is replaced with the UID of the page the content element is placed on

The most simple type of label is a string. You can add static text or a reference to a locallang value as I said before. The second possibility is to fetch the label from the database. To do so, you have to define the label as an array. Inside of the array you have to define some things that dynaflex knows where to fetch the label from.

table

Key

table

Datatype

string

Description

The table where the label should be fetched from.

field

Key

field

Datatype

string

Description

The field that contains the value for the label.

where

Key

where

Datatype

string

Description

the where clause for fetching the data. Here you can add some markers that will replaced before executing the query.

It's importand to recognize, that the query will allways be limited to one! This is hardcoded in dynaflex because it makes no sense to get more than one row from the database. ;-)

HOW-TO

In this section I will give you some small how-tos where you can see how different things can be realized with dynaflex. I don't want to and I can't explain every option with an example. But I think this gives you nice introduction and invites you to make your own experiments. :-)

All the examples you see here a realized in an tutorial extension which can be downloaded on http://www.typo3-unleashed.net or from the TER (Extensionkey: dynaflex_tut).

Basics (an introduction)

The basic flexform.

 1: $TCA['tx_dynaflextut_test'] = Array (
 2:     'ctrl' => $TCA['tx_dynaflextut_test']['ctrl'],
 3:     'interface' => Array (
 4:         'showRecordFieldList' => 'number,flexdata'
 5:     ),
 6:     'feInterface' => $TCA['tx_dynaflextut_test']['feInterface'],
 7:     'columns' => Array (
 8:         'number' => Array (
 9:             'exclude' => 1,
10:             'label' => 'LLL:EXT:dynaflex_tut/locallang_db.xml:tx_dynaflextut_test.number',
11:             'config' => Array (
12:                 'type' => 'input',
13:                 'size' => '30',
14:                 'eval' => 'int',
15:             )
16:         ),
17:         'flexdata' => Array (
18:             'exclude' => 1,
19:             'label' => 'LLL:EXT:dynaflex_tut/locallang_db.xml:tx_dynaflextut_test.flexdata',
20:             'config' => Array (
21:                 'type' => 'flex',
22:                 'ds' => array (
23:                     'default' => '<T3DataStructure>' .
24:                             '    <meta>' .
25:                             '        <langDisable>1</langDisable>' .
26:                             '    </meta>' .
27:                             '    <ROOT>' .
28:                             '        <type>array</type>' .
29:                             '        <el>' .
30:                             '            <dummy></dummy>' .
31:                             '        </el>' .
32:                             '    </ROOT>' .
33:                             '</T3DataStructure>'
34:                 ),
35:             ),
36:         ),
37:     ),
38:     'types' => Array (
39:         '0' => Array('showitem' => 'number;;;;1-1-1, flexdata')
40:     ),
41:     'palettes' => Array (
42:         '1' => Array('showitem' => '')
43:     )
44: );

Adding a simple field

To insert a single field you should use the “method” “add”. As “type” you use “field”. To define some things for the new field you can use the property “field_config”. Here you set the “name”, the “label” an the config which is described in the Core APIs.

 1: array (
 2:     'method' => 'add',
 3:     'path' => 'ROOT/el',
 4:     'type' => 'field',
 5:     'field_config' => array (
 6:         'name' => 'df_field_0',
 7:         'label' => 'myfirstdynaflexfield',
 8:         'config' => array (
 9:             'type' => 'input'
10:         )
11:     )
12: )

Adding multiple fields

To add more than one field with a single modfication you set the “type” to “fields”. Now the property “field_config” is interpreted as an array. Each element of this array is the same as the “field_config” in the example above.

 1: array (
 2:     'method' => 'add',
 3:     'path' => 'ROOT/el',
 4:     'type' => 'fields',
 5:     'field_config' => array (
 6:         0 => array (
 7:             'name' => 'df_field_1_a',
 8:             'label' => 'myseconddynaflexfield',
 9:             'config' => array (
10:                 'type' => 'input'
11:             )
12:         ),
13:         1 => array (
14:             'name' => 'df_field_1_b',
15:             'label' => 'mythirddynaflexfield',
16:             'config' => array (
17:                 'type' => 'input'
18:             )
19:         )
20:     )
21: ),

Adding fields depending on sources

The two examples above where not very impressive. Now we want to add fields depending on a source. In this example we use the be_users table as source. That means, that we create a field for every backend user that is in the be_users table and which is not marked as deleted.

To achieve this, you have to set some additional properties. First is the “source” which defines what is used as source. The second one is the “source_type” what defines how the source is handled. The third property is the “source_config” what configures how the sourcedata is fetched.

Now we can use the “field_config” property to create the fields. In the properties “name” and “label” the markers ###uid### and ###username### are substituted with the value from the source. In fact you can substitute every field that comes from the source.

 1: array (
 2:     'method' => 'add',
 3:     'path' => 'ROOT/el',
 4:     'type' => 'fields',
 5:     'source' => 'db',
 6:     'source_type' => 'entry_count',
 7:     'source_config' => array (
 8:         'table' => 'be_users',
 9:         'select' => 'uid,username',
10:         'where' => 'deleted=0',
11:     ),
12:         // keep in mind, that you can create more than one field at once. See the step above
13:     'field_config' => array (
14:         'name' => 'df_field_###uid###',
15:         'label' => 'DataforUser (###username###)',
16:         'config' => array (
17:             'type' => 'input'
18:         )
19:     )
20: ),

Adding some sheets from an input field

Now we do something slightly different. The first difference is, that don't add fields but create sheets. And we don't use a query as source but the value of a field that is inside of our TCAform.

As “source” we select “field” and we configure the source to fetch the value of the field “db_field” from the table “tx_dynaflextut_test”. The dataset is the same we currently working on.

Because we want to add sheets we have the property “sheet_config” which is handled like “field_config”. We just have some special properties for the sheets of course. As you can see, the label for each sheet has a marker ###SINDEX###. This marker is substituted with the index of the sheet we create. The name don't have to have a marker because the index is appended automatically.

 1: array (
 2:     'method' => 'add',
 3:     'type' => 'sheets',
 4:     'source' => 'field',
 5:     'source_config' => array (
 6:         'table' => 'tx_dynaflextut_test',
 7:         'db_field' => 'number',
 8:     ),
 9:         // configure the sheets
10:     'sheet_config' => array (
11:         'label' => 'Sheet ###SINDEX###',
12:         'name' => 'sheet',
13:             // place some fields on each sheet, replacing and tailing is like for the sheet itself
14:         'fields' => array (
15:             array (
16:                 'label' => 'field on sheet ###SINDEX###',
17:                 'name' => 'field',
18:                 'config' => array (
19:                     'type' => 'input',
20:                     'size' => '30',
21:                 )
22:             )
23:         )
24:     )
25: ),

Moving fields to a sheet

As last step we want to have all the fields we cerated before on the first sheet. But this only makes sense if at least one sheet was created (or exists). So we add a condition that checks if the values of the field we use for creating the sheets is greater than 0.

As method we use “move”. The only thing we have to define now is the “source” path and the “dest” (destination) path. In this example we move everything from “ROOT/el” (what is the default if no sheets exists) to “sheets/sheet_0/ROOT/el”. This is the first sheet which is called “sheet_0”.

 1: array (
 2:     'method' => 'move',
 3:         // define the condition when this modification should be executed
 4:     'condition' => array (
 5:         'source' => 'db',
 6:         'if' => 'isGreater',
 7:         'compareTo' => 0,
 8:         'table' => 'tx_dynaflextut_test',
 9:         'select' => 'number',
10:         'where' => 'uid=###uid###'
11:     ),
12:     'source' => 'ROOT/el',
13:     'dest' => 'sheets/sheet_0/ROOT/el'
14: )

Load DCA from Tsconfig

Since version 1.8.0 it is possible to load the DCA from page tsconfig. This makes it possible for admins to provide different forms on various pages. It's pretty easy to use, because the syntax is the same as writing the DCA in an array inside of a class.

Configure DynaFlex to load TS and no class

Add the following line to your ext_localconf.php:

$GLOBALS['T3_VAR']['ext']['dynaflex']['tx_dynaflextut_test'][] = 'TS';

That's all. That tells DynaFlex that for dataelements from table “tx_dynaflextut_test” will be modified by a DCA that comes from the tsconfig on the page the element is placed on.

Write TypoScript

Now we'll add the TypoScript that will modify the element by adding a simple input field.

 0: dynaflex.tx_dynaflextut_test {
 1:   0 {
 2:           path = tx_dynaflextut_test/columns/flexdata/config/ds/default
 3:           modifications {
 4:                   0 {
 5:                           method = add
 6:                           path = ROOT/el
 7:                           type = field
 8:                           field_config {
 9:                                   label = Field from TS
10:                                   name = tsfield
11:                                   config {
12:                                           type = input
13:                                   }
14:                           }
15:                   }
16:           }
17:    }
18: }

As you can see, the code in TyposScript is almost the same as the PHP array definition. It's only a different notation. One thing is a bit different! Please note that you have to add some numbers as keys for the SCAs and MAs (line 1 and 4). This is necessary because TypoScript always awaits array-keys what is not needed by PHP.

If you want, you can add a class with some hook definitions after the TS part of course!

Known problems

Nothing yet

To-Do list

  • Bugfixing! ;)
  • Adding more features... ? Tell me what you need !

Changelog

1.11.0 – Added a new condition type called “regex” (Thanks to Christopher Tan who provided the patch)

1.10.0 – Added a new condition type “cce” which fetches the condition source directly from the current record.

1.9.0 – Added execution conditions

1.8.1 – Added a new source type called “db_row”.

1.8.0 – Added possibility to load DCA from Tsconfig.Updated manual

1.7.0 – Dramatic improvements for calling DynaFlex.Introduced hooks

1.5.0 – Some bugfixes with function_call; updated manual and described a better way for instantiating dynaflex

1.4.0 – Bugfixes and cleanup functions added

1.2.0 – Added support for static markers in fieldconfig (###FINDEX###, ###DATA###)

1.1.0 – Added support for file references in TCA

1.0.0 – A lot of new methods and bugfixes (first stable version)

0.2.0 – Added moving of extra fields inside showitem section of TCA

0.1.0 – First official release

img-1 EXT: Dynamic FlexForms - 14