In einem kürzlichen Anwendungsfall benötigte ich in einem Projekt die Möglichkeit, das View Template dynamisch aus der Datenbank anstatt aus dem Resource/Private/Template Verzeichnis aus einer HTML View zu laden. Dies könnte zum Beispiel dann sinnvoll sein, wenn ein Backend/Admin Benutzer (wie in meinem Fall) in der Lage sein soll den View Code zu editieren.

Ein klassischer Anwendungsfall könnte das Editieren eines kleinen Partials bzw. dem Inhalt eines HTML Newsletter sein.
Leider bietet TYPO3 Flow von Hause aus keine Möglichkeit, den View Code dynamisch zu injecten anstatt aus einer HTML Datei zu laden.

Eine Möglichkeit soll dieser Artikel mit Hilfe einer eigenen View Klasse aufzeigen:

Die View Klasse

Wir erstellen in unserem Package eine eigene View Klasse. Dazu ist es wichtig, dass die Datei innerhalb einer Ordnerstruktur Classes/Vendor/Package/View/MyController/MyActionName.php angelegt wird.
Zur Erklärung: Die View Klasse muss den Namen der Controller Action tragen, in welcher sie automatisch verwendet werden soll. Damit das Funktioniert muss die View Klasse zusätzlich in einem Ordner mit dem Namen des entsprechenden Controllers abgelegt werden. Daraus ergibt sich die Verzeichnisstruktur View/MyController/MyActionName.php

Da wir innerhalb des View Template Codes in der Lage sein wollen Fluid Code zu rendern, müssen wir unsere eigene View Klasse von \TYPO3\Fluid\View\TemplateView ableiten.
Im Großen und Ganzen besteht der gesamte Code unserer eigenen View Klasse aus den wichtigsten Methoden der abgeleiteten Basis Klasse mit ein paar wenigen aber tiefgreifenden Eingriffen in den Code

class MyActionName extends \TYPO3\Fluid\View\TemplateView {

    /**
     * @var string
     */
    protected $xmlTemplateCode = "";

    /**
     * @param $xmlTemplateCode
     */
    public function setXmlTemplateCode($xmlTemplateCode) {
        $this->xmlTemplateCode = $xmlTemplateCode;
    }

    /**
     * Resolve the template path and filename for the given action. If $actionName
     * is NULL, looks into the current request.
     *
     * @param string $actionName Name of the action. If NULL, will be taken from request.
     * @throws Exception\InvalidTemplateResourceException
     * @throws \TYPO3\Fluid\View\Exception\InvalidTemplateResourceException
     * @return string Full path to template
     */
    protected function getTemplateSource($actionName = NULL) {
        // This is the modified Part
        if ($this->xmlTemplateCode != "") {
            return $this->xmlTemplateCode;
        }

        // This can be deleted
        $templatePathAndFilename = $this->getTemplatePathAndFilename($actionName);
        $templateSource = Files::getFileContents($templatePathAndFilename, FILE_TEXT);
        if ($templateSource === FALSE) {
            throw new Exception\InvalidTemplateResourceException('"' . $templatePathAndFilename . '" is not a valid template resource URI.', 1257246929);
        }

        return $templateSource;
    }


    /**
     * Original Render Code with a little adjustment!!!!!
     * isCompilable is always set to FALSE
     * This enables to load a different Fluid Template on each request without cache
     *
     *
     * Loads the template source and render the template.
     * If "layoutName" is set in a PostParseFacet callback, it will render the file with the given layout.
     *
     * @param string $actionName If set, the view of the specified action will be rendered instead. Default is the action specified in the Request object
     * @return string Rendered Template
     * @api
     */
    public function render($actionName = NULL) {
        $this->baseRenderingContext->setControllerContext($this->controllerContext);
        $this->templateParser->setConfiguration($this->buildParserConfiguration());

        $templateIdentifier = $this->getTemplateIdentifier($actionName);
        if ($this->templateCompiler->has($templateIdentifier)) {
            $parsedTemplate = $this->templateCompiler->get($templateIdentifier);
        } else {
            $parsedTemplate = $this->templateParser->parse($this->getTemplateSource($actionName));

            // Set isCompilable to FALSE
            $parsedTemplate->setCompilable(false);

            if ($parsedTemplate->isCompilable()) {
                $this->templateCompiler->store($templateIdentifier, $parsedTemplate);
            }
        }

        if ($parsedTemplate->hasLayout()) {
            $layoutName = $parsedTemplate->getLayoutName($this->baseRenderingContext);
            $layoutIdentifier = $this->getLayoutIdentifier($layoutName);
            if ($this->templateCompiler->has($layoutIdentifier)) {
                $parsedLayout = $this->templateCompiler->get($layoutIdentifier);
            } else {
                $parsedLayout = $this->templateParser->parse($this->getLayoutSource($layoutName));
                if ($parsedLayout->isCompilable()) {
                    $this->templateCompiler->store($layoutIdentifier, $parsedLayout);
                }
            }
            $this->startRendering(self::RENDERING_LAYOUT, $parsedTemplate, $this->baseRenderingContext);
            $output = $parsedLayout->render($this->baseRenderingContext);
            $this->stopRendering();
        } else {
            $this->startRendering(self::RENDERING_TEMPLATE, $parsedTemplate, $this->baseRenderingContext);
            $output = $parsedTemplate->render($this->baseRenderingContext);
            $this->stopRendering();
        }

        return $output;
    }
}

Anwendung / Im Controller

Um nun innerhalb unseres Controllers bzw. genauer gesagt unserer myActionName Action den View Template Code dynamisch zu laden ist lediglich eine Zeile Code notwendig:

/**
 * MyActionName Action
 */
public function myActionName() {

    $myTemplateCode = "
….
"; // Load this from Database or WebService

    $this->view->setXmlTemplateCode($myTemplateCode); // This allows to set a XMLTemplate which is stored in Database
}

In meinem Beispiel arbeite ich mit XML Template Code welcher aus einer Datenbank injected wird, daher der Name der Methode setXmlTemplateCode.

Categories: Neos Flow