1. Aim of a (document-)controller

As noted in Hello World! and Templates document controllers are used to generate dynamic content. The term controller refers to the MVC pattern within the Adventure PHP Framework as in several other products. However, the APF goes beyond that introducing the HMVC pattern to create UIs along with the Page controller.

The HMVC pattern has several advantages over the MVC pattern. It enables you to define highly granular elements of your web page or application that can be easily reused within the current or other projects. From a software design perspective, the dependencies between different components - using the MVC pattern normally leads to heavy logic within controllers taking care of the whole application - can be reduced significantly.

In order to let a controller generate dynamic content it must be referred within a Templates. This can be done by adding

APF template
<@controller class="..." @>

at the top of the template. The class attribute contains the fully-qualified class name of the controller implementation (e.g. VENDOR\pres\controller\NavigationController). During transformation the page controller executes the transformContent() method.

In order to keep the controller code clean and easy to read it is recommended to limit output generation or view logic to a minimum. In case a more complex logic is necessary to generate dynamic content it may be a good idea to create a separate tag that encapsulates the logic.

As a rule of thumb, controllers are recommended as long as view logic and the complexity of dynamic data that is displayed is limited. As soon as the controller is required to gear into creation or manipulation of the DOM tree or the data structure to display is more complex data custom tags should be implemented.

Moreover, only one concern should be handled by one controller especially because the APF uses the HMVC-Pattern. In case more that one functionality is contained, you should consider to separate the component into two separate components.

2. Design of a (document-)controller

A document controller is defined by the interface DocumentController and usually inherits from BaseDocumentController that already offers a lot of basic functionality.

The basic structure of a document controllers is thus as follows:

PHP code
namespace VENDOR\pres\controller; use APF\core\pagecontroller\BaseDocumentController; class NavigationController extends BaseDocumentController { public function transformContent(){ ... } }

transformContent() is used by the Page controller during transformation of the page in order to execute the functionality of the current DOM element. The content of the class except this basic structure is subjected to the developer.

As you may take from the API documentation the BaseDocumentController class includes several methods that help you implementing dynamic functionality. These are:

  • getDocument(): Returns a reference on the current DOM node. This enables you to access the entire tree using the methods getChildren() and getParent().
  • getTemplate($name): Returns a reference on a template object (instance of TemplateTag) defined within the current document.
  • getForm($name): Returns a reference on a form object (instance of HtmlFormTag) defined within the current document.
  • getLabel($name): Returns a reference on a label object (instance of LanguageLabelTag) defined within the current document.
  • getIterator(): Returns a reference on an iterator object (instance of HtmlIteratorTag) defined within the current document.
  • getNodeById($id): Returns a reference on any DOM node that defines attribute dom-id with the applied $id value. To obtain a DOM node (e.g. template) within the entire DOM tree using a unique ID the tag definition must be as follows:
    APF template
    <html:template name="..." dom-id="xyz"> ... </html:template>
    The template can then be obtained within a controller as follows:
    PHP code
    $template = $this->getNodeById('xyz');
  • placeHolderExists($name): Checks, whether a place holder exists within the current document.
  • templatePlaceHolderExists(TemplateTag &$template, $name): Checks, whether a place holder exists within the applied template instance of the current document.
  • setPlaceHolder($key, $value): Fills a place holder with the applied value.
  • setPlaceHolders(array $placeHolderValues): Fills a list of place holders with the applied values.

Moreover, the BaseDocumentController inherits all attributes and functions of the class APFObject.

Besides, each document controller instance contains a reference on the current DOM node the controller is responsible for transformation. The reference can be retrieved using the $this->getDocument() method. Using this reference, you can access the document's foo attribute via
PHP code

3. (Document-)controller example

3.1. Representation of dynamic meta tags

One common use case of a document controller is filling the META information within an HTML header. As an example, the title and the current date should be filled for a dynamic web page. For this reason, let us first define the HTML structure and the necessary place holders:

APF template
<@controller class="VENDOR\pres\controller\MainController" @> <html> <head> <title><html:placeholder name="title" /></title> <meta name="date" content="<html:placeholder name="date" />" /> </head> <body> ... </body> </html>

The document controller referred to within the templates implements the transformContent() method and a getTitle() method to evaluate the value for the current page. The source code of the class is as follows:

PHP code
namespace VENDOR\pres\controller; use APF\core\pagecontroller\BaseDocumentController; class MainController extends BaseDocumentController { public function transformContent(){ $this->setPlaceHolder('title', $this->getTitle()); $this->setPlaceHolder('date', date('Y-m-d')); } private function getTitle() { return ... } }

3.2. Display of dynamic lists

Using the <html:template /> tags you can easily create lists within a controller by iterating over the list of items.

<html:template /> tags are only suitable to display content with only one hierarchy level (e.g. lists of products with prices). In case a <html:template /> tag is no longer sufficient and the display creation requires nesting of different templates it is recommended to create a custom tag that handles the display of an entire list element or the whole list in one. Further notes can be found with in tutorial Implementation of tags.

Please note the following template that was defined to display products along with their price as a table:

APF template
<@controller class="VENDOR\pres\controller\ListController" @> <table cellpadding="0" cellspacing="0" border="0"> <thead> <tr> <td>Produkt</td> <td>Preis</td> </tr> </thead> <tbody> <html:placeholder name="products" /> </tbody> </table> <html:template name="product-item"> <tr> <td><html:placeholder name="display-name" /></td> <td><html:placeholder name="price" /></td> </tr> </html:template>

The appropriate controller is as follows:

PHP code
namespace VENDOR\pres\controller; use APF\core\pagecontroller\BaseDocumentController; class ProductListController extends BaseDocumentController { public function transformContent() { $tmpl = $this->getTemplate('product-item'); $buffer = ''; foreach ($this->getProducts() as $product) { $tmpl->setPlaceHolder('display-name', $product->getDisplayName()); $tmpl->setPlaceHolder('price', $product->getPrice()); $buffer .= $tmpl->transformTemplate(); } $this->setPlaceHolder('products', $buffer); } /** * @return Product[] */ private function getProducts() { } }

Within transformContent() the list of products is retrieved by the internal function getProducts(). The loop is used to fill the product information into the template and to collect the output of the template transformation within a buffer. Later one, the result is passed to the products place holder to create the final output.

3.3. Further reading

Further use cases of controllers can be found under Tutorials and Articles.

4. Controller creation via DI container

For more complex use cases document controllers can also be created with the DIServiceManager. This offers a lot of advantages compared to the classic approach:

  • Dependent objects (e.g. business services, domain objects) can be configured easily and applied to the controller. This avoids redundant code and improves testability of the controller since the implementation no longer defines explicit code-based dependencies.
  • Using static and dynamic configuration facilities of an APFDIService controllers can be configured easily for different use cases. A controller's behaviour can therefor be influenced by a simple configuration parameter and can thus be re-used within different applications.

In order to create controllers via the DIServiceManager an adaption of the controller declaration is necessary. Here, the implementation is no longer referred to by the class attribute but using a service declaration:

APF template
<@controller namespace="APF\sites\controller" service="ContactController" @>

The namespace attribute names the namespace of the service definition, service declares the service definition that contains the document controller. Details on addressing services can be taken from chapter Services.

In order to use the service declaration please create a service configuration accordingly. Details on the scheme of service configurations can be read about in section Configuration.

The following code box contains a sample configuration that applies a static parameter to the controller (E-Mail):

APF configuration
[ContactController] class = "APF\sites\documentcontroller\ContactController" servicetype = "NORMAL" conf.email.method = "setEMail" conf.email.value = "me@example.com"

Further information on using the Dependency Injection Containers is contained in chapter Usage.


Do you want to add a comment to the article above, or do you want to post additional hints? So please click here. Comments already posted can be found below.
There are no comments belonging to this article.

In order to provide a state-of-the-art web experience and to continuously improve our services we are using cookies. By using this web page you agree to the use of cookies. For more information, please refer to our Privacy policy.