MODULE ERSTELLEN FÜR OXID 6

MODULE ERSTELLEN FÜR OXID 6

OXID 6 steht in den Startlöchern. Während sich oberflächlich nicht viel ändert, wurde der Shop-Core deutlich entschlackt und modularisiert. Nicht zuletzt ist es nun möglich und erwünscht, sowohl den Shop als auch Erweiterungen / Module per Composer zu installieren. Eine noch im Aufbau befindliche Dokumentations-Plattform beschreibt die Modulinstallation mittels Composer genauer. Das wollen wir uns im folgenden einmal näher ansehen.

Module portieren

OXID hat sich viel Mühe gemacht, damit Shop-Updates und Modulübernahmen trotz großem Refactoring, der Einführung von PHP Namespaces usw. nicht allzu schwer fallen, u.a. gibt es einen eigenen Layer, der alte Klassennamen auf die neuen Namespace-Klassen mappt. So sollten die meisten bestehenden Module lt. OXID ohne jegliche Anpassungen zumindest zum größten Teil lauffähig sein. Nichtsdestotrotz sollte man sich natürlich dennoch die Mühe machen, Module korrekt zu portieren, d.h. Namespaces einzuführen, DB-Zugriffe anzupassen, die „metadata.php“ zu aktualisieren, eine „composer.json“ zu erstellen usw. Eine detaillierte Anleitung findet sich auf Oxidforge. Wichtig sind auch die Änderungen bzgl. Datenbankabfragen, Resultsets usw. in diesem Blogbeitrag sowie das Changelog mit entfernten Methoden usw.

Neue Module erstellen

Wir wollen nun jedoch versuchen, ein komplett neues, über Composer installierbares Modul zu erstellen. Nachdem der Shop ebenfalls über Composer installiert wurde (Document-Root im Webserver auf das erstellte „source“-Verzeichnis setzen!), müssen wir in der OXID „composer.json“ unser Modul-Repository einfügen und das Modul als Requirement ergänzen. Falls es noch kein GIT-Repository gibt, kann Composer auch aus ZIP-Dateien installieren. Das wollen wir nutzen und fügen der „composer.json“ von OXID selbst ein „ZIP-Repository“ hinzu:

"repositories": [{
"type": "package",
"package": {
"name": "myvendor/mymodule",
"type": "oxideshop-module",
"version": "0.1.5",
"dist": {
"url": "http://localhost/mymodule-0.1.5.zip",
"type": "zip"
}
},
"autoload": {
"psr-4": {
"MyVendor\": ""
}
}
}],

Wichtig ist hier, wenn man ein ZIP verwenden will, auch an dieser Stelle den Autoloader für eigene Namespaces zu konfigurieren, ansonsten können die Source-Dateien später nicht automatisch von OXID geladen werden. Für ein „richtiges“ GIT-Repository sähe dies z.B. so aus:

„repositories“: {   „myvendor/mymodule“: { „type“: „vcs“, „url“: „[email protected]:myvendor/mymodule.git“} },

Statt die „composer.json“-Datei zu editieren, kann man zusätzliche Abhängigkeiten übrigens auch über die Konsole hinzufügen, z.B.

composer config repositories.smx/mymodule vcs https://github.com/shoptimax/mymodule composer require smx/mymodule:dev-master

Spätestens beim nächsten „php composer.phar update“ sollte das zusätzliche Modul automatisch mit installiert werden.

Das Modul selbst

Im Modul-Repo selbst sollte man direkt die Modul-Dateien im Hauptverzeichnis ablegen, alternativ kann man auch den Parameter „source-directory“ angeben und einen Unterordner verwenden, z.B. „src“:

"extra": {
"oxideshop": {
"blacklist-filter": [
"documentation/**/*.*"
],
"target-directory": "shoptimax/shoptifind",
"source-directory": "src"
}
},

Die Verzeichnisstruktur unter „modules“ wird dann beim Composer Install automatisch angelegt. Ein gutes Referenz-Modul ist das Paypal-Modul von OXID.

Zentral ist hier natürlich die „composer.json“ für das Modul selbst, hier muss man v.a. zwingend den „type“ auf „oxideshop-module“ setzen, damit der korrekte Composer Installer aktiviert wird, ausserdem muss auch hier der Autoloader konfiguriert sowie mit „target-directory“ unter „extras“ das Installationsverzeichnis (relativ zum „modules“-Verzeichnis im Shop) des Moduls angegeben werden:

{
"name": "myvendor/mymodule",
"description": "This is a module for the OXID eShop.",
"type": "oxideshop-module",
"keywords": ["oxid", "modules", "eShop"],
"homepage": "https://www.myvendor.de",
"license": "GPL-3.0", "proprietary" ],
"extra": {
"oxideshop": {
"blacklist-filter": ["documentation/**/*.*"],
"target-directory": "myvendor/mymodule"
}
},
"require": {
"php": ">=5.6"
},
"autoload": {
"psr-4": {
"MyVendor\": "../../../source/modules/myvendor/mymodule"
}
}
}

In den eigenen Modul-Klassen kann man nun ebenfalls Namespaces nutzen, z.B. hier ein neuer Frontend-Controller:

namespace MyVendor\MyModule\Controller;

use \OxidEsales\Eshop\Application\Controller\FrontendController as FrontendController;

/**

* A frontend controller...

*/

class MyDemoController extends FrontendController

{

...

Auch die Metadata hat einige Updates erfahren, Details hierzu finden sich in der Dokumentation, auch hier ist das OXID Paypal Modul wieder eine gute Referenz! Hier ein Beispiel für eine erweiterte Klasse und einen neuen, eigenen Controller:

'extend'=>array(

\OxidEsales\Eshop\Application\Controller\ArticleDetailsController::class => MyVendor\MyModule\Controller\MyArticleDetailsController::class,

),

'controllers'=>array(

'mydemocontroller'=> MyVendor\MyModule\Controller\MyDemoController::class,

),

Modul-Code anpassen

Neben Modulstruktur, Namespaces und angepasster metadata.php muss man bei der Portierung alter Module natürlich auch den Modul-Code selbst „anfassen“. Hier eine kurze und unvollständige „Search and Replace“-Übersicht (alt => neu), wie sich Klassen-Namen und -Pfade konkret geändert haben – eine hilfreiche Übersicht der „Mappings“ von alten auf neue Klassennamen findet sich in der Datei „vendor/oxid-esales/oxideshop-ce/source/Core/Autoload/BackwardsCompatibilityClassMap.php:
 
Datenbank-Handler Instanz holen:
 
oxDb::getDb => \OxidEsales\Eshop\Core\DatabaseProvider::getDb
 
 
Mit Datenbank-Handler / Resultsets arbeiten:
 
$rs->moveNext() => $rs->fetchRow()
$rs->recordCount() => $rs->count()
$oDb->getArray => $oDb->select($sQ)->fetchAll()
$oDb->execute( => $oDb->select( [für lesende Abfragen!]
$oDb->escapeString( => ??? [hier gibt es leider scheinbar keinen Ersatz]
 
oxRegistry => \OxidEsales\Eshop\Core\Registry
oxI18n => \OxidEsales\Eshop\Core\Model\MultiLanguageModel
oxStr => \OxidEsales\Eshop\Core\Str
oxField => \OxidEsales\Eshop\Core\Field
oxLang = \OxidEsales\Eshop\Core\Language
 
 
„oxNew“ kann weiterhin genutzt werden, am besten aber nun auch mit kompletten Namespaces, z.B.:
 
oxNew(„oxCategory“) => oxNew(\OxidEsales\Eshop\Application\Model\Category::class)
oxNew(„oxBase“) => oxNew(\OxidEsales\Eshop\Core\Model\BaseModel::class)

Debugging

Während der Portierung trifft man nicht selten auf Exceptions, leider ist hier OXID aktuell recht strikt und der ExceptionHandler geht davon aus, dass alle ankommenden Fehler das Exception-Interface implementieren… tun sie das nicht (z.B. bei einem ParseError), dann erhält man nur relativ nichtssagende Ausgaben wie z.B.
 
    [06 Nov 12:32:29.218467 2017] [uncaught error] [type E_ERROR] [file /var/www/html/vendor/oxid-esales/oxideshop-ce/source/Core/Exception/ExceptionHandler.php] [line 130]
    [message Uncaught TypeError: Argument 1 passed to OxidEsales\EshopCommunity\Core\Exception\ExceptionHandler::handleUncaughtException() must be an instance of         
    Exception, instance of ParseError given in /var/www/html/vendor/oxid-esales/oxideshop-ce/source/Core/Exception/ExceptionHandler.php:130
 
    Stack trace:     #0 [internal function]: OxidEsales\EshopCommunity\Core\Exception\ExceptionHandler->handleUncaughtException(Object(ParseError))     #1 {main}
    ...
 
Hier hat man meist keine Chance, den eigentlichen Fehler zu finden. Ein Quick-and-Dirty Hilfsmittel ist, die Stelle im ExceptionHandler vorübergehend zu modifizieren, um detaillierte Hinweise zu bekommen, das geht z.B. mit dieser kleinen Anpassung in "vendor/oxid-esales/oxideshop-ce/source/Core/Exception/ExceptionHandler.php":
 
//public function handleUncaughtException(\Exception $exception)
public function handleUncaughtException($exception)
 
 
Sauberer wäre es, einen eigenen Error-Handler zu registrieren, indem man diesen in "modules/functions.php" definiert, z.B.
 
set_exception_handler(
[
new \MyVendor\MyModule\Core\Exception\ExceptionHandler(),
'handleUncaughtException'
]
);

Aktueller Stand OXID 6

Der finale Release wird in KW45 zum OXID Partnertag erwartet, der aktuelle Release Candidate 2 wirkt bereits recht robust. Ein Problem ist uns allerdings aufgefallen beim Portieren eines größeren Moduls, welches auch viele Backend-Erweiterungen enthält (Shoptifind): es gibt leider noch einen Bug, welcher verhindert, dass ein Modul eigene "Ajax-Popups" im OXID Backend nutzen kann, siehe OXID Bugtracker. Es bleibt zu wünschen, dass dieser noch bis zum finalen Release behoben wird. UPDATE 14.11.17: der Bug wurde behoben!
Keine Kommentare