
Das Schreiben Ihrer Pakete für MODX ist für Anfänger nicht einfach, und ein erfahrener Entwickler hat manchmal eine süße Zeit. Aber der Anfänger hat Angst und der Erfahrene versteht :).
In diesem Beitrag wird erläutert, wie Sie ein Komponentenpaket für MODX schreiben und erstellen können, ohne MODX selbst zu installieren und zu konfigurieren. Das Niveau ist überdurchschnittlich, so dass Sie in einigen Fällen Ihr Gehirn zerbrechen müssen, aber es lohnt sich.
Ich frage nach Details unter Katze.
Als MODX Revolution gerade erschien, war es in der frühen Beta-Version, und die Entwickler wussten immer noch nicht, wie sie damit arbeiten und wie sie Plugins dafür schreiben sollten. Nun, bis auf das Team, das über das CMS nachgedacht hat. Und ich muss sagen, das Team hat es teilweise geschafft und dafür gesorgt, dass das System selbst bequem Pakete sammelt, die dann über das Repository installiert werden können, was logisch erscheint. Aber seitdem sind viele Jahre vergangen und die Anforderungen an Pakete und deren Montage haben sich ein wenig geändert.
Copy-Paste ist böse, wenn auch nicht immer
In den letzten Monaten wurde ich von dem Gedanken verfolgt, warum Sie zum Erstellen eines Pakets für MODX es installieren, eine Datenbank erstellen, einen Administrator erstellen usw. müssen. So viele zusätzliche Aktionen. Nein, daran ist nichts auszusetzen, wenn Sie es einmal einrichten und dann verwenden. Viele tun dies. Aber was ist, wenn Sie die Versammlung dem Drehbuch anvertrauen und selbst einen Kaffee trinken möchten?
Es kam vor, dass die Entwickler von MODX daran gewöhnt waren, mit MODX selbst zu arbeiten, und dem Paket Klassen direkt zum Kernel hinzufügten. Sie schrieben auch die ersten Komponenten, die ersten Build-Skripte, die dann von anderen Entwicklern als Beispiele verwendet wurden, die die Lösung einfach kopierten und sich nicht immer mit der Essenz des Geschehens befassten. Und ich habe es geschafft.
Die Aufgabe besteht jedoch darin, die Zusammenstellung des Pakets, vorzugsweise auf dem Server, immer mit einem Minimum an erforderlicher Software, mit minimalen Ressourcen und daher mit höherer Geschwindigkeit zu automatisieren. Die Aufgabe wurde festgelegt und nach dem Studium der Quelle fand Jason, der im Chat bremste, eine Lösung.
Und welches?
Das erste, was ich herausgefunden habe, ist, dass der Code, der für die Erstellung des Pakets verantwortlich ist, direkt in der xPDO-Bibliothek liegt. In MODX gibt es nur Wrapper-Klassen, die eine bequemere API bieten und mit denen etwas einfacher zu arbeiten ist, aber nur, wenn MODX installiert ist. Daher kann wahrscheinlich nur xPDO irgendwie verwendet werden, aber im Code muss der Konstruktor des xPDO-Objekts Daten für die Datenbankverbindung angeben.
public function __construct( $dsn, $username = '', $password = '', $options = [], $driverOptions= null );
Nach der Befragung von Jason wurde klar, dass, obwohl Parameter festgelegt werden müssen, die tatsächliche physische Verbindung zur Datenbank genau zu dem Zeitpunkt erfolgt, zu dem dies erforderlich ist. Faule Ladung in all ihrer Pracht. Das zweite Problem wurde behoben.
Das dritte Problem war das Problem der Verbindung von xPDO mit dem Projekt. Composer kam sofort in den Sinn, aber die 2.x-Version, auf der der aktuelle MODX ausgeführt wird, unterstützt Composer nicht, und der 3.x-Zweig verwendet Namespaces, und Klassennamen werden anders geschrieben als in 2.x, was zu Konflikten und Fehlern führt. Im Allgemeinen nicht kompatibel. Dann musste ich Git-Tools verwenden und xPDO als Submodul verbinden.
Verwendung von Submodulen
Lesen Sie zuerst die Dokumentation dazu.
Wenn dies ein neues Projekt ist, müssen Sie ein Submodul hinzufügen:
$ git submodule add https://github.com/username/reponame
Dieser Befehl klont und installiert ein Submodul in Ihrem Projekt. Anschließend müssen Sie den Submodulordner mit dem Befehl git add zu Ihrem Repository hinzufügen. Es wird nicht der gesamte Ordner mit dem Submodul hinzugefügt, sondern nur ein Link zum letzten Commit des Submoduls zu git hinzugefügt.
Damit ein anderer Entwickler das Projekt mit allen Abhängigkeiten klonen kann, müssen Sie eine .gitmodules-Konfiguration für Submodule erstellen. Im Slackify-Projekt sieht es so aus:
[submodule "_build/xpdo"] path = _build/xpdo url = https://github.com/modxcms/xpdo.git branch = 2.x
Geben Sie danach beim Klonen einfach das rekursive Flag an, und git lädt alle abhängigen Repositorys herunter.
Als Ergebnis haben wir xPDO, xPDO kann verwendet werden, ohne eine Verbindung zur Datenbank herzustellen. Wenn dies nicht erforderlich ist, kann xPDO als externe Abhängigkeit (Git-Submodul) mit dem Komponentencode verbunden werden. Nun die Implementierung des Build-Skripts.
Lass uns verstehen
Ich werde das
Build-Skript des Slackify- Add-
Ons beschreiben, das kürzlich von mir veröffentlicht wurde. Diese Komponente ist kostenlos und auf GitHub öffentlich verfügbar, was das Selbststudium erleichtert.
Verbinden Sie xPDO
Wir lassen die Aufgabe von Konstanten mit dem Paketnamen und anderen notwendigen Aufrufen weg und verbinden xPDO.
require_once 'xpdo/xpdo/xpdo.class.php'; require_once 'xpdo/xpdo/transport/xpdotransport.class.php'; $xpdo = xPDO::getInstance('db', [ xPDO::OPT_CACHE_PATH => __DIR__ . '/../cache/', xPDO::OPT_HYDRATE_FIELDS => true, xPDO::OPT_HYDRATE_RELATED_OBJECTS => true, xPDO::OPT_HYDRATE_ADHOC_FIELDS => true, xPDO::OPT_CONNECTIONS => [ [ 'dsn' => 'mysql:host=localhost;dbname=xpdotest;charset=utf8', 'username' => 'test', 'password' => 'test', 'options' => [xPDO::OPT_CONN_MUTABLE => true], 'driverOptions' => [], ] ] ]);
Ich habe das xPDO-Submodul zum _build-Ordner hinzugefügt, das wir nur in der Phase der Entwicklung und Montage des Pakets benötigen und das nicht in das Hauptarchiv der Komponente gelangt. Die zweite Kopie von xPDO auf der Site mit Live-MODX benötigen wir nicht.
In den xPDO-Verbindungseinstellungen habe ich den Datenbanknamen in
dsn
, aber er spielt keine Rolle. Es ist wichtig, dass der Cache-Ordner in xPDO beschreibbar ist. Das war's, xPDO wird initialisiert.
Machen Sie einen kniffligen Hack mit Klassen
Wenn Sie beim Erstellen des Pakets den installierten MODX verwenden, ist alles einfach. Wir nehmen und erstellen ein Objekt der Klasse, die wir benötigen. MODX findet tatsächlich die erforderliche Klasse, findet die erforderliche Implementierung für diese Klasse (die Klasse mit dem Postfix _mysql), die von der Datenbank abhängt, und erstellt dann das gewünschte Objekt (aufgrund dieser Funktion können beim Erstellen des Pakets Fehler auftreten, die die Klasse * _mysql nicht gefunden, das ist nicht beängstigend). Wir haben jedoch weder eine Basis noch eine Implementierung. Wir müssen irgendwie die gewünschte Klasse ersetzen, was wir tun.
class modNamespace extends xPDOObject {} class modSystemSetting extends xPDOObject {}
Wir erstellen eine Dummy-Klasse (Stub), die zum Erstellen des gewünschten Objekts benötigt wird. Dies wäre nicht erforderlich, wenn xPDO nicht speziell prüfen würde, zu welcher Klasse das Objekt gehört. Aber er prüft.
Es gibt jedoch Sonderfälle, in denen Sie etwas mehr tun müssen, als nur eine Klasse zu definieren. Dies sind Fälle von Abhängigkeiten zwischen Klassen. Zum Beispiel müssen wir der Kategorie ein Plugin hinzufügen. Im Code nur
$category->addOne($plugin);
aber in unserem Fall wird dies nicht funktionieren.
Wenn Sie sich jemals
das MODX-Datenbankschema angesehen haben , haben Sie wahrscheinlich Elemente wie Aggregat und Composite gesehen. Es wird in der
Dokumentation darüber geschrieben , aber wenn auf einfache Weise, beschreiben sie die Beziehung zwischen Klassen.
In unserem Fall kann eine Kategorie mehrere Plugins enthalten, für die das Aggregatelement für die Klasse
modCategory
verantwortlich ist. Da wir eine Klasse ohne konkrete Implementierung haben, müssen wir diesen Zusammenhang daher von Hand angeben. Dies ist einfacher, wenn Sie die Methode
getFKDefinition
überschreiben:
class modCategory extends xPDOObject { public function getFKDefinition($alias) { $aggregates = [ 'Plugins' => [ 'class' => 'modPlugin', 'local' => 'id', 'foreign' => 'category', 'cardinality' => 'many', 'owner' => 'local', ] ]; return isset($aggregates[$alias]) ? $aggregates[$alias] : []; } }
In unserer Komponente werden nur Plugins verwendet, daher fügen wir nur für sie Links hinzu. Danach kann die addMany-Methode der modCategory-Klasse die erforderlichen Plugins einfach zur Kategorie und dann zum Paket hinzufügen.
Erstellen Sie ein Paket
$package = new xPDOTransport($xpdo, $signature, $directory);
Wie Sie sehen können, ist alles sehr, sehr einfach. Hier mussten wir den Parameter
$xpdo
, den wir ganz am Anfang initialisiert haben. Ohne diesen Moment gäbe es kein Problem 2.
$signature
- der Name des Pakets, einschließlich der Version,
$directory
- der Ort, an dem das Paket sorgfältig platziert wird. Woher diese Variablen kommen, sehen Sie selbst in der Quelle.
Erstellen Sie einen Namespace und fügen Sie ihn dem Paket hinzu
Wir benötigen einen Namespace, um Lexika und Systemeinstellungen daran zu binden. In unserem Fall werden andere nur aus diesem Grund noch nicht berücksichtigt.
$namespace = new modNamespace($xpdo); $namespace->fromArray([ 'id' => PKG_NAME_LOWER, 'name' => PKG_NAME_LOWER, 'path' => '{core_path}components/' . PKG_NAME_LOWER . '/', ]); $package->put($namespace, [ xPDOTransport::UNIQUE_KEY => 'name', xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => true, xPDOTransport::RESOLVE_FILES => true, xPDOTransport::RESOLVE_PHP => true, xPDOTransport::NATIVE_KEY => PKG_NAME_LOWER, 'namespace' => PKG_NAME_LOWER, 'package' => 'modx', 'resolve' => null, 'validate' => null ]);
Der erste Teil ist jedem klar, der jemals Code für MODX geschrieben hat. Die zweite mit der Ergänzung des Pakets ist etwas komplizierter. Die
put
Methode verwendet zwei Parameter: das Objekt selbst und ein Array von Parametern, die dieses Objekt und sein mögliches Verhalten zum Zeitpunkt der Installation des Pakets beschreiben. Beispielsweise bedeutet
xPDOTransport::UNIQUE_KEY => 'name'
, dass das
xPDOTransport::UNIQUE_KEY => 'name'
mit dem Namen des Namespace selbst als Wert für den Namespace als eindeutiger Schlüssel in der Datenbank verwendet wird. Sie können mehr über die Parameter
in der xPDO-Dokumentation lesen und besser den Quellcode studieren.
Auf die gleiche Weise können Sie andere Objekte hinzufügen, z. B. Systemeinstellungen.
$package->put($setting, [ xPDOTransport::UNIQUE_KEY => 'key', xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => true, 'class' => 'modSystemSetting', 'resolve' => null, 'validate' => null, 'package' => 'modx', ]);
Erstellen Sie eine Kategorie
Mit der Hinzufügung einer Kategorie hatte ich den größten Knebel, als ich alles herausfand. Elemente, die im xPDO-Modell in eine Kategorie eingefügt werden, müssen beide zu dieser Kategorie gehören, d. H. darin verschachtelt sein, und erst dann muss die Kategorie selbst im Paket verschachtelt sein. Gleichzeitig müssen Sie die Beziehungen zwischen den Klassen berücksichtigen, die ich oben bereits beschrieben habe. Es hat ziemlich lange gedauert, es zu verstehen, zu realisieren und richtig anzuwenden.
$package->put($category, [ xPDOTransport::UNIQUE_KEY => 'category', xPDOTransport::PRESERVE_KEYS => false, xPDOTransport::UPDATE_OBJECT => true, xPDOTransport::ABORT_INSTALL_ON_VEHICLE_FAIL => true, xPDOTransport::RELATED_OBJECTS => true, xPDOTransport::RELATED_OBJECT_ATTRIBUTES => [ 'Plugins' => [ xPDOTransport::UNIQUE_KEY => 'name', xPDOTransport::PRESERVE_KEYS => false, xPDOTransport::UPDATE_OBJECT => false, xPDOTransport::RELATED_OBJECTS => true ], 'PluginEvents' => [ xPDOTransport::UNIQUE_KEY => ['pluginid', 'event'], xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => false, xPDOTransport::RELATED_OBJECTS => true ] ], xPDOTransport::NATIVE_KEY => true, 'package' => 'modx', 'validate' => $validators, 'resolve' => $resolvers ]);
Es sieht monströs aus, aber nicht so gesehen. Ein wichtiger Parameter ist
xPDOTransport::RELATED_OBJECTS => true
gibt an, dass die Kategorie verschachtelte Elemente enthält, die ebenfalls
xPDOTransport::RELATED_OBJECTS => true
und dann installiert werden müssen.
Da die meisten Module verschiedene Elemente enthalten (Chunks, Snippets, Plugins), ist die Kategorie mit Elementen das wichtigste Element des Transportpakets. Daher werden hier Validatoren und Resolver angegeben, die während der Installation des Pakets ausgeführt werden.
Validatoren werden vor der Installation durchgeführt, Resolver - danach.
Ich hätte fast vergessen, bevor wir die Kategorie packen, müssen wir unsere Elemente hinzufügen. So:
$plugins = include $sources['data'] . 'transport.plugins.php'; if (is_array($plugins)) { $category->addMany($plugins, 'Plugins'); }
Fügen Sie dem Paket weitere Daten hinzu.
Im Paket müssen Sie eine weitere Datei mit einer Lizenz, eine Datei mit einem Änderungsprotokoll und eine Datei mit einer Beschreibung der Komponente hinzufügen. Bei Bedarf können Sie über das Attribut
setup-options
weiteres spezielles Skript hinzufügen, das das Fenster vor der Installation des Pakets anzeigt. Dies ist, wenn anstelle von "Installieren" die Schaltfläche "Installationsoptionen". Ab der Version von MODX 2.4 konnten Abhängigkeiten zwischen Paketen mithilfe des Attributs require angegeben werden. Darin können Sie auch die Version von PHP und MODX angeben.
$package->setAttribute('changelog', file_get_contents($sources['docs'] . 'changelog.txt')); $package->setAttribute('license', file_get_contents($sources['docs'] . 'license.txt')); $package->setAttribute('readme', file_get_contents($sources['docs'] . 'readme.txt')); $package->setAttribute('requires', ['php' => '>=5.4']); $package->setAttribute('setup-options', ['source' => $sources['build'] . 'setup.options.php']);
Wir packen
if ($package->pack()) { $xpdo->log(xPDO::LOG_LEVEL_INFO, "Package built"); }
Das war's, holen Sie das fertige Paket in
_packages
, oder von dort, wo Sie die Assembly konfiguriert haben.
Was ist das Ergebnis?
Das Ergebnis hat meine Erwartungen übertroffen, da dieser Ansatz zwar einige Einschränkungen mit sich bringt und an einigen Stellen einige Unannehmlichkeiten mit sich bringt, aber hinsichtlich der Anwendungsmöglichkeiten gewinnt.
Um das Paket zu erstellen, führen Sie einfach zwei Befehle aus:
git clone --recursive git@github.com:Alroniks/modx-slackify.git cd modx-slackify/_build && php build.transport.php
Das erste ist das Klonen des Repositorys und seiner Submodule. Ein wichtiger Parameter ist
--recursive
, dank dessen git zusätzlich zum Komponentencode selbst alle als Submodule beschriebenen Abhängigkeiten herunterlädt und installiert.
Der zweite ist das direkte Erstellen des Pakets. Danach können Sie das fertige
package-1.0.0-pl.transport.zip
aus dem Ordner
package-1.0.0-pl.transport.zip
_packages
und beispielsweise in das Repository laden.
Die Aussichten sind groß. Sie können beispielsweise einen Hook in GitHub konfigurieren, der nach dem Festschreiben in einem Zweig ein Skript auf Ihrem Server ausführt, das das Paket sammelt und auf allen vorhandenen Websites ablegt. Oder laden Sie die neue Version in ein Repository hoch, und zu diesem Zeitpunkt kochen Sie sich Kaffee, wie ich zu Beginn sagte. Oder Sie können Tests für das Modul erstellen und schreiben und den Testlauf ausführen und über Jenkins oder Travis erstellen. Ja, eine Reihe von Szenarien, die Sie sich vorstellen können. Mit diesem Ansatz ist dies jetzt viel einfacher.
Fragen stellen, versuchen zu beantworten.
PS Gehen Sie nicht vorbei,
setzen Sie bitte
einen Slackify-Stern auf GitHub .