FabsH
I'm new here

Datenbankabfrage aus einer Vorlage heraus

Jump to solution

Hallo zusammen,

ich habe in FS ein Datenbank-Schema ("fabian") angelegt. Darin enthalten ist u.a. die Tabelle "divisions" mit den Spalten "name" und "description". Ebenso gibt es eine Tabellen-Vorlage mit dem Ref-Namen "fabiandivisions".

Nun frage ich mich, ob ich die Inhalte dieser Tabelle im HTML-Bereich einer Absatzvorlage abfragen kann. Das müsste ich ja im CMS_HEADER erreichen, stehe aber gerade auf dem Schlauch, wie ich dies erreichen kann.. Hier meine Abfrage:

<CMS_FUNCTION name="contentSelect" resultname="getDivisions">

   <CMS_PARAM  name="schema" value="fabian" />

   <QUERY entityType="divisons">

     <NOTNULL attribute="name"/>

   </QUERY>

</CMS_FUNCTION>

Zusätzlich würde mich interessieren, ob ich eine CMS_FUNCTION erstellen kann, in der auf die DB-Abfrage eine CMS_CDATA_PARAM-Anweisung kommt. Also etwa so:

<CMS_FUNCTION name="contentSelect" resultname="getDivisions">

   <CMS_PARAM  name="schema" value="fabian" />

   <QUERY entityType="divisons">

     <NOTNULL attribute="name"/>

   </QUERY>

   <CMS_CDATA_PARAM name="source">

    <![CDATA[

    Inhalt

    ]]>

   </CMS_CDATA_PARAM>

</CMS_FUNCTION>

Oder kann ich innerhalb eines CMS_HEADER von einer Funktion eine andere aufrufen? Dann würde ich eine DB-Abfrage-Funktion und eine CDATA-Funktion schreiben und aus der CDATA-Funktion auf die Ergebnisse der DB-Abfrage-Funktion zugreifen.

Danke!

Grüße,

Fabian

1 Solution

Accepted Solutions
mbergmann
Crownpeak employee

Hallo Fabian,

Dein Problem ist erstmal, dass das "mehrteilige" CMS_SET (also das mit CMS_END_SET) keinen String o.ä. erzeugt sondern eben ein TemplateDocument. Das kann man sich als eine Art Funktion vorstellen, die auch tatsächlich erst beim Aufruf (und zwar bei jedem Aufruf) entsprechend des jeweils geltenden Variablenkontextes ausgewertet wird. Letztlich passt das nicht zu Deinem Anwendungsfall.

Bei Dir passen die ganzen Namen irgendwie nicht zusammen. Du musst die Liste schon von "außen" - also beim Rendertemplate-Aufruf - reingeben und nicht erst im Rendertemplate definieren. Der Aufruf eines Rendertemplates erzeugt einen eigenen Scope - auf Variablen, die erst dort definiert werden, hast Du dann im Absatztemplate keinen Zugriff.

Ganz grob sollte es ungefähr so aussehen:

Im Absatztemplate:

$CMS_SET(set_divisions,[])$

$CMS_RENDER(template:"previewoverlay_divisions_query", resultContainer:set_divisions)$

$CMS_FOR(for_divisionEntry, set_divisions)$

  ...

  $CMS_VALUE(for_divisionEntry.SPALTENNAME)$

  ...

$CMS_END_FOR$

Im Rendertemplate:

<CMS_HEADER>

  <CMF_FUNCTION name="contentSelect" resultname="fr_divisions">

  ...

  </CMS_FUNCTION>

</CMS_HEADER>

$CMS_FOR(for_entry,fr_divisions)$

  $CMS_SET(void, resultContainer.add(for_entry))$

$CMS_END_FOR$

Anmerkung: Das führt dazu, dass die DB-Einträge im Speicher landen. Wenn es nur wenige sind, ist das OK.

Viele Grüße

Michael

View solution in original post

7 Replies
neumann
Crownpeak employee

Hallo Fabian

Grundsätzlich liegst du schon richtig darin, dass du Abfragen im CMS_HEADER machen kannst. Siehe dazu auch

http://www.e-spirit.com/odfs52/vorlagenentwick/vorlagensyntax/datenbank-abfra/

Ich verstehe leider nicht genau wo es bei dir hapert. Geht es bei deiner Frage darum wie man dann in der HTML-Vorlage auf die Ergebnisse der Abfrage aus dem Header zugreift?

Viele Grüße

Emre

0 Kudos

Hallo Emre,

erstmal vielen Dank für deine Antwort. Dadurch bin ich schon mal ein gutes Stück weiter gekommen.

Mein Script sieht jetzt wie folgt aus:

//!Beanshell

import de.espirit.firstspirit.access.store.Store;

import de.espirit.firstspirit.access.store.Store.Type;

import de.espirit.firstspirit.access.store.templatestore.Query;

import de.espirit.firstspirit.access.store.templatestore.Schema;

import de.espirit.firstspirit.agency.SpecialistsBroker;

import de.espirit.firstspirit.agency.StoreAgent;

import de.espirit.firstspirit.service.value.ValueService;

import de.espirit.or.EntityList;

import de.espirit.or.Session;

import de.espirit.or.schema.Entity;

import de.espirit.or.query.*;

StoreAgent storeAgent = context.requireSpecialist(StoreAgent.TYPE);

Store store = storeAgent.getStore(Type.TEMPLATESTORE);

Schema schema = store.getSchemes().getSchemaByName("fabian");

Session session = schema.getSession();

Select select = session.createSelect("divisions");

EntityList entityList = session.executeQuery(select);

return(entityList);

Somit erhalte ich wie gewünscht alle Einträge aus der Tabelle

In meiner Vorlage führe ich das Script mittels CMS_RENDER aus. Doof ist nur, dass CMS_RENDER ja immer ein Object vom Typ de.espirit.firstspirit.parser.impl.TemplateDocumentImpl zurückgibt. Bedeutet ja für mich, dass ich das Ergebnis meines Scripts nicht mit einer for-Schleife durchlaufen kann.

Kann ich also das Abfrageergebnis irgendwie in eine Liste übertragen? Im ODFS wird ein Beispiel genannt, wie eine Liste erzeugt wird:

$CMS_SET(myList, [])$

$CMS_SET(void, myList.add("1.Eintrag"))$

Für meinen Fall müsste ich also etwas in dieser Richtung nutzen (wenn es das gibt) um die "Umwandlung" durch CMS_RENDER in ein TemplateDocument zu umgehen:

$CMS_SET(divisions, script:"mein_abfrag_script")$

Geht das irgendwie?

Danke!

0 Kudos
mbergmann
Crownpeak employee

Hallo Fabian,

zum Thema "Rückgabe aus einem Script" schau einfach mal hier.

Allerdings: An die Einträge der Tabelle kommst Du auch ganz ohne Skript, eben per ContentSelect. Und Dein Skript berücksichtigt übrigens auch nicht den Freigabestand, d.h. Du gibst immer den Current stand aus - auch bei einer Generierung.

Ich würde das mit einem normalen ContentSelect lösen.

Viele Grüße

Michael

0 Kudos

Hallo Michael,

der Hinweis auf contentSelect ist natürlich richtig. Das ist ein Ansatz den ich ebenfalls verfolge. Und gerade mit der Freigabegeschichte ist das natürlich so eine Sache.

In Sachen ContentSelect gehe ich bisher den Weg, dass ich eine Formatvorlage erstellt habe, welche im Header die Abfrage mittels ContentSelect tätigt. Nach dem Header schreibe ich die Daten in eine Liste:

$CMS_SET(divisions, [])$

$CMS_FOR(x, mod_divisions)$

    $CMS_SET(void, divisions.add(x))$

$CMS_END_FOR$

$CMS_VALUE(divisions)$

In meiner Absatzvorlage greife ich wie folgt darauf zu:

$CMS_SET(set_divisions)$

    $CMS_RENDER(template:"previewoverlay_divisions_query", resultContainer:set_divisions)$

$CMS_END_SET$

Das Ergebnis ist nur leider dasselbe wie bei der Script-Variante. Ich bekomme ein Objekt des Typ TemplateDocumentImpl zurück.

Ein Gedanke von mir war, die Ausgabe komplett in der Formatvorlage zu gestalten. Dagegen spricht aber, dass ich das Abfrageergebnis in verschiedenen Absatzvorlagen benutzen will/muss und die HTML-Ausgabe jedesmal anders aussieht.

Wie schaffe ich es also, dass ich das Ergebnis meiner Datenbankabfrage aus der Formatvorlage in einer Liste in meiner Absatzvorlage verwenden kann? Leider bin ich weder hier im Forum noch im ODFS fündig geworden.

Vielen Dank schon mal mbergmann

Fabian

0 Kudos
mbergmann
Crownpeak employee

Hallo Fabian,

Dein Problem ist erstmal, dass das "mehrteilige" CMS_SET (also das mit CMS_END_SET) keinen String o.ä. erzeugt sondern eben ein TemplateDocument. Das kann man sich als eine Art Funktion vorstellen, die auch tatsächlich erst beim Aufruf (und zwar bei jedem Aufruf) entsprechend des jeweils geltenden Variablenkontextes ausgewertet wird. Letztlich passt das nicht zu Deinem Anwendungsfall.

Bei Dir passen die ganzen Namen irgendwie nicht zusammen. Du musst die Liste schon von "außen" - also beim Rendertemplate-Aufruf - reingeben und nicht erst im Rendertemplate definieren. Der Aufruf eines Rendertemplates erzeugt einen eigenen Scope - auf Variablen, die erst dort definiert werden, hast Du dann im Absatztemplate keinen Zugriff.

Ganz grob sollte es ungefähr so aussehen:

Im Absatztemplate:

$CMS_SET(set_divisions,[])$

$CMS_RENDER(template:"previewoverlay_divisions_query", resultContainer:set_divisions)$

$CMS_FOR(for_divisionEntry, set_divisions)$

  ...

  $CMS_VALUE(for_divisionEntry.SPALTENNAME)$

  ...

$CMS_END_FOR$

Im Rendertemplate:

<CMS_HEADER>

  <CMF_FUNCTION name="contentSelect" resultname="fr_divisions">

  ...

  </CMS_FUNCTION>

</CMS_HEADER>

$CMS_FOR(for_entry,fr_divisions)$

  $CMS_SET(void, resultContainer.add(for_entry))$

$CMS_END_FOR$

Anmerkung: Das führt dazu, dass die DB-Einträge im Speicher landen. Wenn es nur wenige sind, ist das OK.

Viele Grüße

Michael

Hallo Fabian,

alternativ könntest Du auch folgendes versuchen:

Im Absatztemplate:

$CMS_SET(set_divisionResultContainer,[])$

$CMS_RENDER(template:"previewoverlay_divisions_query", resultContainer:set_divisionResultContainer)$

$CMS_SET(set_divisions, set_divisionResultContainer.first)$

$CMS_FOR(for_divisionEntry, set_divisions)$

  ...

  $CMS_VALUE(for_divisionEntry.SPALTENNAME)$

  ...

$CMS_END_FOR$

Im Rendertemplate:

<CMS_HEADER>

  <CMF_FUNCTION name="contentSelect" resultname="fr_divisions">

  ...

  </CMS_FUNCTION>

</CMS_HEADER>

$CMS_SET(void, resultContainer.add(fr_divisions))$

Der Unterschied ist hier, dass die einzelnen Einträge nicht erst alle in den Speicher gepackt werden - ein ContentSelect arbeitet nämlich "lazy". Stattdessen gibst Du einfach das Listable zurück. Auch hier brauchst Du einen Container, in den der Wert hineingelegt wird. In diesem Fall enthält er nach dem CMS_RENDER-Aufruf allerdings nur ein Element (eben das Listable).

Habe ich in der Form allerdings noch nicht ausprobiert.

Eine weitere Variante wäre grundsätzlich - vor allem wenn Du es öfter brauchst und es eher wenige Datensätze sind - die Abfrage im Ausgabekanal des Projekteinstellungs-Templates zu machen. Der wird nämlich einmal zu Beginn jeder Generierung (allerdings auch bei jeder Preview-Anforderung) ausgewertet, die dort definierten Variablen stehen dann im root-Kontext zur Verfügung.

Viele Grüße

Michael

mbergmann​, ich danke dir! Du hast meinen Tag gerettet Smiley Happy

0 Kudos