#this gibt in Preview andere Klasse zurück
Hallo zusammen,
ich habe festgestellt, dass das Systemobjekt #this unterschiedliche Klassen für denselben Code ausgibt, je nachdem ob es in der Vorschau oder Generierung passiert. Kann mir das jemand erklären?
Folgende Konstellation habe ich:
- Absatztemplate mit einer CMS_INPUT_IMAGEMAP
- in der ImageMap ein Linktemplate mit einem FS_CATALOG
- Ausgabe von #this im Absatztemplate innerhalb des Katalogs
Folgende Klassen werden von #this zurückgegeben:
- Vorschau:
de.espirit.firstspirit.generate.IdentifiableCatalogCard$IdentifiableCatalogCardItem - Generierung:
de.espirit.firstspirit.store.access.DataWrappingFormData
Das ist ungünstig, denn aus ersterem kann ich z.B. eine UUID auslesen:
#this.getIdentifiers().get(1)
Aus der Klasse DataWrappingFormData bekomme ich aber keine UUID heraus.
Auch wenn ich z.B. mit #this.getHashCode() einen eindeutigen Kennung erzeugen will, funktioniert das in der Preview, aber nicht in der Generierung, weil dann immer derselbe Code ausgegeben wird.
Wer hat dafür eine Erklärung? Warum wird beim Generieren eine andere Klasse erzeugt als in der Vorschau???
Grüße
Matthias
-
Hallo Matthias,
das klingt erst einmal nach einem Bug. Bitte stell ein entsprechendes Ticket bei unserem Customer Support ein. Bitte dabei auch unbedingt die Art angeben, wie generiert wird ("Normale" Generierung, RenderingAgent, CaaS, ...).
Anmerkung: Mit dem RenderingAgent kenne ich einen ähnlichen Bug, der aber seit der FirstSpirit Version 2023.4 behoben wurde (interne ID: CORE-14930). Falls Du wider Erwarten eine ältere Version einsetzen solltest und den RenderingAgent für die Ausgabe nutzt, könnte ein Update das Problem beseitigen.Sehr hilfreich wäre es, wenn Du einen entsprechenden Testfall (z.B. ein Feature mit der entsprechenden Seite und allen verwendeten Vorlagen und Medien) mit einstellen würdest :)
Viele Grüße
Holger0 -
Haha, danke! Vom Support wurde ich hierher verwiesen ;)
Aber dann ergänze ich die entsprechenden Informationen noch im Ticket.
Grüße
Matthias0 -
Hallo Matthias,
ich wurde gerade darauf aufmerksam gemacht, dass Du ein "unerwartetes" Objekt benutzt. Im Catalog steht #card zur Verfügung, mit dem man problemlos die UUID (#card.getId()) ausgeben kann.
Ich hatte bei der Antwort nur auf das unterschiedliche Verhalten zwischen Vorschau und Generierung geachtet und den Anwendungsfall nicht beachtet :(
Viele Grüße
Holger0 -
Hallo Holger,
vielen Dank für den Hinweis auf #card, daran hatte ich gar nicht gedacht. Aber leider funktioniert das sowieso nicht, weil #card nicht zur Verfügung steht, wenn der Catalog mit CMS_FOR abgearbeitet wird (warum eigentlich nicht?). Aufgrund weiterer Formatierungen lässt sich das auch nicht auf eine reine CMS_VALUE Ausgabe umstellen. Nützt mir also leider auch nichts.
Grüße
Matthias0 -
Hallo Matthias,
wenn Du den Catalog mittels CMS_FOR ausgibst, sollte folgendes funktionieren (wie in der Doku beschrieben)
$CMS_FOR(_card,<myCatalog>)$
...
$CMS_VALUE(_card.getId())$
..
$CMS_END_FOR$Und um die Frage zu beantworten, warum beim Iterieren über CMS_FOR das #card Objekt nicht zur Verfügung steht: Weil es nicht benötigt wird. Das Objekt, dass Du beim Iterieren nutzt, ist bereits eine Card, auf der die entsprechenden Funktionen auch zur Verfügung stehen.
Viele Grüße
Holger1 -
Hallo Holger,
danke dafür, das funktioniert!
Ansonsten belasse ich das jetzt dabei. Ich habe ein paar Testtemplates erstellt, die ich an das Ticket hängen wollte, aber die geben bei fast identischer Konstellation in beiden Fällen ein DataWrappingFormData aus. Da ich eine Lösung für das ursprüngliche Problem habe (die ID der Card), stecke ich da keine weitere Energie mehr rein.
Grüße
Matthias0 -
Hallo Matthias,
vielleicht noch ganz kurz ein bisschen Hintergrund: Dass in verschiedenen "Situationen" (hier: Vorschau vs. Generierung) unterschiedliche Klassen kommen, ist "per se" erstmal nicht unbedingt ein Fehler. Technisch bedingt "sieht/bekommt" man beim ja immer ein Objekt der implementierenden, "tatsächlichen" Klasse und insbesondere kein Interface.
Nutzen sollte man aber natürlich nur die entsprechenden Interfaces (selten: Klassen) der öffentlichen API - bzw. nur deren Methoden. Für Eingabekomponenten (bzw. deren Werte) ist hier einer der "Einstiegspunkte, welche das sind" die graue Box oben rechts bei den Eingabekomponenten unter "zu den Methoden". In Deinem Fall Catalog (ok, als kleinen aber dokumentierten Sonderfall im Ausgabekanal sogar einen CatalogAccessor) bzw. für dich eher relevant Card.
Hier ist natürlich sichergestellt, dass - egal welche konkrete Klasse man jeweils bekommt, diese auf jeden Fall das entsprechende Interface implementiert und man dessen Methoden auch nutzen kann, unabhängig von der konkreten Klasse. Heißt aber natürlich: Entsprechend sollte man sich auch nur auf die Methoden dieser Interfaces "verlassen".
In deinem Fall war, soweit ich es beurteilen kann, der entscheidende Punkt der Aufruf von .getIdentifiers(). Diese Methode gibt es eben nicht auf dem eigentlich "relevanten" Interface Card sondern "zufällig" in der im Vorschaukontext genutzten (internen!) konkreten Klasse IdentifiableCatalogCardItem.
Allgemeine Randbemerkung, nicht konkret auf deinen Fall bezogen: Diese Tatsache, also dass immer das Interface relevant ist und sich die konkrete Klasse theoretisch jederzeit ändern kann (in eine andere, die aber eben auch dasselbe Interface implementiert) ist übrigens der Grund, warum man im Ausgabekanal nicht mit sowas wie .getClass().getName() + Stringvergleich (z.B. in If-statements) arbeiten sollte - nur so als Ergänzung, weil mir das auch gelegentlich schon begegnet ist.
Ich hoffe das erklärt es ein wenig :-)
Viele Grüße
Michael1 -
Super erklärt Michael, wenn man instanceof im template benötigt, kann man das z.B. so machen:
$CMS_IF(class("de.espirit.firstspirit.access.editor.fslist.IdProvidingFormData").isCase(object))$Für oft benötigte Klassenobjekte legen wir im project settings template globale Veriablen an:
$CMS_SET(gv_classEntity, class("de.espirit.or.schema.Entity"))$
$CMS_SET(gv_classDataset, class("de.espirit.firstspirit.access.store.contentstore.Dataset"))$
$CMS_SET(gv_classFormData, class("de.espirit.firstspirit.forms.FormData"))$
$CMS_SET(gv_classIdProvidingFormData, class("de.espirit.firstspirit.access.editor.fslist.IdProvidingFormData"))$
$CMS_SET(gv_classFormDataList, class("de.espirit.firstspirit.forms.FormDataList"))$
$CMS_SET(gv_classPage, class("de.espirit.firstspirit.access.store.pagestore.Page"))$
$CMS_SET(gv_classSection, class("de.espirit.firstspirit.access.store.pagestore.Section"))$
$CMS_SET(gv_classSiteStoreFolder, class("de.espirit.firstspirit.access.store.sitestore.SiteStoreFolder"))$
$CMS_SET(gv_classList, class("java.util.List"))$1 -
Danke Heiko :-)
Stimmt, das "Ablegen" der meistgenutzten Klassen ist tatsächlich ein sinnvoller Weg.
Wo wir gerade bei "praktischen Dingen" sind: Wenn man es noch ein bisschen "sprechender" haben will - und das .isCase(...) loswerden will (ich muss da selbst ehrlich gesagt immer wieder nachsehen, was hier die Reihenfolge ist...): Man kann sich ja auch "sowas ähnliches wie Funktionen" bauen mit Lambdas, die man dann mit .eval(...) aufrufen kann (siehe hier - ziemlich am Ende unter "Definition und Aufruf eigener Funktionen").
Heißt: Mit sowas wäre etwas möglich wie
$CMS_IF(func_isInstance.eval({obj: object, class:"Entity"}))$
...
$CMS_END_IF$Im ProjectSettings-Template würde man sich dafür dann ein "passendes" Lambda bauen. Hier mal so gaaanz grob, ungetestet. Und bestimmt noch hübscher möglich ;-)
$CMS_SET(gv_classes,{
"Entity": class("de.espirit.or.schema.Entity"),
"FormData": class("de.espirit.firstspirit.forms.FormData"),
...
})$
$CMS_SET(func_isInstance, params -> gv_classes.get(params.get("class")).isCase(params.get("obj")))$Ok, ist in diesem Kontext vielleicht ein wenig "übertrieben" - ist Geschmackssache :-)
Viele Grüße
Michael0
Please sign in to leave a comment.
Comments
9 comments