Pager

Scrolling/paging of lists is a frequently-used functionality within web applications. To ease implementation of multi-page lists the APF delivers a special module for that - the pager.

The Pager takes responsibility for selection of the relevant content and additionally delivers a component to display scrolling/paging options. You can find the Pager in action within the Comment function and Objektorientierte Implementierung eines Gästebuchs (German) module.

The following chapters describe the structure of the module and it's usage in detail.

1. Structure

The Pager consists of the following components:

  • PagerManager: The PagerManager is the key component of the module. It provides an Interface to load entries and to diplay the HTML representation of scrolling/paging options.
  • PagerMapper: Implements a simple data mapper for abstraction of the database communication and to directly load entries using a component of your application.
  • SimplePagerController or AdvancedPagerController: Both (Document-)Controller represent an HTML scrolling/paging bar. You can easily exchange them by your custom implementation if necessary.
  • PagerManagerFabric: Class PagerManagerFabric can be used to create a PagerManager instance. Please note, that this class has been replaced in version 2.0 by creating the pager using the DIServiceManager.

Due to the fact that the PagerManager is built as a generic component you have to configure it prior to use. Chapter 2 provides details on the necessary configuration files and settings.

2. Configuration

Using the Pagers a few configuration files must be created. The following chapters describe the necessary content and the intent of the options.

2.1. Basic configuration

Creating the PagerManager two different flavours are available: creation via the PagerManagerFabric or via DIServiceManager.

2.1.1 Creation via PagerManagerFabric
Please note that creation via PagerManagerFabric has been marked as deprecated in version 2.0. Functionality is still there but will be removed within one of the following releases. This, please use creation as described in chapter 2.1.2.

In case you intend to create the desired PagerManager instance using the PagerManagerFabric please create configuration file

Code
/APF/config/modules/pager/{CONTEXT}/{ENVIRONMENT}_pager.ini

It contains all necessary settings or refers to further parts of the configuratio that are described in chapters 2.2 up to 2.4. Details on configuration and naming of files can be found under Configuration.

The PagerManager expects the following content within the main configuration files:

APF configuration
[{Pager-Name}] Pager.DatabaseConnection = "..." Pager.EntriesPerPage = "..." Pager.ParameterPageName = "..." Pager.ParameterCountName = "..." Pager.StatementNamespace = "" Pager.CountStatement = "..." Pager.EntriesStatement = "..." Pager.StatementParameters = "..." Pager.DesignNamespace = "" Pager.DesignTemplate = "" Pager.CacheInSession = "true|false" Pager.AllowDynamicEntriesPerPage = "true|false"

The parameters listed above do have the following meaning:

Name Description
{Pager-Name} Defines the unique identifier of the pager configuration. You can create/obtain the desired pager instance by this identifier within your application.
Pager.DatabaseConnection Refers to the database connection defined within /APF/config/core/database/{CONTEXT}/{ENVIRONMENT}_connections.ini. Details on creating database connections can be found in chapter ConnectionManager.
Pager.EntriesPerPage Defines the number of entries per page (e.g. 10).
Pager.ParameterPageName Defines the name of the url parameter for the page to display (e.g. page).
Pager.ParameterCountName Defines the name of the url parameter to determine the amount of entries per page (z.B. count).
Pager.StatementNamespace Defines the namespace where the Pager searches for the SQL statement files that are used to query the amount of content and the entries itself (e.g. APF\modules\comments). Details on using statement files can be found in chapter statement files.
Pager.CountStatement Defiles the name of the statement file to load the total amount of entries (e.g. load_entries_count.sql).
Pager.EntriesStatement Defiles the name of the statement file to load the entries for the current page (e.g. load_entry_ids.sql).
Pager.StatementParameters Allows to define further static parameters that are considered during execution of the configured statements (e.g. foo:bar|bar:baz).
Pager.DesignNamespace Defines the namespace where the HTML representation of the pager navigation bar can be found (e.g. APF\modules\pager\pres\templates).
Pager.DesignTemplate Defines the template used to create the HTML representation of the pager navigation (e.g. pager_2).
Pager.CacheInSession Allows you to cache results of pager queries during one session. This improves performance but at the same time possibly prohibits new entries from being displayed (e.g. true|false).
Pager.AllowDynamicEntriesPerPage Allows (true) or disallows (false) dynamic definition of the amount of entries per page via URL.
Please be aware that using the Pager.AllowDynamicEntriesPerPage option set to true may cause the statement runtime to increase applying a high number of entries per page.

Within your application you can create the desired pager instance as follows:

PHP code
use APF\modules\pager\biz\PagerManager; use APF\modules\pager\biz\PagerManagerFabric; /* @var $fabric PagerManagerFabric */ $fabric = & $this->getServiceObject('APF\modules\pager\biz\PagerManagerFabric'); $pager = & $fabric->getPagerManager('{Pager-Name}');
2.1.2 Erzeugung per DIServiceManager

Using the DIServiceManager code and configuration can be separated much easier. This advantage can also be used with the PagerManager as configuration of the Pager can directly be stored under the namespace of the application.

In order to create the PagerManager using the DI container of the APF a service configuration must be created. The configuration can be stored at any desired place. It is recommended to place it under the namespace of your application (e.g. APF\modules\comments for the Comment function module). The name of the file is

Code
{ENVIRONMENT}_serviceobjects.ini

according to chapter configuration schema. The content of the file depends on your use case. The following code box shows all available options:

APF configuration
[{Pager-Name}] servicetype = "NORMAL|SINGLETON|SESSIONSINGLETON|APPLICATIONSINGLETON" class = "APF\modules\pager\biz\PagerManager" conf.database-connection.method = "setDatabaseConnectionName" conf.database-connection.value = "..." conf.entries-per-page.method = "setEntriesPerPage" conf.entries-per-page.value = "..." conf.url-param-page.method = "setPageUrlParameterName" conf.url-param-page.value = "..." conf.url-param-count.method = "setCountUrlParameterName" conf.url-param-count.value = "..." conf.statement-namespace.method = "setStatementNamespace" conf.statement-namespace.value = "..." conf.count-statement.method = "setCountStatementFile" conf.count-statement.value = "..." conf.entries-statement.method = "setEntriesStatementFile" conf.entries-statement.value = "..." conf.statement-params.method = "setStatementParameters" conf.statement-params.value = "..." conf.ui-namespace.method = "setPagerUiNamespace" conf.ui-namespace.value = "..." conf.ui-template.method = "setPagerUiTemplate" conf.ui-template.value = "..." conf.caching.method = "setCacheInSession" conf.caching.value = "true|false" conf.dynamic-page-size.method = "setAllowDynamicEntriesPerPage" conf.dynamic-page-size.value = "true|false"

The above directives have the following meaning:

Name Description
{Pager-Name} Defines the unique identifier of the pager configuration. You can create/obtain the desired pager instance by this identifier within your application.
servicetype Defines the way of creating an instance of the PagerManager. Details on the areas of validity can be taken from chapter Creation of objects.
conf.database-connection.value Refers to the database connection defined within /APF/config/core/database/{CONTEXT}/{ENVIRONMENT}_connections.ini. Details on creating database connections can be found in chapter ConnectionManager.
conf.entries-per-page.value Defines the number of entries per page (e.g. 10).
conf.url-param-page.value Defines the name of the url parameter for the page to display (e.g. page).
conf.url-param-count.value Defines the name of the url parameter to determine the amount of entries per page (z.B. count).
conf.statement-namespace.value Defines the namespace where the Pager searches for the SQL statement files that are used to query the amount of content and the entries itself (e.g. APF\modules\comments). Details on using statement files can be found in chapter statement files.
conf.count-statement.value Defiles the name of the statement file to load the total amount of entries (e.g. load_entries_count.sql).
conf.entries-statement.value Defiles the name of the statement file to load the entries for the current page (e.g. load_entry_ids.sql).
conf.statement-params.value Allows to define further static parameters that are considered during execution of the configured statements (e.g. foo:bar|bar:baz).
conf.ui-namespace.value Defines the namespace where the HTML representation of the pager navigation bar can be found (e.g. APF\modules\pager\pres\templates).
conf.ui-template.value Defines the template used to create the HTML representation of the pager navigation (e.g. pager_2).
conf.caching.value Allows you to cache results of pager queries during one session. This improves performance but at the same time possibly prohibits new entries from being displayed (e.g. true|false).
conf.dynamic-page-size.value Allows (true) or disallows (false) dynamic definition of the amount of entries per page via URL.
Please be aware that using the conf.dynamic-page-size.value option set to true may cause the statement runtime to increase applying a high number of entries per page.

Within your application you can create the desired pager instance as follows:

PHP code
use APF\modules\pager\biz\PagerManager; /* @var $pager PagerManager */ $pager = $this->getDIServiceObject('...', '{Pager-Name}');

2.2. Statement files

To allow easy usage of the PagerManager within your application specific parts have been extracted to configurable elements. This is true for the general configuration (see chapter 2.1) as well as the sql statements necessary to display the paging functionality.

Displaying the content and the navigation elements two SQL queries are used that are configured with the directives in brackets:

  • Total amount of entries (Pager.CountStatement or conf.count-statement.value)
  • Entries of the currently selected page (Pager.EntriesStatement or conf.entries-statement.value)

Both have to contain defined parameters and aliases in order the pager is able to evaluate the result information.

Statement files as well as configuration files are subjected to path and naming conventions as described in chapter Configuration. For instance, if you fill Pager.StatementNamespace or conf.statement-namespace.value with value VENDOR\ui and set Pager.CountStatement or conf.count-statement.value to load_entries_count.sql the database connection uses a file named:

Code
/VENDOR/config/ui/{CONTEXT}/{ENVIRONMENT}_load_entries_count.sql
2.2.1. Query of total entries

Requesting the total amount of entries is used to calculate the amount of pages to display. For this reason, a statement with the following content is required:

SQL statement
SELECT COUNT(...) AS EntriesCount ...

Alias EntriesCount is used by the PagerMapper to evaluate the result. Thus, the alias must be defined in this specific naming. The rest of the statement can be defined freely as required by your application.

The subsequent code box contains a statement as used within the Comment function module:

SQL statement
SELECT COUNT(*) AS EntriesCount FROM article_comments WHERE CategoryKey = '[CategoryKey]' GROUP BY CategoryKey;

Further hints on dynamic parameters within statements can be found in chapter 2.2.3.

2.2.2. Query of entries

The statement referred to by the Pager.EntriesStatement or conf.entries-statement.value directive is used to select the content of the currently selected page. For this reason, a statement with the following content is required:

SQL statement
SELECT ... AS DB_ID ... LIMIT [Start], [EntriesCount]

Alias DB_ID is used by the PagerMapper to evaluate the result. Thus, the alias must be defined in this specific naming. The limit clause is a fixed part of the statement as well and is used by the Pager to load the content of the desired page. The rest of the statement can be defined freely as required by your application.

The subsequent code box contains a statement as used within the Comment function module:

SQL statement
SELECT ArticleCommentID AS DB_ID FROM article_comments WHERE CategoryKey = '[CategoryKey]' ORDER BY Date DESC, Time DESC LIMIT [Start],[EntriesCount];

Further hints on dynamic parameters within statements can be found in chapter 2.2.3.

2.2.3. Dynamic parameters

All statements described in chapter 2.2.1 and 2.2.2 can be added any amount of static and dynamic parameters.

You may either use the configuration (direktives: Pager.StatementParameters or conf.statement-params.value) or directly apply dynamic parameters to the PagerManager while loading entries (methods: PagerManager::loadEntries() or PagerManager::loadEntriesByAppDataComponent()).

Scheme of the parameter definition within the configuration is as follows:

Code
param1:value1|param2:value2|...

Parameter and value are separated by : (colon) and value-couples are separated by | (Pipe).

Initialization of parameters is subjected to the following logic: initializing the configuration parameters the Pager tries to fill the values with data from the URL to. This allows manipulation of parameters by the URL. In case you do not wish to change parameters, please take care to not add parameters contained within the configuration to the URL.

2.3. Display of navigation control

Displaying the navigation control of the pager is controlled by the Pager.DesignNamespace and Pager.DesignTemplate or conf.ui-namespace.value and conf.ui-template.value directives.

The template referred to by both parameters is intended to create the page navigation control and another component to vary the amount of entries per page.

The APF ships a standard implementation for both use cases that can be extended or replaced by a custom implementation if desired.

Hints on the implementation of custom display elements can be found in chapter 3.3.

2.3.1. Simple control

In order to activate the simple control delivered with the APF the parameters of the configuration have to be set as follows:

APF configuration
; Option 1: Creation via PagerManagerFabric Pager.DesignNamespace = "APF\modules\pager\pres\templates" Pager.DesignTemplate = "pager" ; Option 2: Creation via DIServiceManager conf.ui-namespace.method = "setPagerUiNamespace" conf.ui-namespace.value = "APF\modules\pager\pres\templates" conf.ui-template.method = "setPagerUiTemplate" conf.ui-template.value = "pager"

The control includes a list of pages like this:

Code
Page | 1 | 2 |

The visual appearance can be adapted by CSS.

2.3.2. Extended control

In order to activate the extended control delivered with the APF the parameters of the configuration have to be set as follows:

APF configuration
; Option 1: Creation via PagerManagerFabric Pager.DesignNamespace = "APF\modules\pager\pres\templates" Pager.DesignTemplate = "pager_2" ; Option 2: Creation via DIServiceManager conf.ui-namespace.method = "setPagerUiNamespace" conf.ui-namespace.value = "APF\modules\pager\pres\templates" conf.ui-template.method = "setPagerUiTemplate" conf.ui-template.value = "pager_2"

The control includes a list of pages as well as the possibility to change the amount of entries per page like this:

Code
Page < 1 | 2 > Entries/Page: | 5 | 10 | 15 | 20 |

The visual appearance can be adapted by CSS.

2.4. Database connection

To get the PagerManager ready for your application specific parts have been extracted to configurable elements. This applies to the general configuration (see chapter 2.1), the SQL queries necessary to display the pager control (see chapter 2.2) as well as the database connection of your application.

The Pager is implemented to refer to the database connection of your application using the Pager.DatabaseConnection or conf.database-connection.value directives rather than to define separate connections to ease usage.

The following code box outlines using the existing connection comments for the Pager:

APF configuration
; Option 1: Creation via PagerManagerFabric Pager.DatabaseConnection = "comments" ; Option 2: Creation via DIServiceManager conf.database-connection.method = "setDatabaseConnectionName" conf.database-connection.value = "comments"

Details on defining database connections are described in chapter ConnectionManager.

3. Examples

The present chapter contains two typical code samples as well as hints on the implementation of custom HTML representations of the Pager.

Another use case example can be found in chapter Data layer of the comment function tutorial.

3.1. Loading entries

Method PagerManager::loadEntries() can be used to load the entries to display. As the name of the method reveals the call only returns the relevant ids of the entries to display.

The subsequent code sample shows how to use the Pager to display relevant entries along with a navigation element to jump between pages:

PHP code
namespace VENDOR\ui; use APF\core\pagecontroller\BaseDocumentController; use APF\modules\pager\biz\PagerManager; use DateTime; class DisplayTweetsController extends BaseDocumentController { public function transformContent() { $pager = $this->getPager(); $queryParameters = array( 'FromDate' => '2013-10-01', 'ToDate' => date('Y-m-d') ); $list = $pager->loadEntries($queryParameters); foreach ($list as $item) { $this->setPlaceHolder( 'tweets', $this->generateTweetView( $this->loadTweetById($item) ), true ); } $this->setPlaceHolder('pager', $pager->getPager($queryParameters)); } /** * @param int $id * @return Tweet */ private function loadTweetById($id) { ... } /** * @return PagerManager */ private function getPager() { return $this->getDIServiceObject('VENDOR\ui', 'TweetsPager'); } /** * @param Tweet $tweet * @return string */ public function generateTweetView(Tweet $tweet) { ... } }

3.2. Loading domain objects

In order to improve encapsulation of loading application and/or domain objects within your application you may want to use method loadEntriesByAppDataComponent() of the PagerManager. Using this function you can directly load application-specific objects.

The subsequent code sample is based on the (Document-)Controller of the previous chapter:

PHP code
namespace VENDOR\ui; use APF\core\pagecontroller\BaseDocumentController; use APF\modules\pager\biz\PagerManager; use DateTime; class DisplayTweetsController extends BaseDocumentController { public function transformContent() { $pager = $this->getPager(); $queryParameters = array( 'FromDate' => '2013-10-01', 'ToDate' => date('Y-m-d') ); /* @var $list Tweet[] */ $list = $pager->loadEntriesByAppDataComponent( $this->getServiceObject('VENDOR\data\TweetLoader'), 'loadTweetById', $queryParameters ); foreach ($list as $item) { $this->setPlaceHolder('tweets', $this->generateTweetView($item), true); } $this->setPlaceHolder('pager', $pager->getPager($queryParameters)); } /** * @return PagerManager */ private function getPager() { return $this->getDIServiceObject('VENDOR\ui', 'TweetsPager'); } /** * @param Tweet $tweet * @return string */ public function generateTweetView(Tweet $tweet) { ... } }

Class VENDOR\data\TweetLoader offers loadTweetById() to load an instance of VENDOR\ui\Tweet based on a given id.

3.3. Implementation of custom navigation elements

The APF module ships two standard implementations as noted in chapter 2.3. In case you intend to create your own implementations you may want to use the configuration directived Pager.DesignNamespace and Pager.DesignTemplate or conf.ui-namespace.value and conf.ui-template.value respectively.

To ease implementation the PagerManager provides a list of APF\modules\pager\biz\PageItem instances and further information that are required to generate a pager navigation bar. This data can be used within a DocumentController accessing the attributes of the current Document instance.

To gather the list of pages to display you can use the following code:

PHP code
namespace VENDOR\ui; use APF\core\pagecontroller\BaseDocumentController; use APF\modules\pager\biz\PageItem; class DisplayTweetsController extends BaseDocumentController { public function transformContent() { /* @var $pages PageItem[] */ $pages = $this->getDocument()->getAttribute('Pages'); ... } }

The below table describes all information the PagerManager provides as an attribute of the current documente:

Attribute Description
Pages List of pages that should be displayed for the current result (type: list of APF\modules\pager\biz\PageItem instances)
PageUrlParameterName Name of the URL parameter for the page to display.
CountUrlParameterName Name of the URL parameter to determine the amount of entries per page.
EntriesPerPage Amount of pages to display per page.
DynamicPageSizeActivated Defines, whether dynamic definition of the amount of entries per page via URL is allowed (true) or not (false).

Based on the above noted functions you can create custom representations of pafer navigation elements. Details can be taken from chapters 2.1.1 and 2.1.2.

As a start you may want to use the SimplePagerController and AdvancedPagerController controllers shipped with the APF.

Comments

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.