dbxapp Knowledge Module patterns

Module patterns

On this page
  1. Relationship to the mandatory module manual
  2. Module types
  3. Complete structure
  4. Configuration
  5. Router: Small and clear
  6. Service framework
  7. Data model
  8. Form method
  9. Reporting methodology
  10. Templates instead of PHP-HTML
  11. Call possibilities
  12. Request values
  13. AJAX, openWin and Confirm
  14. JSON-API as its own route
  15. Separate frontend and admin module
  16. Installation and scheme
  17. Assets and skin capability
  18. Recommended order for a new module
  19. Mandatory rules
  20. Real references

Official dbxapp website

A dbxapp module encapsulates a technical task. It receives requests via the module router, reads data via DD/dbxDB, using dbxForm and dbxReport and renders via dbxTPL. Kernel, global JavaScript ribs and central UI pipelines are reused.

Relationship to the mandatory module manual

Mandatory module manual is the normative Golden Path and contains a complete reference module. This chapter complements variants, larger divisions and real project patterns. If guidance conflicts, the binding module manual applies together with the current security invariants.

Module types

Species Example Task
Front end module dbxContact, dbxShop, dbxWorkflow public or user-related functions
Admin module dbxContact admin, dbxShop admin Protected management and reports
Infrastructure module dbxContent, dbxKi CMS, API or system services
Embedded module In principle, any suitable module Call from CMS/Templates about [modulus=...

Frontend and administration can be separated. This keeps rights, design, routes and technical interface clear.

Complete structure

dbx/modules/myTasks/
myTasks.class.php
cfg/config.php
include/myTasksService.class.php
dd/myTask.dd.php
fd/myTask-form.fd.php
fd/rpt-myTask-selection.fd.php
tpl/htm/start.htm
tpl/htm/my-task-form.htm
tpl/htm/my-task-report.htm
tpl/htm/my-task-row-action.htm
design/css/myTasks.css
design/js/myTasks.js
tpl/img/myTasks.png
README.md

Not every module needs all folders. A pure output module can do without DD and FD; A API module may not require HTML templates. The separation of router, service and template still makes sense.

Configuration

cfg/config.php sets activation and groups:

<?php
$config['version'] = '1.0';
$config['activ'] = '1';
$config['groups'] = '*';
$config['page_size'] = '30';
?>

For an admin module:

<?php
$config['version'] = '1.0';
$config['activ'] = '1';
$config['dbxConfig_modul'] = 'secure';
$config['groups'] = 'admin';
?>

Read via the central configuration:

$pageSize = (int)dbx()->get_cfg('myTasks', 'page_size');

Do not create a second JSON/ENV configuration for the same values.

Router: Small and clear

File myTasks.class.php:

<?php
namespace dbx\myTasks;
class myTasks {
public function run() {
$run = dbx()->get_modul_var('dbx_run1', 'report', 'parameter');
$service = dbx()->get_include_obj('myTasksService', 'myTasks');
switch ($run) {
case 'form':
case 'edit':
return $service->form();
case 'delete':
return $service->delete();
case 'detail':
return $service->detail();
case 'api':
return $service->api();
case 'report':
case 'list':
default:
return $service->report();
}
}
}
?>

The router only decides which technical method runs. Database queries, shape design and long HTML fragments do not belong in the switch.

Real router patterns:

  • dbx/modules/dbxContact/dbxContact.class.php: Small frontend router.
  • dbx/modules/dbxWorkflow/dbxWorkflow.class.php: Start, Run and Overview.
  • dbx/modules/dbxShop/dbxShop.class.php: Extensive domain router.
  • dbx/modules/dbxWorkflow_admin/dbxWorkflow_admin.class.php: Delegating admin wrapper.

Service framework

File include/myTasksService.class.php:

<?php
namespace dbx\myTasks;
class myTasksService {
private $dd = 'myTasks|myTask';
private function baseUrl($run = 'report', array $params = array()) {
$url = '?dbx_modul=myTasks&dbx_run1=' . rawurlencode($run);
foreach ($params as $key => $value) {
$url .= '&' . rawurlencode((string)$key) . '=' .
rawurlencode((string)$value);
}
return $url;
}
public function detail() {
$rid = (int)dbx()->get_modul_var('rid', 0, 'int');
$row = dbx()->get_system_obj('dbxDB')->select1($this->dd, $rid);
if ((int)($row['id'] ?? 0) <= 0) {
return dbx()->get_system_obj('dbxTPL')->get_tpl(
'dbx|alert-warning',
array('msg' => 'Aufgabe nicht gefunden.')
);
}
return dbx()->get_system_obj('dbxTPL')->get_tpl('myTasks|detail', array(
'id' => (int)$row['id'],
'title' => (string)$row['title'],
'description' => (string)($row['description'] ?? ''),
));
}
}
?>

The service class may be further divided in large domain areas, for example into repository, service, provider adapter or renderer. The shop uses exactly this division.

Decompose large sequence classes without runtime overhead

If multiple responsibilities explicitly share the same request state, a direct trait composition may make more sense than a chain of forwarding objects. Technically named blocks remain binding, explicit require once-Inclusions and a small visible main class. Magic dispatch, dynamic trait search and mutual service dependencies are not allowed.

The reference is dbxContent cms and dbxShopAdmin: The main classes hold only state, entry and action contract. Form, report, page actions, tree, catalogue, order and the individual media tasks are in named *Service.trait.php- Files. PHP composes these methods directly into the class; This results in neither additional service objects nor additional database queries. Persistent technical boundaries such as: dbxContentCmsPersistenceService remain independent classes.

The contract shall include: dbxModuleDecomposition_contract_test.php checked: The sequence class remains under 250 lines, a responsibility block under 1000 lines. Source text contracts read the explicit composition about dbxModuleSourceBundle.phpInstead of forcing implementation back into a monolith. Data access, output and input continue to be used within the blocks dbxDB, dbxTPL, dbxForm and dbxReport.

Data model

A new persistent table gets a complete DD:

dd/myTask.dd.php -> myTasks|myTask -> Tabelle my_task

The DD follows the directly readable dbxapp export format: TABLE, FIELDS and INDEXES will be with $table[...], $field[...], $fields[] = $field, $index[...] and $indexes[] = $index explicitly defined. A local $addField-Closure or DD-Includes are not allowed for this.

Form views and report filters are separate:

fd/myTask-form.fd.php
fd/rpt-myTask-selection.fd.php

The full DD/FD sample is under dbxDB, dbxDD and FD.

Form method

public function form() {
$rid = (int)dbx()->get_modul_var('rid', 0, 'int');
$data = $rid > 0
? dbx()->get_system_obj('dbxDB')->select1($this->dd, $rid)
: array('status' => 'open', 'active' => 1);
$form = dbx()->get_system_obj('dbxForm');
$form->init('my-task-form');
$form->_dd = $this->dd;
$form->_fd = 'myTasks|myTask-form';
$form->_data = $data;
$form->_rid = $rid;
$form->_action = $this->baseUrl('form', array(
'rid' => $rid > 0 ? $rid : 'new',
));
$form->add_flds();
if ($form->submit() && !$form->errors()) {
$ok = $form->save_post($this->dd, $rid > 0 ? $rid : 'new');
$form->_msg_success = $ok ? 'Aufgabe gespeichert.' : '';
$form->_msg_error = $ok ? '' : 'Speichern fehlgeschlagen.';
}
return $form->run();
}

Further possibilities such as individual fields, callbacks, shells and embedded reports are available under dbxForm.

Reporting methodology

public function report() {
$db = dbx()->get_system_obj('dbxDB');
$report = dbx()->get_system_obj('dbxReport');
$report->init('my-task-report');
$report->_dd = $this->dd;
$report->_action = $this->baseUrl('report');
$report->_pages = true;
$report->_create_row_edit = true;
$report->_create_row_delete = true;
$report->create_selection_fields('myTasks|rpt-myTask-selection');
$search = $report->get_fld_val('dbx_rwhere', '', 'sqlsearch|max=64');
$rows = max(10, min(100, (int)$report->get_fld_val('dbx_rrows', 30, 'int')));
$pos = max(0, (int)$report->get_fld_val('dbx_rpos', 0, 'int'));
$sort = $report->get_fld_val('dbx_rsort', 'title', 'parameter');
$desc = strtoupper((string)$report->get_fld_val('dbx_rdesc', 'ASC', 'parameter'));
if (!in_array($sort, array('id', 'title', 'status', 'update_date'), true)) {
$sort = 'title';
}
if (!in_array($desc, array('ASC', 'DESC'), true)) {
$desc = 'ASC';
}
$where = array('trash' => 0);
if ($search !== '') {
$where['search'] = array(
'value' => $search,
'like' => array('title', 'description'),
'mode' => 'contains',
);
}
$report->_rflds = array(
'id' => 'ID',
'title' => 'Titel',
'status' => 'Status',
'update_date' => 'Aktualisiert',
);
$report->_rpt_format = array('update_date' => 'php-datetime-usr');
$report->_rrows = $rows;
$report->_rpos = $pos;
$report->_count_all = $db->count($this->dd, array('trash' => 0));
$report->_rcount = $db->count($this->dd, $where);
$report->_rdata = $db->select(
$this->dd, $where,
array('id', 'title', 'status', 'update_date'),
$sort, $desc, '', $rows, $pos
);
return $report->run();
}

Multi-selection, actions, HTML fields and TPL/Grid-Modus are under dbxReport described.

Templates instead of PHP-HTML

return dbx()->get_system_obj('dbxTPL')->get_tpl('myTasks|detail', array(
'title' => $row['title'],
'status' => $row['status'],
));
<article class="card my-task-detail">
<div class="card-body">
<h2 class="h4">{title}</h2>
<p class="text-muted">{status}</p>
<div>{description}</div>
</div>
</article>

Possible template types:

  • Page/panel template for a single output.
  • mould template comprising: [dbx:form and {obj:*.
  • report template with dbx split and [rpt:row.
  • row/card template for mode = 'tpl'.
  • Mail, PDF or print template within the intended pipeline.

Templates can be language-dependent as name_de.htm, name_en.htm etc. present. dbxTPL uses the active language variant and then the neutral fallback.

Menu entries of the active main module

A main module can register an entry for the existing user or admin menu without setting up its own menu system:

$menu = dbx()->get_include_obj('dbxMenuSlot', 'dbxMenu');
$menu->register('user', 'menu-user', array('count' => $count));
if (dbx()->can('admin')) {
$menu->register('admin', 'menu-admin');
}

The templates tpl/htm/menu-user.htm and tpl/htm/menu-admin.htm provide only the structure matching the existing menu, usually one or more <li>Elements. Dynamic values are transmitted as template data; the module is not re-run. Language variants solved dbxTPL As usual.

The customer menu templates decide on the position. They contain optional {dbx:module menu user and {dbx:module menu admin. If a slot or registration is missing, the output remains empty. Only the active main module may register; embedded modules within an already rendered CMS content must not subsequently modify the menu. Guest-dependent posts must be deterministic for URL, language, and design to remain compatible with the full-page cache.

Call possibilities

Direct module request

?dbx_modul=myTasks&dbx_run1=report
?dbx_modul=myTasks&dbx_run1=form&rid=12

CMS or template inclusion

[modul=myTasks]dbx_run1=report&status=open[/modul]

Multiple instances on the same page

[modul=myTasks]dbx_run1=report&status=open[/modul]
[modul=myTasks]dbx_run1=report&status=done[/modul]

Use form and report templates {i} in IDs and Targets. As a result, parameter, AJAX and UI states of the instances remain separate.

Request values

$rid = dbx()->get_modul_var('rid', 0, 'int');
$status = dbx()->get_modul_var('status', 'open', 'parameter');
$search = dbx()->get_modul_var('q', '', 'sqlsearch|max=64');

The third parameter is the validator rule. Values not directly from $ GET or $ POST in SQL, templates or file paths. dbxForm has its own request pipeline for its fields; The router only reads route parameters.

AJAX, openWin and Confirm

Existing JavaScript-Libs are declaratively connected via Klassen/Datenattribute:

<a class="btn btn-primary dbx-win"
href="?dbx_modul=myTasks&amp;dbx_run1=form&amp;rid={id}"
data-dbx="lib=openWin|title=Aufgabe bearbeiten|width=70%|height=80%">
Bearbeiten
</a>
<a class="btn btn-danger dbxAjax dbxConfirm"
href="?dbx_modul=myTasks&amp;dbx_run1=delete&amp;rid={id}"
data-confirm="Wirklich löschen?" data-confirm-buttons="yesno"
data-target="dbx_target_{i}" data-replace="target">
Löschen
</a>

Do not install a second modal, AJAX or Confirm system in the module.

dbx ajax=1 It is not manually attached to normal links. Only ajax.js sets the Ajax context for the request it executes. A link to open a full CMS or admin page is left without dbx ajax.

If an action is to be confirmed and then loaded in a window, Confirm and openWin handlers must not simultaneously process the same click. Either confirm.js handles the declarative continuation, or the module waits programmatically for dbx.confirm.open() Then call them first. action === "yes" The ajax/openwin step.

JSON-API as its own route

public function api() {
$action = dbx()->get_modul_var('action', 'list', 'parameter');
if ($action === 'list') {
$rows = dbx()->get_system_obj('dbxDB')->select(
$this->dd,
array('active' => 1, 'trash' => 0),
array('id', 'title', 'status'),
'title', 'ASC', '', 100, 0
);
dbx()->json_response(array('ok' => 1, 'items' => $rows));
}
dbx()->json_response(array(
'ok' => 0,
'message' => 'Unbekannte Aktion.',
));
}

API and HTML are separate response types. A API endpoint does not render a form template, and a normal AJAX form action usually delivers HTML for your target. Rights apply identically to both paths.

Separate frontend and admin module

Recommended for larger applications:

myTasks/ öffentliche/benutzerbezogene Anzeige und Aktionen
myTasks_admin/ Installation, Pflege, Reports und Konfiguration

Common logic can be used via clearly named classes of the domain module. However, the admin module must not circumvent the rights check of the domain module. A good real pattern dbxShop and dbxShop admin.

Installation and scheme

An installation path synchronizes only the DDs of its module:

public function install() {
$dd = dbx()->get_system_obj('dbxDD');
$dd->sync_dd_to_db('myTasks', 'myTask', 'reset');
do {
$state = $dd->sync_dd_to_db('myTasks', 'myTask', 'apply');
} while (($state['status'] ?? '') === 'running');
return dbx()->get_system_obj('dbxTPL')->get_tpl('dbx|alert-info', array(
'msg' => ($state['status'] ?? '') === 'finished'
? 'Modul installiert.'
: (string)($state['message'] ?? 'Installation fehlgeschlagen.'),
));
}

Installation is an admin action. It does not run on every normal request.

Assets and skin capability

Module-specific CSS and JavaScript are below design/css or design/js. CSS uses the variables and components provided by the active design. Do not wire colours, distances or backgrounds so hard that dbxapp, Flowers or other skins become unreadable.

JavaScript expands the existing libs and initializes repeatable even after AJAX has used new HTML content.

Recommended order for a new module

  1. Determine domain purpose, user groups and routes.
  2. Read a similar existing module.
  3. Model DD and, if applicable, FD.
  4. Create templates and unique targets.
  5. Implement small router and domain service.
  6. Use dbxForm for input and dbxReport for lists.
  7. Protect admin access and installation separately.
  8. Direct request and [modulus=...Testing embedding.
  9. Test AJAX, openWin, Confirm, multiple instances and active skins.
  10. Update Doxygen and Module-README.

The current dbxWizard can create a new module or additions to an existing module. It validates module and file names, restricts all goals to dbx/modules/{Module } /, can generate DD/FD, router, service, form, report and templates and provides backups before overwrites files/module-backup/ an. After generation, PHP syntax and the route with dbx/modules/dbxAdmin/tests/dbxWizard_generation_test.php checked.

Mandatory rules

  • Module code remains under dbx/modules/{Module } /.
  • Keep the router small; Logic in Include/Service Classes.
  • Data access via dbxDB and DD, entries via dbxForm, lists via dbxReport.
  • Issued via dbxTPL; No large HTML strings in PHP.
  • Configuration over cfg/config.php and dbx()->get cfg().
  • Request values with get module var() or dbxForm.
  • Use existing AJAX, openWin, Confirm and Core Libs.
  • No private db()-, tpl()- or create escape aliases that only exist dbx()-Transmitting methods.
  • Do not replicate automatic DD system fields in the module.
  • Frontend, Admin, API and installation each have clear rights and response types.
  • Use multiple instances {i} and separate targets.
  • Module surfaces remain responsive and skin-capable.
  • Pure GET navigation remains tokenless. Writing GET actions use the existing action token in addition to module and DD rights.

Real references

  • dbx/modules/dbxAdmin/include/dbxWizard.class.php: Current generator for router, service, DD, FD, form and report.
  • dbx/modules/dbxContact: A compact frontend/admin pattern.
  • dbx/modules/dbxWorkflow: declarative technical process with its own engine.
  • dbx/modules/dbxShop and dbxShop admin: large application with repository, service, providers, frontend and administration.
  • Mandatory module manual — complete and normative Golden Path.
  • dbxDB, dbxDD and FD, dbxForm and dbxReport.