# SQUEEZE 2 Customizing Handbuch

Dieses Handbuch soll als Leitfaden für das Customizing dienen.

# Einleitung User Exits

User Exits dienen dem Erweitern / Customizing von SQUEEZE. Sollten Standard-Funktionen nicht ausreichen, kann mittels PHP-Code das Verhalten der Software angepasst werden.

## Grundlagen

User Exits sind grundsätzlich mandantenspezifisch. D. h., dass jeder Mandant eines SQUEEZE Servers seine eigenen User Exits definieren und einsetzen kann. Daher befinden sich die jeweiligen User Exits im Repository des Mandanten.

## Cloud vs. On Premise

Die Customizing-Optionen unterscheiden sich je nach Installationsart.

Bei On Premise Installationen stehen alle Customizing-Funktionen frei zur Verfügung. I. d. R. handelt es sich hier um Installationen bei einzelnen Endkunden. Mehr Details in: [Customizing On Premise](https://docs.squeeze.one/books/squeeze-user-exit-handbuch/page/customizing-on-premise "Customizing On Premise")

Bei Cloud-Mandanten ist die Bearbeitung von User Exits je nach Projekt bzw. Kunde eingeschränkt. Hier muss mit dem jeweiligen Ansprechpartner der DEXPRO oder des betreuenden Partners abgestimmt werden, ob ein Customizing möglich ist. Mehr Details in: [Customizing In Der Cloud](https://docs.squeeze.one/books/squeeze-user-exit-handbuch/page/customizing-in-der-cloud "Customizing In Der Cloud")

## Entwicklungsumgebung

### IDE / Editor

Die Entwicklung von User Exits ist grundsätzlich mit jedem Text-Editor möglich. **Wir raten allerdings stark zur Verwendung von [Visual Studio Code](https://code.visualstudio.com/).** Ergänzend setzen wir auf folgende Extensions:

- [PHP Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client)
- [vscode-icons](https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons)

[![image-1647871315757.png](https://docs.squeeze.one/uploads/images/gallery/2022-03/scaled-1680-/image-1647871315757.png)](https://docs.squeeze.one/uploads/images/gallery/2022-03/image-1647871315757.png)

### Einrichten von Code Completion

<p class="callout info">Code Completion ist ab Squeeze 2.3 möglich.</p>

Wir empfehlen die Einrichtung von Code Completion in ihrer IDE. Dieses Feature bietet ein wertvolles Werkzeug bei der Entwicklung *und dem Support* von User Exits. Vorteile der Verwendung sind:

- Ihre Entwicklungsumgebung zeigt Ihnen bessere (und vor allem korrektere) Fehlermeldungen an.
- Sie sehen direkt im Editor die Dokumentation von Methoden und Klassen, die Sie verwenden.
- Falls Sie Code nutzen, der in zukünftigen Versionen verändert oder entfernt wird, sehen sie das direkt im Editor.
- Ihr Editor schlägt ihnen Vervollständigungen vor, während Sie tippen. So sparen Sie sich (häufig) den Blick in eine Dokumentation.

[![image-1673349341766.png](https://docs.squeeze.one/uploads/images/gallery/2023-01/scaled-1680-/image-1673349341766.png)](https://docs.squeeze.one/uploads/images/gallery/2023-01/image-1673349341766.png)

#### Visual Studio Code

Dazu sind die folgenden Schritte notwendig:

1. Installieren sie [PHP Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client)
2. Legen sie eine Stubs-Datei (s. Abschnitt unten) im Ordner ab, den Sie mit Visual Studio Code öffnen. Es eignet sich die Ablage bspw. im Repository oder dem Mandantenordner im Repository.
3. Konfigurieren Sie folgende Einstellungen in Visual Studio Code ([Wie geht das?](https://code.visualstudio.com/docs/getstarted/settings)). Passen Sie dabei ggf. die PHP Version auf die von ihnen eingesetzte PHP Version an.

```JSON
{
  "php.suggest.basic": false,
  "intelephense.files.maxSize": 100000000,
  "intelephense.environment.phpVersion": "7.4.22"
}
```

##### Code Completion wird nicht angezeigt

Falls Autocomplete nicht funktioniert, dann prüfen Sie die Log-Ausgaben der Intelephense Extension:

- Output Konsole öffnen
- Im Dropdown auf Intelephense umstellen

Falls Sie hier einen Fehler sehen, hilft das ggf. bei der Problemlösung. Im Screenshot wurde bspw. die Einstellung bzgl. der Dateigröße nicht gesetzt.

[![image-1673347764604.png](https://docs.squeeze.one/uploads/images/gallery/2023-01/scaled-1680-/image-1673347764604.png)](https://docs.squeeze.one/uploads/images/gallery/2023-01/image-1673347764604.png)

#### Stubs-Datei

Die Stubs-Datei ist eine eine Datei mit PHP-Quellcode, die dafür sorgt, dass Visual Studio Code die Datentypen, Klassen und Funktionen von Squeeze kennt.

<p class="callout info">Die Stubs werden aktuell noch manuell erzeugt. Fragen Sie gern im Support nach, ob sie eine Stub-Datei erhalten können.</p>

# Customizing in der Cloud

<p class="callout info">Folgendes gilt nur bei Mandanten / Umgebungen, für welche Customizing in der Cloud freigeschalten wurde.  
Ggf. treffen nur einige Funktionen in Ihren Kontext zu.</p>

## Web-IDE

Es ist möglich [Visual Studio Code](https://code.visualstudio.com/) als Web-IDE für die Programmierung von User Exits einzusetzen. Insofern dies freigeschalten ist, erhalten Sie eine URL über welche auf die IDE im Browser zugegriffen werden kann.

Die Verwendung dieser IDE ist im Prinzip gleich mit der auf On Premise Umgebungen, allerdings sind einige Funktionen (bspw. das Terminal) gesperrt.

# Leitfäden

# Best Practices Für Die User Exit Entwicklung

Dieser Leitfaden dokumentiert Coding-Richtlinien, deren Einhaltung zu fehlerfreien und updatefähigen User Exits führen sollen.

Motivation hinter dem Leitfaden ist, dass in der Vergangenheit häufig User Exits programmiert wurden, welche bspw. auf Variablen oder Methoden zugeriffen haben, die eigentlich nicht für den Gebrauch in User Exits entworfen wurden. Das wiederum erhöht die Wahrscheinlichkeit für Fehler bei der Ausführung von User Exits, besonders nach Updates von SQUEEZE. Aus dieser Motivation wird perspektivisch eine [Scripting API](https://docs.squeeze.one/books/squeeze-customizing-handbuch/page/scripting-api "Scripting API") entwickelt und bereitgestellt.

## Best Practices

### Methoden vs. Klassen-Eigenschaften

<p class="callout info">Grundsätzlich ist die Verwendung von Methoden dem direkten Zugriff auf [Klassen-Eigenschaften](https://www.php.net/manual/de/language.oop5.properties.php) vorzuziehen.</p>

```PHP
$creditor = $xDoc->getFieldByName("Creditor");

// Bad
$creditor->value->value = "New Creditor";

// Good
$creditor->getValue()->setValue("New Creditor");
```

Der direkte Zugriff auf Eigenschaften, insbesondere bei Verkettung (`$creditor->value->value`) kann zu Exceptions führen, falls eine Eigenschaft null ist. Außerdem kann so nicht gewährleistet werden, dass die Werte, die gesetzt werden sollen, normalisiert werden sollen.

### Deprecations - Veraltete APIs nicht verwenden

Wenn Sie ihre IDE korrekt konfiguriert haben, werden Ihnen veraltete Methoden, Felder und Klassen durchgestrichen dargestellt. Das ist im folgenden Screenshot zu sehen:

[![image-1673348307446.png](https://docs.squeeze.one/uploads/images/gallery/2023-01/scaled-1680-/image-1673348307446.png)](https://docs.squeeze.one/uploads/images/gallery/2023-01/image-1673348307446.png)

Hier ist bspw. das Feld `value` der Klasse `xDocValue` veraltet. Im Tooltip ist zu lesen:

> Please use the setter, getter or adder to modify this field. This field will not be removed but could eventually be made private.

Sie sollten hier also versuchen Getter und Setter zu verwenden:

[![image-1673348569743.png](https://docs.squeeze.one/uploads/images/gallery/2023-01/scaled-1680-/image-1673348569743.png)](https://docs.squeeze.one/uploads/images/gallery/2023-01/image-1673348569743.png)

<p class="callout info">Deprecations werden phasenweise eingeführt als frühzeitige Warnungen, dass der Standard sich zukünftig verändern **kann**.  
  
Solche Veränderungen werden in den Changelogs als Breaking Changes festgehalten, damit Sie bei einem Update prüfen können, ob es Handlungsbedarf gibt.</p>

### Fehlende Funktionalitäten

Sollten Funktionalitäten in Script-Libraries oder der [Scripting API](https://docs.squeeze.one/books/squeeze-customizing-handbuch/page/scripting-api "Scripting API") fehlen, ist es sinnvoll die fehlenden Funktionalitäten bspw. im Forum zu diskutieren. So können Funktionalitäten, die häufiger benötigt werden in den Standard aufgenommen werden.

### Wiederverwendbare Methoden

Es bietet sich an einzelne Funktionen, die in User Exits umgesetzt werden müssen, als seperate, wiederverwendbare Methoden umzusetzen. Der Vorteil davon ist, dass die Methoden einfacher kopiert und bei anderen Systemen erneut eingesetzt werden können, vorausgesetzt die Produktversionen sind kompatibel.

Die Zusammenfassung von gleichartigen Methoden in Klassen ist zu empfehlen.

# Migrations-Guide: SqueezeConfig zu TenantConfig

In Squeeze Version 2.32 wird die seit 2021 veraltete und als "deprecated" markierte Klasse `SqueezeConfig` endgültig durch den neuen, über Dependency Injection (DI) verfügbaren Service `TenantConfig` ersetzt. Dieser Guide beschreibt, wie bestehende UserExits, die `SqueezeConfig` verwenden, auf den neuen Standard migriert werden können.

## Hintergrund

`SqueezeConfig` basierte oft auf statischen Zuständen oder manueller Instanziierung, was die Testbarkeit und Flexibilität einschränkte. Der neue `TenantConfig`-Service ist Teil der V2-Architektur und nutzt den modernen DI-Container von SQUEEZE.

## Was hat sich geändert?

<table id="bkmrk-merkmal-alt%3A%C2%A0squeeze"><thead><tr><th>Merkmal</th><th>Alt: `SqueezeConfig`</th><th>Neu: `TenantConfig`</th></tr></thead><tbody><tr><td>**Namespace**</td><td>`Squeeze\SqueezeConfig`</td><td>`App\V2\Base\Config\TenantConfig`</td></tr><tr class="intellij-row-even"><td>**Instanziierung**</td><td>`new SqueezeConfig()`</td><td>Über Dependency Injection oder den Service-Container</td></tr><tr><td>**Konfigurations­zugriff**</td><td>`$config->get('key')`</td><td>`$config->get('key')` (wenn der Wert `null` sein darf)

oder

`$config->mustGet('key')` (wenn der Wert immer gesetzt sein muss)

</td></tr><tr class="intellij-row-even"><td>**Typsicherheit**</td><td>Rückgabe meist `string` oder `mixed`</td><td>Spezifische Methoden wie `getInt()`, `getBool()`, `getArray()`</td></tr></tbody></table>

## Migration von UserExits

In UserExits gibt es zwei primäre Wege, um auf die Konfiguration zuzugreifen.

<p class="callout warning">**Hinweise  
  
Namespaces:** Achten Sie beim Import von `TenantConfig` auf den korrekten Namespace `App\V2\Base\Config\TenantConfig`.  
**Statische Methoden:** Vermeiden Sie statische Zugriffe auf Konfigurationen, wo immer möglich. Nutzen Sie die Instanz-Methoden des `TenantConfig`-Services.</p>

### 1. In klassischen Funktions-basierten UserExits (Legacy)

Wenn Ihr UserExit eine einfache PHP-Funktion ist (z. B. `AfterExtraction`), die eine `xDoc`-Instanz erhält, aber keinen Zugriff auf DI hat, können Sie den Service-Container manuell abfragen:

#### Vorher (SqueezeConfig):

```php
use Squeeze\SqueezeConfig;

function AfterExtraction(xDoc $xDoc) {
    $config = new SqueezeConfig();
    $repo = $config->get('repository.root');
    // ...
}

```

#### Nachher (TenantConfig über Container):

```php
use App\V2\Base\Config\TenantConfig;
use App\V2\Base\DI\Dependencies;

function AfterExtraction(xDoc $xDoc) {
    /** @var TenantConfig $config */
    $config = Dependencies::getContainer()->get(TenantConfig::class);
    
    // Zugriff mit Standardwert
    $repo = $config->get('repository.root');
    
    // Zugriff mit Exception, falls Key fehlt (sicherer)
    $repo = $config->mustGet('repository.root');
    
    // Typsicherer Zugriff
    $maxSize = $config->getInt('upload.maxSize', 1024);
    // ...
}

```

### 2. In modernen V2 UserExits (Klassen-basiert)

Wenn Sie bereits V2-UserExits verwenden, die als Klassen implementiert sind (z. B. Erben von `AbstractUserExitBase`), sollten Sie `TenantConfig` einfach per Constructor Injection anfordern.

#### Beispiel:

```php
namespace App\V2\Modules\UserExit\UserExits;

use App\V2\Base\Config\TenantConfig;

class MyCustomUserExit extends AbstractUserExitBase {
    public function __construct(
        protected UserExitRegistry $userExitRegistry,
        private TenantConfig $tenantConfig // Neu hinzugefügt
    ) {
        parent::__construct($userExitRegistry);
    }

    public function call(xDoc $xDoc) {
        $repo = $this->tenantConfig->mustGet('repository.root');
        // ...
    }
}

```

## Wichtige Methoden im Vergleich

<table id="bkmrk-squeezeconfig-tenant"><thead><tr><th>SqueezeConfig</th><th>TenantConfig (Empfehlung)</th><th>Beschreibung</th></tr></thead><tbody><tr><td>`get($key, $default)`</td><td>`get(string $key, $default = null)`</td><td>Gibt den Wert oder den Default zurück.</td></tr><tr class="intellij-row-even"><td>-</td><td>`mustGet(string $key)`</td><td>Wirft eine `MissingConfigException`, wenn der Key fehlt.</td></tr><tr><td>`getElasticsearchIndex()`</td><td>`get('elasticSearch.index')`</td><td>Der Index wird nun direkt über den Key abgefragt.</td></tr><tr class="intellij-row-even"><td>`getClientName()`</td><td>`get('tenant.name')` (oder über `TenantContext`)</td><td>Zugriff auf den Mandantennamen.</td></tr></tbody></table>