[inhalt]
Projekt BISO 3 - Handbuch

BISO 3 - ExtJS-Frontend-Neubau - Architektur

BISO 3 ist das Projekt zur Erneuerung des Sencha ExtJS 4.x-UIs. Wir lösen es mit der neuesten Sencha-Version (aktuell: 7.3) ab. Im gleichen Zug können wir gleich ein paar Anpassungen in der Architektur vornehmen.

Projekt-Folder-Struktur

Die bisherige Struktur sieht so aus:

.                          # --> Docker-Volume auf /var/www/html
├── ....
└── webroot                # --> Apache-Webroot
    ├── app.js             # Sencha App-Start
    ├── app                # Sencha App
    ├── config.php         # BISO-Projekt-Config (gaia config)
    ├── backend            # BISO-Backend (PHP-Klassen)
    ├── components         # Vendor-Libs
    ├── data               # Userdaten
    ├── db_migrations
    ├── packages           # Ext-Packages (Thema)
    │   └── poseidon
    ├── reporting          # Backend-Reporting-Scripte
    └── templates          # Kunden-spezifische Backend-Templates

Problematisch an dieser Struktur ist, dass alle Dateien im Webroot liegen, und somit auch vom Webroot aus zugänglich sind.

Das soll mit einer neuen Struktur verhindert werden:

.                          # --> Docker-Volume auf /var/www/html
├── ....
└── webroot                # webroot selber wird vom Webserver aus nicht ausgeliefert
    ├── public             # --> Apache-Webroot
    │   ├── app            # Sencha App
    │   ├── app.js         # Sencha App-Start
    │   ├── backend        # Webroot-Pfad "/backend"
    │   │   ├── .htaccess  # redirect alle Requests auf index.php
    │   │   └── index.php  # Bootstrap: gaia-Include ausserhalb public/
    │   ├── index.html     # Frontend-Startseite (ExtJS start)
    │   ├── packages       # Ext-Packages (Thema)
    │   │    └── poseidon
    │   └── components     # Vendor-Libs JavaScript (Ext etc.)
    ├── config.php         # BISO-Projekt-Config (gaia config)
    ├── backend            # BISO-Backend (PHP-Klassen)
    ├── vendor             # Vendor-Libs composer (php-only)
    ├── data               # Userdaten
    ├── db_migrations
    ├── reporting          # Backend-Reporting-Scripte
    └── templates          # Kunden-spezifische Backend-Templates

Das Webroot, welches der Webserver "sieht", wird also eine Stufe nach unten/innen geschoben. So können alle weiteren Daten, welche nicht vom Web zugänglich sein müssen, ausserhalb des Webroots platziert werden, es werden keine Daten exponiert.

Docker-Struktur

Wie oben aufgezeigt ändert sich für den Entwicklungs-Container folgendes:

Als Konsequenz muss dies dann auch auf den produktiven Systemen so umgebaut werden.

neue Ext-App-Struktur

Neu wird die Ordnerstruktur der Ext-Applikation anhand der Datenmodelle, nicht mehr anhand der Funktion organisiert:

Anstatt wie bisher:

app/store/Benutzer.js,
app/model/Benutzer.js,
app/proxy/Benutzer.js,
app/view/benutzer/List.js

wird neu

app/benutzer/Store.js,
app/benutzer/Model.js,
app/benutzer/Proxy.js,
app/benutzer/ListView.js

verwendet.

Implementation: Vorgehen beim Entwickeln

App-Architektur / Tools

Globale Events

Neu werden globale Events alle von KP.GlobalEventsController abgehandelt. Dieser stellt auch die Event-Namen als Statics zur Verfügung, welche anstelle reiner Strings verwendet werden soll.

Ein globaler Event wird folgendermassen ausgelöst:

Ext.globalEvents.fireEvent(KP.GlobalEventsController.getShowBeratungsfall(), rec.getId());

Alle globalen Events werden direkt von diesem Controller abgehandelt, NICHT mehr von einzelnen View-Controllern!

Global Store Helper

Anstelle:

let situationStore = Ext.StoreManager.lookup('Situation.Fall');
if (!situationStore) {
    situationStore = Ext.create('KP.situation.Store', {
        autoLoad: false,
        remoteFilter: true,
        remoteSort: true,
        pageSize: -1,
        storeId: 'Situation.Fall'
    });
    situationStore.getProxy().extraParams.onlyActive = true;
    situationStore.load();
}

soll nun das KP.tools.StoreManagerHelperMixin verwendet werden: Diese Helper- Funktion erstellt den Store anhand der Config, und registriert ihn im StoreManager. Bei der 2. Verwendung kommt er dann direkt aus dem StoreManager.

// mixins: ['KP.tools.StoreManagerMixin']
{
    xtype: 'combobox',
    // Mixin definiet this.storeFromManager
    store: this.storeFromManager({
        type: 'Situation', // store-alias, must
        storeId: 'Situation.Fall', // must
        // optionale Store-config:
        autoLoad: false,
        remoteFilter: true,
        remoteSort: true,
        // auch Extra-Params für den Proxy sind möglich:
        proxy: { extraParams: { onlyActive: true } }
    }),
},

Store-Filter-Callback: remoteFilterPromise

Neu in ExtJS 7 kann in Ext.data.Store.filter() keine Callback für den load-Event mehr angegeben werden. Damit wir bei Remote-Stores trotzdem auf den load()-Event nach einem filter() erhalten, haben wir die Methode remoteFilterPromise() auf dem KP.base.Store entwickelt:

store.remoteFilterPromise({
    property: 'id',
    'value': 4
}).then(() => {
    // ... do something
});

// oder:
await store.remoteFilterPromise({
    property: 'id',
    'value': 4
});
// do something

Beratungsfall: Öffnen von Child-Panels

Im Beratungsfall-Form öffnen wir Unter-Forms (z.B. BF-Termin) nicht mehr in Windows, sondern als weiteres Panel im übergeordneten Card-Layout. Dies ist vom UI her intuitiver und tablet-tauglicher.

Um aus dem Beratungsfall-Form heraus ein Unter-Panel zu öffnen, muss das Beratungsfallpanel ( KP.beratungsfall.BaseForm oder Kindklasse) ein event opencarditem gefeuert werden, mit der zu öffnenden Komponente als Parameter:

Beispiel:

let fallpanel = this.getFallPanel(); // KP.beratungsfall.BaseForm

let form = Ext.create('KP.bf_notiz.Form');
fallpanel.fireEvent('opencarditem', form);

Bei Listen in der Sublist-Tab-Panel-Komponente wird dies schon selbständig gemacht: Alle list-Komponenten in beratungsfallform [itemId=sublists] relayen den opencarditem event automatisch: Dort kann also folgendes direkt auf der Grid-Komponente gemacht werden:

let form = Ext.create('KP.bf_notiz.Form');
let verlaufgrid = getVerlaufGridIrgendwie().fireEvent('opencarditem', form);