Developers/Html frontend/Extend existing components

From Aimeos documentation

Developers
Other languages:
English 100%


2018.x+ version

Sometimes you need additional information from the database for one of the components because you have created your own template for a subpart and it should display these information. If you only want to add information, extending existing component classes is troublesome. Another developer may want to add other information and several users might need the information from both classes in their templates.

A far better way is to create a HTML client decorator and to configure this decorator for the component where the information should be added to the views. In this case, anybody else can add both decorators (and more) to their components only by configuration. This way of stacking is far more flexible than extending classes.

The decorators you are implementing have to be stored in your own Aimeos extension in this directory:

./client/html/src/Client/Html/Common/Decorator/

The directory matches the namespace of the decorator class.

Basic class structure

A basic skeleton for an HTML client decorator is simple as the abstract class contains all required logic to get a working decorator:

  1. namespace Aimeos\Client\Html\Common\Decorator;
  2.  
  3. class Mydecorator
  4.     extends \Aimeos\Client\Html\Common\Decorator\Base
  5.     implements \Aimeos\Client\Html\Common\Decorator\Iface
  6. {
  7. }

Decorators wrap around existing components like layers of an onion and there's no limitation in the numbers of layers. You only have to keep in mind that you will add another function call to the stack for each decorator you are adding.

Both, components and decorators must implement the same interface and therefore the same public methods. Thus, you can overwrite any public method but no private or protected ones. The list of public methods for both are:

  • addData(): Adds the necessary data used in the template
  • getBody(): Returns the HTML code for insertion into the body
  • getHeader(): Returns the HTML string for insertion into the header
  • getSubClient(): Returns the sub-client given by its name
  • getView(): Returns the view object that will generate the HTML output
  • modifyBody(): Modifies the cached body content to replace content based on sessions or cookies
  • modifyHeader(): Modifies the cached header content to replace content based on sessions or cookies
  • process(): Processes the input, e.g. check and store the given values
  • setView(): Sets the view object that will generate the HTML output

The modifyBody() and modifyHeadder() methods are only used by components that implement content caching, like the catalog filter, list or detail component. Please have a look into the component you want to decorate if they support content caching an will call both methods.

Supporting methods

Decorators inherit all methods also available in the components and subparts via the Aimeos\Client\Html\Base class and you can use them everywhere in your decorator. There's one additional method exclusive to decorators named getClient(). It returns the HTML client or decorator representing the next layer in the onion down to the core object:

  1. public function process()
  2. {
  3.     // do something before
  4.     $this->getClient()->process();
  5.     // do something afterwards
  6. }

You must use it to call the same method of the next object. Otherwise, the methods of the inner objects won't be executed.

Example

Say you want to add the supplier object to the component because one of your new templates need to print some information about the supplier of the product. If this information should be available in both, body and header of the component, you should create a decorator like this:

  1. namespace Aimeos\Client\Html\Common\Decorator;
  2.  
  3. class Mydecorator
  4.     extends \Aimeos\Client\Html\Common\Decorator\Base
  5.     implements \Aimeos\Client\Html\Common\Decorator\Iface
  6. {
  7.     public function addData( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
  8.     {
  9.         $view = parent::addData( $view, $tags, $expire );
  10.  
  11.         // access already added data
  12.         $products = $view->get( 'listsItems', [] );
  13.  
  14.         // fetch some items from the database
  15.         $view->decoratornameMyparam = ...
  16.  
  17.         return $view;
  18.     }
  19. }

For your parameters, you should use the name of your decorator as prefix to prevent overwriting parameters from other decorators or subparts.

Configuration

After you've created your new decorator, you need to tell the factory to add it to the HTML client for the component. This is done via configuration, e.g. for the catalog detail component:

client/html/catalog/detail/decorators/global = array( 'Mydecorator' )

For the mini basket component, the configuration would be:

client/html/basket/mini/decorators/global = array( 'Mydecorator' )

You can add your decorator to every component you like as they all implement the same interface. In case you really want to add a decorator to ALL components, you can use this configuration setting instead:

client/html/common/decorators/default = array( 'Mydecorator' )

Remember that decorators suitable for all components must be very generic and must work not only for components creating the output for the browser but also for the components that are generating the e-mails executed by cronjobs on the command line!