Frage zu Chains und Hooks

Alles rund um Module und Plugins in CONTENIDO 4.9.
homtata
Beiträge: 1094
Registriert: Mi 14. Jan 2004, 14:41
Kontaktdaten:

Frage zu Chains und Hooks

Beitrag von homtata » Do 22. Okt 2020, 12:31

Hallo zusammen,
ich habe mal wieder ein grundsätzliches Verständnisproblem mit Hooks und Chains.

Ich möchte eigene Chains hinzufügen bzw. Aktionen definieren. Formal weiß ich, wie das geht, aber WOHER kommen diese Aufrufe im Stil von "Contenido.Upload.UploadPostprocess"? Woher weiß ich, wie das Konstrukt heißt z.B. das nach dem Erstellen eines Artikels ausgeführt werden soll? Wie bauen sich diese durch Punkte getrennte Ketten auf, gibts dafür eine Liste, und wenn ja, wo steht die? Ich finde nix. Alle schreiben immer nur wie selbstverständlich, dass es die gibt, aber in der /data/../config.chains.load.php stehen weniger drin als es offensichtlich gibt.
Bin ratlos.
LG
Viktor

xmurrix
Beiträge: 2930
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von xmurrix » Do 22. Okt 2020, 15:40

Hallo homtata,

Format der Chains:
Das Format der Chain-Bezeichnung baut sich wie folgt auf:

Code: Alles auswählen

"{namensraum}.{bereich}.{aktion}"
Alle Chains, die im CONTENIDO verwendet werden, fangen deshalb mit "Contenido." an.

Willst du eigene Chains hinzufügen, dann solltest du einen eigenen Namensraum dafür verwenden, damit es nicht vorhandene Chains überschreibt.

Code: Alles auswählen

z. B. "MeinPlugin.{bereich}.{aktion}"

Klassen cApiCecHook und cApiCecRegistry
Die Klasse cApiCecHook dient nur dazu um Chain-Funktionen auszuführen.
Mit der Klasse cApiCecRegistry kann man Chain-Funktionen zu Chains registrieren, aber auch Chain-Funktionen ausführen.


Ausführen von Chain-Funktionen:
Chain-Funktionen werden mit Hilfe der Funktionen in der Klasse cApiCecHook ausgeführt, und zwar dort, wo man dies braucht. Siehe folgende Aufrufe im CONTENIDO Sourcecode:

Code: Alles auswählen

cApiCecHook::execute(...)
cApiCecHook::executeAndReturn(...)
cApiCecHook::executeWhileBreakCondition(...)
Es gibt auch folgende Variante zum Ausführen der Chain-Funktionen mittels der Klasse cApiCecRegistry, allerdings werden eher die oberen Varianten verwendet, weil der Code kürzer ist:

Code: Alles auswählen

$_cecIterator = cRegistry::getCecRegistry()->getIterator($chainName);
if ($_cecIterator->count() > 0) {
    while (false !== $chainEntry = $_cecIterator->next()) {
        $businessName = $chainEntry->execute();
    }
}
Eine Zusammenfassung der existierenden Chains in CONTENIDO findest du unter:
https://docs.contenido.org/display/CONDEVE/Chain


Datei "config.chains.load.php":
In dieser Datei werden nicht Chains, sondern auszuführende Funktionen zu Chains registriert, z. B.:

Code: Alles auswählen

$_cecRegistry = cApiCecRegistry::getInstance();
$_cecRegistry->addChainFunction('Contenido.Frontend.CategoryAccess', 'cecFrontendCategoryAccess');
Der Chain 'Contenido.Frontend.CategoryAccess' wird die Funktion 'cecFrontendCategoryAccess' registriert. Die Funktion ist in der Datei 'contenido/includes/chains/include.chain.frontend.cat_access.php' definiert. Bevor man eine Funktion der Chain registriert, muss man sicherstellen, dass auch die Funktion verfügbar ist, die entsprechende PHP-Datei ist vorher per include/require einzubinden.

Die Chain 'Contenido.Frontend.CategoryAccess' wird z. B. in 'contenido/includes/frontend/include.front_content.php' ausgeführt. Dabei wird die Funktion 'cecFrontendCategoryAccess' ausgeführt, die in 'config.chains.load.php' registriert wurde.

Code: Alles auswählen

cApiCecHook::setBreakCondition(true, false);
$allow = cApiCecHook::executeWhileBreakCondition('Contenido.Frontend.CategoryAccess', $lang, $idcat, $auth->auth['uid']);
In der ersten Zeile wird die Abbruchbedingung der auszuführenden Chain-Funktionen (einer Chain kann man Chain-Funktionen registrieren) definiert. In diesem Fall ist es so, dass die Ausführung der Chain-Funktionen abgebrochen wird, sobald eine Chain-Funktion den Wert true zurückliefert.
Genauer geht es hier die Prüfung des Zugriffs auf die Kategorie. Handelt es sich um eine geschützte Kategorie, werden die registrierten Chain-Funktionen ausgeführt, die Prüfen sollen, ob der aktuelle Benutzer Zugriff darauf hat. Sobald eine Chain-Funktion den Wert true zurückliefert, wird die Ausführung weiterer Chain-Funktionen beendet.

Gruß
xmurrix
CONTENIDO downloads: CONTENIDO 4.10.1
CONTENIDO links: Documentation, API documentation
CONTENIDO @ Github: CONTENIDO 4.10

homtata
Beiträge: 1094
Registriert: Mi 14. Jan 2004, 14:41
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von homtata » Do 22. Okt 2020, 17:54

Danke Murat, aber ich versteh es trotzdem nicht. Ok, ich definiere Namensräume, aber woher WEISS denn der Namensraum, wann er reagieren muss? Wo ist denn definiert, dass "Contenido.Content.AfterStore" heißt, dass nach dem Speichern des Content also die besagte Funktion/Klasse auszuführen ist? Wo ist der Trigger? Ich steh irgendwie auf dem Schlauch. Ich will ja z.B. mit etwas wie "Contenido.Article.AfterCreation" irgendwas machen, aber ... ich kann ja doch nicht einfach irgendwelche Triggernamen erfinden, wenn das System gar nicht weiß, was ich meine? Irgendwoher müssen diese Namespaces und Aktionsnamen doch kommen, aber woher?

xmurrix
Beiträge: 2930
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von xmurrix » Do 22. Okt 2020, 20:56

Hallo Viktor,

der Namensraum ist nur eine Konvention und es sorgt nicht dafür, dass automatisch irgendwo eine Funktion ausgeführt wird. Nehmen wir z. B. "Contenido.Content.AfterStore". Die wird in der "contenido/includes/functions.con.php" am Ende der Funktion conMakeArticleIndex() ausgeführt, siehe folgende Zeilen am Ende der Funktion:

Code: Alles auswählen

    $iterator = cRegistry::getCecRegistry()->getIterator('Contenido.Content.AfterStore');
    while (false !== $chainEntry = $iterator->next()) {
        $chainEntry->execute($articleIds);
    }
Man muss im Code also den Aufruf der Chain Funktionen explizit angeben.

Willst du eine neue Chain hinzufügen, wie z. B. eine "Contenido.Article.AfterCreation", so müsstest du z. B. in der "contenido/includes/functions.con.php" an Ende der Funktion conEditFirstTime() die Chain-Funktionen dazu ausführen, also am Ende der Funktion folgende Zeile hinzufügen:

Code: Alles auswählen

    cApiCecHook::execute('Contenido.Article.AfterCreation', [
        'idart' => $idart
    )];

    return $idart;
Aber das würde bedeuten, dass du den Code von CONTENIDO änderst, was keine gute Idee ist, da es beim nächsten Update überschrieben wird.


Was aber auch geht, ist die Verwendung von Callback-Funktionen in Item-Klassen (Datenbankabstraktionsebene, also Generic DB) für bestimmte Events, die beim Erstellen, Aktualisieren oder Löschen von Datensätzen ausgeführt werden können. Das geht mit allen cApi* Klassen, die von der Item-Klasse ableiten, also mit cApiArticle, cApiArticleLanguage, cApiCategory, cApiCategoryLanguage, cApiLanguage, cApiLayout, usw...

Willst du z. B. eine Funktion ausführen, wenn ein Artikel neu erzeugt wurde, so kannst du das wie folgt machen:

Code: Alles auswählen

function myNewArticleSucessCallback($idart) {
    // Benutzerdefinierter Code, der nach dem erfolgreichen Erzeugen eines Artikels ausgeführt wird
}

cGenericDb::register(cGenericDb::CREATE_SUCCESS, 'myNewArticleSucessCallback', 'cApiArticle');
Immer, wenn ein neuer Artikel erstellt wird, wird auch die registrierte Callback-Funktion ausgeführt. Dieses Feature gibt es schon ziemlich lange (seit CONTENIDO 4.9), es ist aber nicht bekannt. Zu beachten ist, dass das nur dann funktioniert, wenn auch die Datensätze mit den cApi* Klassen verwaltet werden. Wird z. B. irgendwo eine SQL-Anweisung erzeugt, mit der ein Artikel-Datensatz in der Datenbank angelegt wird, also ohne die Verwendung der cApiArticle zum Erstellen eines neuen Artikels, so kann auch keine der Callback-Funktionen ausgeführt werden.

Siehe auch Doku zu Generic DB Callbacks:
https://docs.contenido.org/display/COND ... +callbacks

Mit den Generic DB Callbacks kann man so ziemlich auf alle Änderungen in der Datenbank reagieren, sofern diese Änderungen durch die Verwendung der cApi* Klassen stattfinden.

Gruß
Murat
CONTENIDO downloads: CONTENIDO 4.10.1
CONTENIDO links: Documentation, API documentation
CONTENIDO @ Github: CONTENIDO 4.10

Faar
Beiträge: 1657
Registriert: Sa 8. Sep 2007, 16:23
Wohnort: Brandenburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von Faar » Fr 23. Okt 2020, 09:38

homtata hat geschrieben:
Do 22. Okt 2020, 17:54
Danke Murat,
Moin Homtata, ich probiere es auch mal zu erklären.
Ich danke Murat auch, die Chains sind sehr wirksam und praktisch, aber oft vernachlässigt bei der eigenen Entwicklung.
Was nicht zuletzt an einem kleinen Problem liegt, nämlich der Einbindung und überschreiben bei Updates.
aber ich versteh es trotzdem nicht.

Wordpress hat ähnliches, nennt sich auch Hooks.
Ok, ich definiere Namensräume, aber woher WEISS denn der Namensraum, wann er reagieren muss?
Falscher Ansatz. Du könntest es auch BluemchenwieseWeissBlau nennen, aber dann wüsste später keiner, wo das Ding eingesetzt wird.
Darum Namensräume, dass man eine Logik hat, die Hinweise gibt.
Wo ist der Trigger?
Der Trigger (Hook) ist im Programmcode von Contenido, und zwar immer dort, wo man dachte, dass man da einen brauchen könnte. In deinem Beispiel dann, wenn Inhalte gespeichert wurden. Da hat sich jemand gedacht, dass man nach erfolgreicher SQL zum speichern noch einen Hook einbaut, der es ermöglicht, hier einen Code quasi zwischen rein zu schieben.
Jemand hat einen Artikel gespeichert und deine Chain schreibt dann "Vielen dank fürs Speichern". Für sowas banales braucht es eben diesen Hook an dieser Stelle im Contenido-Code.
Ich steh irgendwie auf dem Schlauch.
:?
ich kann ja doch nicht einfach irgendwelche Triggernamen erfinden, wenn das System gar nicht weiß, was ich meine?
Genau so ist es.
Du kannst nur die Trigger benutzen, die Murat und andere in den Quellcode von Contenido eingebaut haben.
Lös dich vom Namensraum, man könnte auch eine Liste machen die heißt Trigger1, Trigger2, Trigger3 und in irgendeiner Doku-Tabelle stünde dann, wo diese Trigger eingebaut sind.
Praktisch könntest du auch mit einer Entwickleroberfläche alle Trigger aus dem gesammten Contenio Code suchen lassen, dann wüstest Du, wie die Programmdatei heißt und sehr genau, wo sie sitzen. Bestimmt haben alle Trigger wenigstens eine Gemeinsamkeit, nach der sich suchen lässt.

Wenn Du also was neues haben willst, was noch nicht eingebaut ist und es für viele sinnvoll wäre, dann schreib es auf oder noch besser, du machst dich daran, den selbst mittels Git-Kopie einzubauen und Murat oder sonstwer übernimmt den Code, wenn er gut ist.
Es fehlen durchaus noch solche Trigger, wie zum Beispiel in der "My Contenido" Seite im Backend.
Wir können diese Seite noch nicht als Dashboard bearbeiten wie es bei Wordpress möglich ist.
Fliegt der Bauer übers Dach, ist der Wind weißgott nicht schwach.

Faar
Beiträge: 1657
Registriert: Sa 8. Sep 2007, 16:23
Wohnort: Brandenburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von Faar » Fr 23. Okt 2020, 09:40

xmurrix hat geschrieben:
Do 22. Okt 2020, 20:56
Siehe auch Doku zu Generic DB Callbacks:
https://docs.contenido.org/display/COND ... +callbacks
Hallo Murat,
hast Du dafür gesorgt, dass die Doku endlich wieder läuft?
Falls ja, vielen Dank dafür, die Doku fehlte mir sehr. :)
Fliegt der Bauer übers Dach, ist der Wind weißgott nicht schwach.

homtata
Beiträge: 1094
Registriert: Mi 14. Jan 2004, 14:41
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von homtata » Fr 23. Okt 2020, 09:57

Hallo Murat,
danke für diese ausführlichere Erklärung - so langsam komme ich dahinter. Ich wollte es jetzt versuchen mit

Code: Alles auswählen

cGenericDb::register(cGenericDb::CREATE_SUCCESS, 'myNewArticleSucessCallback', 'cApiArticle');
und die CallBackFunktion wird auch aufgerufen, ABER ich muss dann mit den idart und idartlang-Werten für diesen neuen Artikel weiterarbeiten; die sind aber nicht gesetzt, wenn ich sie mir ausgeben lasse...

Ich ermittle sie erst Stück für Stück aus den cRegistry-Werten wie folgt:

Code: Alles auswählen

	$idclient = cRegistry::getClientId();
	$idlang = cRegistry::getLanguageId();
	$idcat = cRegistry::getCategoryId();
	$idart = cRegistry::getArticleId();
	$idcatlang = cRegistry::getCategoryLanguageId();
	$idartlang = cRegistry::getArticleLanguageId();	
packe sie in ein Array, weil ich sie dann so benötige, und kriege nur Murks; die idart ist vom Typ String und "0", die idartlang und die idcatlang gar nicht korrekt gesetzt.

Code: Alles auswählen

array(6) { ["idclient"]=> string(1) "1" ["idlang"]=> string(1) "1" ["idcat"]=> string(1) "3" ["idcatlang"]=> int(0) ["idart"]=> string(1) "0" ["idartlang"]=> int(0) } 
Ich hatte neulich schonmal das Problem in meinem Plugin, dass cRegistry::getArticleLanguageId() zwischendurch keinen Wert mehr zurückgab und bin da mit meinem Latein am Ende. Da ich nicht weiß, welche Dinge und Module und Plugins beim Anlegen eines neuen Artikels durchlaufen werden, kann ich nicht nachvollziehen, wo diese Werte möglicherweise zur Laufzeit zerschossen werden. Gebe ich sie mir in Bestandsartikeln aus, sind sie da, sowohl als cRegistry-Werte wie auch als globale $idartlang usw.

Hm.

xmurrix
Beiträge: 2930
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von xmurrix » Fr 23. Okt 2020, 09:59

Moin Frank,
Faar hat geschrieben:
Fr 23. Okt 2020, 09:40
...hast Du dafür gesorgt, dass die Doku endlich wieder läuft?...
nein, ich habe gar nichts gemacht. Vermutlich was es jemand vom 4fb-Team.
CONTENIDO downloads: CONTENIDO 4.10.1
CONTENIDO links: Documentation, API documentation
CONTENIDO @ Github: CONTENIDO 4.10

Faar
Beiträge: 1657
Registriert: Sa 8. Sep 2007, 16:23
Wohnort: Brandenburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von Faar » Fr 23. Okt 2020, 10:30

homtata hat geschrieben:
Fr 23. Okt 2020, 09:57
Ich hatte neulich schonmal das Problem in meinem Plugin, dass cRegistry::getArticleLanguageId() zwischendurch keinen Wert mehr zurückgab und bin da mit meinem Latein am Ende. Da ich nicht weiß, welche Dinge und Module und Plugins beim Anlegen eines neuen Artikels durchlaufen werden, kann ich nicht nachvollziehen, wo diese Werte möglicherweise zur Laufzeit zerschossen werden. Gebe ich sie mir in Bestandsartikeln aus, sind sie da, sowohl als cRegistry-Werte wie auch als globale $idartlang usw.
Hallo Homtata,
die getArticleLanguageID macht nichts anderes, als die Globale Variable zu holen.

Code: Alles auswählen

return self::_fetchGlobalVariable('idartlang', 0);
Wenn Du das in ein Plugin einbaust, befindest Du dich nicht in einem Artikel, wie wenn es in einem Modul wäre, das quasi im Artikel läuft.
Ohne Artikel auch keine gesetzte globale Variable eines Artikels.
Probier mal den Befehl in einem kleinen Modul und gibt die ID mit ECHO aus und da sollte dann eine $idartlang sein.
Wie Dir sicher bekannt ist, ist die $idartlang sowieso da, in einem Artikel ... :wink:
Das heißt, du musst an der Programmlogik deines Plugins etwas ändern.
Fliegt der Bauer übers Dach, ist der Wind weißgott nicht schwach.

homtata
Beiträge: 1094
Registriert: Mi 14. Jan 2004, 14:41
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von homtata » Fr 23. Okt 2020, 10:51

Hallo Murat,
es tut mir leid, wenn ich nerve; ich verstehe den Teil deiner Ausführungen, aber nicht, wie ich denn jetzt an die idartlang des gerade angelegten Artikels komme.
Das heißt, du musst an der Programmlogik deines Plugins etwas ändern.
Genau das ist mein Verständnisproblem... Ich MUSS diese Logik im Plugin ausführen. Dort steht bisher:

Code: Alles auswählen

cGenericDb::register(cGenericDb::CREATE_SUCCESS, 'FullsearchCollection::addNewSingleArticle', 'cApiArticle');
WIE kriege ich es jetzt hin, meinen Callbackaufruf wissen zu lassen, welcher Artikel (welche idartlang) denn jetzt neu angelegt wurde? Der Callback braucht doch irgendeinen Startpunkt, sonst kann ich ja für genau diesen neu erstellen Artikel gar nichts machen. An dieser Stelle ist mir das ganze wirklich noch ein Buch mit sieben Siegeln.

xmurrix
Beiträge: 2930
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von xmurrix » Fr 23. Okt 2020, 10:54

Hallo Viktor,

die cRegistry-Funktionen liefern dir die globalen Variablen, die müssen nicht mit denen übereinstimmen, die im Kontext der Callback-Funktion benötigt werden.

Die Klasse cApiArticle weiß nichts über die idartlang, die idartlang wird in der Tabelle con_art_lang gespeichert.

Was du dann brauchst, ist eine Callback-Funktion für cApiArticleLanguage.

Code: Alles auswählen

function myNewArticleLanguageSucessCallback($idartlang) {
    $artLang = new cApiArticleLanguage($idartlang);
    
    // idart des aktuellen artLang Objektes
    $idart = $artLang->get('idart');

    // idlang des aktuellen artLang Objektes
    $idlang = $artLang->get('idlang');
}

cGenericDb::register(cGenericDb::CREATE_SUCCESS, 'myNewArticleLanguageSucessCallback', 'cApiArticleLanguage');
Die globalen Variablen für idart und idartlang sind bei dir deshalb leer, weil diese Werte nicht verfügbar sind.
CONTENIDO downloads: CONTENIDO 4.10.1
CONTENIDO links: Documentation, API documentation
CONTENIDO @ Github: CONTENIDO 4.10

Faar
Beiträge: 1657
Registriert: Sa 8. Sep 2007, 16:23
Wohnort: Brandenburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von Faar » Fr 23. Okt 2020, 11:39

xmurrix hat geschrieben:
Fr 23. Okt 2020, 10:54
Was du dann brauchst, ist eine Callback-Funktion für cApiArticleLanguage.
Hallo Murat,
wäre der Hook "Contenido.Article.AfterCreation" hier nicht genau der richtige?
Ein Artikel wurde neu angelegt und mit diesem Hook könnte man dann mittels Chain heraus finden, welche IDs vergeben wurden?
Oder ist da dann kein mehr Bezug dazu?
Fliegt der Bauer übers Dach, ist der Wind weißgott nicht schwach.

xmurrix
Beiträge: 2930
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von xmurrix » Fr 23. Okt 2020, 11:47

Faar hat geschrieben:
Fr 23. Okt 2020, 11:39
wäre der Hook "Contenido.Article.AfterCreation" hier nicht genau der richtige?
Gibt es denn in CONTENIDO eine Chain mit dem Namen "Contenido.Article.AfterCreation"?
CONTENIDO downloads: CONTENIDO 4.10.1
CONTENIDO links: Documentation, API documentation
CONTENIDO @ Github: CONTENIDO 4.10

homtata
Beiträge: 1094
Registriert: Mi 14. Jan 2004, 14:41
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von homtata » Fr 23. Okt 2020, 13:24

Hallo Murat,

der vorgeschlagene Weg mit CREATE_SUCCESS funktioniert nicht. Offensichtlich bekomme ich zwar die (zukünftige) idartlang zurück damit, ABER der Artikel ist zum Zeitpunkt, wo der Callback abgearbeitet wird, noch gar nicht in der Datenbank angelegt. Damit kann ich keine Werte wie idart ermitteln. Damit wird das schwierig.
Ich muss für mein Suchtool einen Artikel direkt nach dem Erstellen schonmal in der Suchdatenbanktabelle anlegen. Wenn das so alles nicht geht, muss ich eine komplett andere Strategie fahren. Ich muss dann bei jedem Abspeichern eines Content erstmal prüfen, ob der zu überarbeitende Artikel schon im Suchindex ist, was natürlich nicht so schön ist, als wenn ich ihn direkt anlegen könnte beim Erstellen.

homtata
Beiträge: 1094
Registriert: Mi 14. Jan 2004, 14:41
Kontaktdaten:

Re: Frage zu Chains und Hooks

Beitrag von homtata » Fr 23. Okt 2020, 13:25

den Hook "Contenido.Article.AfterCreation" gibts nicht.

Antworten