Login / Status
developer.Resource
Home . Development . Articles . How to use existing hooks
Sponsors
hosted by punkt.deTYPO3 and Open Source Magazine
<<  A real world example  Insert coin and choose a flavour Doing it with callUserFunc >> 

Insert coin and choose a flavour

As you already know, hooks come in two flavours: callUserFunction and the getUserObj. Most of the core developers prefer the tasty getUserObj way - and not only because it has some fancy design pattern behind it (to be honest, it's not that fancy at all, but at least it has something to do with objects).

Anyways, before you can register your method at some hook, you'll have to find out what kind of species you deal with. In our little example, we came across some getUserObj style.

Going the getUserObj way

Instead of explaining the different implementations, I'll just go ahead with describing how we implement our userfunction the getUserObj way. Later on I will show the same implementation using the callUserFunction method, so you'll easily see the differences.

Create and include your own class

The first thing we need, is a new class which contains our user function. This is one important fact about getUserObj driven hooks: You create a class (usually one for each function you want to override in the original source) which contains as many methods as hooks you want to implement.

In our example we want to use one hook in the function process_datamap in the class tce_main. That's why I create a new file, called class.tx_myextension_tcemainprocdm.php containing a class named tx_myextension_tcemainprocdm.

Of course it would have been nicer to use the whole function name (..._tcemain_processdatamap) as a filename, but according to the coding guidelines we are only allowed to use 31 characters for our filename.

Finally we create an empty function in our new classed, with exactly the same name mentioned in the hook. Our class should now look like this: 

class tx_myextension_tcemainprocdm {

    function processDatamap_postProcessFieldArray ($status, $table, $id, &$fieldArray, &$this) {

        // here comes the code

    }

Register the method

We have seen in the code snippet from TCEmain, that an array called $hookObjectsArr is being traversed and a method processDatamap_postProcessFieldArray is being called if it exists. Aparrently we have to make sure that our new class is also available as an instantiated object in just that array $hookObjectsArr.

The solution lies at the very beginning of the process_datamap function:

 // First prepare user defined objects (if any) for hooks which extend this function:

$hookObjectsArr = array();

if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) {

    foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) {

        $hookObjectsArr[] = &t3lib_div::getUserObj ($classRef);

    } 

As you can see, we just have to put our class name into the global variable called $TYPO3_CONF_VARS (this is also the recommended place for managing hooks in general). We will do that by adding one line to the ext_localconf.php of our extension:

$GLOBALS ['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = 'tx_myextension_tcemainprocdm';

 

Note that the ...php']['processDatamapClass'] variable is an array and we just add another value (our class name).

There is another thing we have to take care of: Make sure that the file containing our new class is loaded when it's going to be used in the hook. You could either add a require_once statement into the ext_tables.php of your extension - or, which is the reccomended way, use the following hint:

Hint: Include and register your class simultaneously

There is a nice feature in the getUserObj method which allows us to combine the two steps loading the file and registering the class: Instead of putting require_once into ext_tables.php and registering your class in ext_localconf.php, you may register your new class with a line like this (in ext_localconf.php):

$GLOBALS ['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = 'EXT:myextension/class.myextension_tcemainprocdm.php:tx_myextension_tcemainprocdm';

 

Another great advantage of this method is that your file will only be included if it's really used! 

Hide page - Part I

Now we finally may create the actual implementation providing the new functionality.  Let's have a look at our new function again:

function processDatamap_postProcessFieldArray ($status, $table, $id, &$fieldArray, &$reference) {

 

As you can see, the variable &$fieldArray was passed by reference, which means that we can modify it if we want! And we do want to change it: If there is a backend user logged in, not being member of a certain backend usergroup (namely the chief editor's group), we want to set the hidden field to 1.

if (!t3lib_div::inList ($reference->BE_USER->user['usergroup'], $chiefEditorsUsergroup)) {

    if ($status == 'update' && $table == 'pages') {

        $fieldArray['hidden'] = 1;

    } 

Through the $reference parameter, we have complete access to the parent object providing the hook. In our case we use TCEmain's variable BE_USER which contains an instance of the current backend user object.

Of course you will have to set $chiefEditorsUsergroup to some meaningful value ...

Hide page - Part II

That works just fine, however it doesn't really make sense yet. The page will only be hidden if someone not being the chief editor edits the page, that is: some record in the table pages. But what if someone modifies a content element being part of that page, how can we intercept that? Just like that:

class.tx_myextension_tcemain.php: 

   1: function processDatamap_postProcessFieldArray ($status,$table,$id,&$fieldArray,&$reference) {

   2:     if (!t3lib_div::inList ($reference->BE_USER->user['usergroup'], $chiefEditorsUsergroup)) {

   3:         if ($status == 'update' && $table == 'pages') {

   4:             $fieldArray['hidden'] = 1;

        }

   7:         if ($status == 'update' && $table == 'tt_content') {

   8:             $row = t3lib_BEfunc::getRecord ($table, $id);

  10:             if (is_array ($row)) {

  11:                 $dataArr = array ();

  12:                 $dataArr['pages'][$row['pid']]['hidden'] = 1;

  14:                 $tce = t3lib_div::makeInstance('t3lib_TCEmain');

  15:                 $tce->start($dataArr, array());

  16:                 $tce->process_datamap();

            }

        }

    }

}

Noticed that we call process_datamap from the userfunction itsel?. We have to create a new instance of TCEmain (line 14) to achieve that.