Questions & Answers

SOLVED
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

Type a product name