Translations
Info
All page names need to be in English.
en da  de  fr  it  ja  km  nl  ru  zh

Action Pipeline

From TYPO3Wiki
Jump to: navigation, search
This page belongs to the Extension coordination team (category ECT)

notice - Draft

Change the {{draft}} marker to {{review}} when you need a reviewer for text and TypoScript. info

Keywords (Tags)

Action Pipeline, Design Pattern, MVC, PAC, SPL, ECT, lib, OOP

Overview

Generally you can vitalize the actions methods of lib/div with any code you like, but the library also offers a standard system to build the actions. This standad way is done by the use of objects, that share the API due to inheritance from a common parent class (tx_lib_processor).

Different data has to be transported and processed along the request cycle from the request to the response. This happens in form of a daisy chain of processor objects, following the design pattern of Pipes and Filters . The filters are the processing objects. We call them processors. Processing in this wide sense includes for example the loading and storing tasks of the data.

http://upload.wikimedia.org/wikipedia/de/3/33/PipesFilters.png (Source: http://de.wikipedia.org/wiki/Pipes_and_Filters)

TODO: Replace the PNG with one in English.

If it is not sufficient to process the data in a linear way, the flow of data can be routed. The processors return result status, to which different pipes can be assigned.

The pipes are the methods by use of which the processors are plugged to each other. This methods are part of the inherited SPL interfaces.

The work-flow of the action pipeline can be either controlled by plain PHP actions or by actions done as TS setup.

Why to Choose Action Pipelines

There are different requirements for lib/div that need to be tied:

  • Programming should be easy, even for beginners.
  • Programming should be done by object orientated.
  • The difficult request cycle needs to mastered.
  • Flexibility to create and to extend should be given.

The handling of the request cycle can be mastered by a ready made object, that providins hooks and handlers for the objects of the user, that have to by cycled. The drawbacks of this solution are, that there is no full flexibility and that the procedure isn't that transparent for the user.

The action pipeline is an alternative approach to this. It gives the control of the request cycle fully to the hand of the user. If such a complex task needs to be done by an unexperienced user, it is important, to find a way to make the handling as easy as possible. The natural way to think of a request cycle is to imagine it as a simple linear process. The design pattern of pipes and filters is a very modular and flexible way, to handle such a linar process by the use of objects.

By the use of defined API it is even possible to order and configure a pipeline of pipes and filters by a configuration language like TS. That speeds up the development. It's easy to reconfigure and extend it, even without knowlege of PHP.

Usage Examples

By TS Setup

 plugin.tx_bananas.form = USER_INT
 plugin.tx_bananas.form.userFunc = tx_bananas_controllers_form->main
 plugin.tx_bananas.form.configurations < temp.tx_bananas.common
 plugin.tx_bananas.form.actions {
       clearAction {
               START = view
               view = tx_bananas_views_form
               view {
                       do.1 = render
                       do.1.1  = formTemplate
                       go.TX_LIB_APS_OK = translator
               }
               translator = tx_lib_translator
               translator {
                       do.1 = translate 
                       go.TX_LIB_APS_OK = storeSession
               }
               storeSession = tx_lib_sessionProcessor
       }
}

By Plain PHP

This is a current development snapshop of bananas. It still doesn't show the final solution.


public function defaultAction() {
 // Model.
 $pipe = $this->makeInstance('tx_bananas_models_board');
 $pipe->setTotalResultCountKey('totalResultCount'); // For the result browser.
 $pipe->setResultListKey('resultList'); // For the view.
 $pipe->load();
 if(!$pipe->getStatus() == TX_LIB_APS_OK) $this->_die('Unexpected result statuts in', __FILE__, __LINE__);
 
 // Result browser.
 $pipe = $this->makeInstance('tx_lib_resultBrowserProcessor', $pipe);
 $pipe->setTotalResultCountKey('totalResultCount'); // From the model.
 $pipe->setResultBrowserKey('resultBrowserWidget'); // For the template.
 $pipe->build();
 if(!$pipe->getStatus() == TX_LIB_APS_OK) $this->_die('Unexpected result statuts in', __FILE__, __LINE__);
 
 // View.
 $pipe = $this->makeInstance('tx_lib_phpTemplateEngine', $pipe);
 $pipe->castList('resultList', 'tx_lib_phpTemplateEngine', 'tx_lib_phpTemplateEngine');
 $pipe->setResultKey('result'); // For the translator.
 $pipe->render('listTemplate');
 if(!$pipe->getStatus() == TX_LIB_APS_OK) $this->_die('Unexpected result statuts in', __FILE__, __LINE__);
 
 // Translator.
 $pipe = $this->makeInstance('tx_lib_translator', $pipe);
 $pipe->translate();
 if(!$pipe->getStatus() == TX_LIB_APS_OK) $this->_die('Unexpected result statuts in', __FILE__, __LINE__);
 
 return $pipe->get('result');  // From the view.
}

TS Actions versus PHP Actions

The same processor objects can be joined to action pipeplines by two different languages, the one is TypoScript the other is PHP. They are reffered to as TS actions or PHP actions. Both types can be mixed within the same controller, but not within the same action.

The PHP action is normal PHP code inside the action methods of the plugin controller. It creates and joins the processor objects to the pipeline and controlls switches within the workflow based on the result status of the processors.

TS actions are fully configured in the TS setup of the controller. The same processor properties, methods and result status are addressed like by the PHP action. The pipe of processors is used analoguous. A special subpart of the controller discovers the TS action setup, interpretes it and runs the pipe. It doesn't compile the TS to a PHP action before, so that both technologies can't be mixed within the same action.

TS actions are fast to create and to reconfigure. Simple applications can fully be written in TS this way, using predifined PHP processors from libraries. They are limited to processors that strictly implement the required processor API. PHP actions are more flexible. They can mix such processors with any other PHP code.

The Action Pipeline Implements the Request Cycle

notice - Draft

Change the {{draft}} marker to {{review}} when you need a reviewer for text and TypoScript. info

The action pipeline as a way to implement of the request cycle. Often it starts with loading parameters from the sessions and from the incomming request, does validation and updates the model. Then the view is rendered and the response is send.

notice - Note

Send a redirect after each model update

For many reasons it is a good habit to send a redirect directly upon the update of the model to request the next view.

  • Repeated submit of the same data by accidental reaload is prevented.
  • The pipeline is "cleared" from incomming parameters before requesting the next view, so that no "unexpected" or "overseen" data can go to the next view. A mayor source for bugs can be avoided by this clean design.
  • By the "double request" it is assured that the models of all levels of the PAC tree are updated before any response is generated. Parent levels will always display the updates of child levels.

Internal Data of the Processor Pipeline

Terms

All data that is processed along the request cycle is shiped inside the SPL array of the processors. The SPL array is the internal data array of SPL objects, that can be accessed by the methods defined by the SPL API that is inherited by all processors.

Data:

 Data is the term for the values that are processed through the action pipeline.

Fields:

 Fields is the term, that can be used to refer to the keys of the internal data.

Example names:

  • fieldsToHighlight
  • fieldsToValidate

Naming pattern of data keys

If you implement the action in the TS way, you can make few use of the getters and setters for the internal data. The processor objects handle that for you. The processors use default keys to address the internal data. The keys are regular object properties. If there are naming conflicts for any reasons, you can customize the properties by the technologies and precedency rules described in the section below.

notice - Note

Naming pattern of for data keys

All variable names for the keys for internal data should follow the naming pattern: dataKeyOfSomething.

PHP Example:

$processor->setProperty('dataKeyOfSearchString', 'searchString');

TS Example:

{
  [...]
  highligther {
    set.dataKeyOfSearchString = searchString
    do [ ... ]
    go [ ... ]
 }
  [...]
}


Setters and Getters for the Internal Data

You make few use of the set() and get() methods of processors to handle the data, from the action, because it is shipped internally of the object pipe. Internally the processors use the setters and getters a lot.

You can use the set() method, to set literal strings from the TS setup. The get() method is used in the PHP action,to return the final result.

Properties of Processsors

Generally you can say that local settings of properties overwrite the more global settings.

Precedency from lowest to highest:

  1. default object property
  2. TS setup
  3. flexform setup
  4. local object property
  5. method parameters


notice - Note

Implementation of the precedency

To easily make use of the setup precedencies from within processor methods, a helper method is required. This helper method has the name findProperty($key). It is part of the common processor API. It checks the different levels of the setup for the given key.

notice - Note

The local object property and the method parameters are of special importance, to control processors within actions. It's still an open question, if this is a useful concept for objects of the tx_Lib_Object type in general.

Default Object Property

This are regular class variables. They are coded into the object directly and serve as final fallback, if no setting is found on a different level.

var $pathToTemplateDirectory = 'fileadmin/templates/';

TS Setup

The TS setup is the level used in the most cases for practical configuration. Many settings end on this level.

 [...] {
    configuration {
      pathToTemplateDirectory = EXT:myextension/templates/';
    }
 }

Flexform Setup

Properties set in a flexform overwrite settings of the lower levels TS and default object properties.

Local Object Property

The local object property is not a hardcoded object property like the default object property, but a way to set properties from within actions, either from TS actions or from PHP actions.

(in this example using Smarty) as template engine.

$pipe->setProperty('pathToTemplateDirectory', 'EXT:myextensions/templates/smarty/');
singleViewAction {
  model      [...]
  view = tx_toptop_view
  view {
    set.pathToTemplateDirectory = EXT:myextensions/templates/smarty/
    do.render.1 = singleView.tpl
    go.OK = translator
  }
  translator      [...]
}


This properties are stored into a special array of the processor object called actionProperties. From there they are taken, when the processor requests a property by the method findProperty($key).


notice - Note

Taking local properties from the setup

If a string is given, it is first checked, if such a key exists in the TS or the FF setup. In this case it is replaced by the value. Else the string is taken as value itself.

$pipe->setProperty('pathToTemplateDirectory', 'pathToSmartyDirectory');
$pipe->setProperty('validationRules', 'specialValidationRules');

This way a default property can be overwritten in the action by a value set in TS or FF.

It effects the property value AND the wrapping array i.e. ('validationRules.').

You should be aware of this feature. In rare cases it can lead to unexpected side effects. Only use it with hardcoded keys or in TS configured actions. Don't feed unknown user defined values into this method, because he may feed a setup key by accident.

Method Parameters

Method parameters have the highest precedency of all. This are optional parameters for methods. If omitted they fall back to the same property taken from lover levels.

$processor->render('singleView.tpl', 'EXT:myextension/templates/smarty');
singleViewAction {
  model      [...]
  view = tx_toptop_view
  view {
    do.render.1 = singleView.tpl
    do.render.2 = EXT:myextensions/templates/smarty/
    go.OK = translator
  }
  translator      [...]
}

The second parameter sets the property 'pathToTemplateDirectory'. If it isn't provided, the rendering method falls back to lower levels for this property.

notice - Note

Taking method parameters from the setup

If a string is given, it is first checked, if such a key exists in the setup. In this case it is replaced by it's value, else the string is taken as value itself.

$processor->render('form.tpl', 'pathToSmartyDirectory');

You should be aware of this feature. In rare cases it can lead to unexpected side effects.

Methods of Processors

Result Status of Processors

After the processing task is done each processor has a result status. The next processor in the action chain is determined by this result status. The result status is accessed by the method getStatus().

The assignment of successing processors to result status is either be done in PHP or in TS depending on the requirements. Many processors only have one result status TX_LIB_APS_OK and one successor. Other processors like validators or captchas have at least two different result status.

Result status are integers that are used in form of PHP constants. This constants are defined in the file: EXT:lib/ext_localconf.php:

 define('TX_LIB_APS_ERROR', tx_lib_processor::generateStatus());     // Something unexpected happend.
 define('TX_LIB_APS_OK', tx_lib_processor::generateStatus());        // Simple normal status.
 define('TX_LIB_APS_PASSED', tx_lib_processor::generateStatus());    // Used by checks and tests.
 define('TX_LIB_APS_FAILED', tx_lib_processor::generateStatus());    // Used by checks and tests.
 define('TX_LIB_APS_CREATED', tx_lib_processor::generateStatus());   // Additional status for creating processors. 

APS stands for Action Processor Status. The values of the constants are generated by the static method tx_lib_processor::generateStatus(). This method cares that each constant is unique APS identifier within the whole request.


Class Hierarchy

Simplified hierarchy of action processor objects:

tx_lib_SelfAwareness        (abstract introspection)
   
   ^
   |
   
tx_lib_objectBase           (abstract data carrier in SPL style)
   
   ^
   |    
   
tx_lib_processor            (abstract processor)
   
   ^
   |
   
tx_yours_xyzProcessor       (objects of your action pipe)

Class Interface

Apart from the inherited methods the interface of a processor is quite simple:

 class tx_lib_processor extends tx_lib_objectBase {
   
   // status variable
   var $status = TX_LIB_APS_OK;
   
   // Find a property by precedency from different sources
   function findProperty() { ... }    
   // Static method used by EXT:lib/ext_localconf.php to generate the status.
   function generateStatus() { ... }
    
   // Setter of the status
   function setStatus() { ... }
   
   // Getter of the status
   function getStatus() { ... }
   // Checkers for frequent status
   function hasStatusError();
   function hasStatusOk();
   function hasStatusPassed();
   function hasStatusFailed();
   function hasStatusCreated();
 }