Neos: Controller - Seite als Template nutzen

Hallo,

ich möchte ein kleines FAQ-System in Neos einfügen. Dazu benötige ich dynamische Seiten, da die Einträge aus einer API ausgelesen werden. Ich habe mir das wie folgt vorgestellt:

Ich erstelle eine neue Seite in der Baumstruktur. Diese Seite möchte ich im Controller eine Art als Template ausfüllen. Ich erstelle mir NodeTypes, dessen properties ich im Controller überschreiben möchte.

Doch ich habe da ein paar Probleme. Im Router habe ich die Route hilfe-und-support/{cat}/{title}-{id}.html bereits angelegt, der Controller wird auch aufgerufen. Doch wie komme ich da jetzt an mein Template, das ich dort erstellt habe? Und wie kann ich dort beispielsweise weitere Elemente dem main-childNode hinzufügen. Oder eine Variable einer bereits vorhandenen Node ändern? Und wie gebe ich das ordentlich aus?

Ich habe schon sehr viel versucht und probiert, im neos-eigenen Quelltext nachgeschaut, nach Tutorials gesucht. Aber leider ist das System noch nicht so verbreitet, wie ich es mir wünsche. Potenzial ist da!

Für jede Hilfe oder Wink mit dem Zaunpfahl bin ich dankbar.

Je nach Anforderung könntest du programmatisch den Inhalt aus deiner API auslesen und als echte Nodes anlegen. Der Rest funktioniert wie gehabt mit Neos. Mit passenden Restrictions kannst du dann auch verhindern, dass Benutzer die Inhalte direkt in Neos bearbeiten.

Nachteil: Logik zum Synchronisieren wird benötigt.
Vorteil: Caching von Neos wird benutzt. Einbinden von Inhalt aus den FAQs in anderen Seiten über Content References wird einfacher.

In Bezug auf deine ursprüngliche Idee kann ich nicht viel sagen, da mir dafür die nötige Erfahrung fehlt.

Hi Dennis,

was du möchtest nennt sich Neos-Plugin - die Doku dazu findest du hier. Damit kannst du genau das erreichen: Du bettest einen eigenen Controller als NodeType in deiner Seite ein. Darin hast du die gewohnte MVC-Umgebung einem normalen Flow-Controller und Fluid - nur eben innerhalb des Neos-Seitenrenderings. In deinem Controller kannst du dann einen HTTP-Request an deine API absetzen, zB mit curl oder Guzzle. Die einzelnen FAQ-Elemente kannst du dann normal mit Fluid rendern.

Die Alternative von Max-Milan ist ebenso möglich, aber ein bisschen anspruchsvoller zu bauen, denn du musst Nodes per API erzeugen und mit den FAQ-Einträgen, die aus deiner API kommen synchron halten. Geht auch, ist aber vermutlich etwas schwieriger - hat aber die genannten Vorteile.

Melde dich nochmal, falls du nicht weiter kommst, wir helfen sehr gerne :slight_smile:

Moin,

vielen Dank für den Vorschlag Max. So würde ich es gerne machen. Dankeschön!

@Bastian Heist - auch Dir danke für den Vorschlag. Das mit den Plugins kannte ich schon. Aber das ist mir irgendwie zu sehr auf den Esel gehauen. Könntest Du im Bedarfsfall eine Hilfestellung bei der programmatischen Erstellung von Nodes sein? Kennst du dich damit aus? Ich finde im Netz leider nicht sehr viel dazu.

Okay, ich bin soweit. Ich habe jetzt ein Model erstellt, das die einzelnen Inhalte beinhaltet. Das Repo dafür wird per CMD gefüllt und gesynced.

Durch diese möchte ich dann iterieren und dabei die Nodes erstellen.

Folgende Schritte habe ich im Kopf dafür. Doch ich weiß nicht, wie ich programmatisch da hin komme.

  • Eine Seite vom Typ “V.P.ArticlePage” soll erstellt werden,
  • In die childNode “main” soll ein neues Node des Typs “V.P.ContentText” angehängt werden
  • Das ganze soll abgespeichert werden. Und zwar als Child einer FAQ Einstiegsseite, damit in der URL immer “/faq/” davor steht.

Wie ich die Variablen (properties) fülle, weiß ich schon. Da kann ich z.B. auch uriPathSegment (sowie Titel, Text, usw.) setzen.

Ich habe es hinbekommen. Eigentlich nicht kompliziert.

<?php

class … {

// This is the main entry point of the execution
public function syncCommand() {
	$context = $this->contextFactory->create();
	
	// Identifier of the parent node
    $parentNode = $context->getNodeByIdentifier('0afa6741-646d-4aad-abe9-c93254459e3f');

	foreach(... as $entry) {
		$nodes = [
            "page" => null,
            "title" => null,
            "content" => null
        ];

        // Build the page object
        $nodes["page"] = $parentNode->createNodeFromTemplate($this->makePageTemplate($entry));
        
        // Build content, add it to its primary child node
        // Hint: $content->createNodeFromTemplate($node) adds $node at the end of $content
        $content = $nodes["page"]->getPrimaryChildNode();
        $nodes["title"] = $content->createNodeFromTemplate($this->makeTitleTemplate($entry));
        $nodes["content"] = $content->createNodeFromTemplate($this->makeTextTemplate($entry));
        
        // Publish it!
        $this->publishingService->publishNodes($nodes, $context->getWorkspace());

	}
}

private function makePageTemplate($entry) {
    $nodeTemplate = new \Neos\ContentRepository\Domain\Model\NodeTemplate();
    $nodeTemplate->setNodeType($this->nodeTypeManager->getNodeType('Kramer.PrepaidHosterDe:FaqArticle'));
    $nodeTemplate->setName($this->slugify($entry->title));
    $nodeTemplate->setProperty('title', $entry->title);
    $nodeTemplate->setProperty('titleOverride', $entry->title);
    $nodeTemplate->setProperty('uriPathSegment', $this->slugify($entry->title));
    return $nodeTemplate;
}

private function makeTitleTemplate($entry) {
    $nodeTemplate = new \Neos\ContentRepository\Domain\Model\NodeTemplate();
    $nodeTemplate->setNodeType($this->nodeTypeManager->getNodeType('Kramer.PrepaidHosterDe:ContentHeader'));
    $nodeTemplate->setProperty('title', $entry->title);
    return $nodeTemplate;
}

private function makeTextTemplate($entry) {
    $nodeTemplate = new \Neos\ContentRepository\Domain\Model\NodeTemplate();
    $nodeTemplate->setNodeType($this->nodeTypeManager->getNodeType('Kramer.PrepaidHosterDe:ContentText'));
    $nodeTemplate->setProperty('text', $entry->title);
    return $nodeTemplate;
}

?>

Hi Dennis,

ich weiß zwar nicht was “auf den Esel hauen” ist, aber freut mich dass es geklappt hat :wink:
Was dir vermutlich noch fehlt (außer ich habe es übersehen) ist ein Abgleich zwischen bereits angelegten Nodes und noch unsynchronisierten, oder? Sonst bekommst du ja bei jeder Ausführung des SyncCommand neue Einträge.

Ja, ich habe es zunächst so gelöst, dass Nodes vom Typ “FaqArticle” vorher gelöscht werden, dann synced er komplett. Jetzt habe ich aber den NodePath mit in den Model geschrieben und arbeite damit, wenn vorhanden :slight_smile: Es hat alles geklappt. Ich bin sehr zufrieden. Vielen Dank für die Hilfe!

Für eine weitere (kleine Frage) melde ich mich gleich noch mal :stuck_out_tongue:

Je nachdem wie häufig du nun die FAQs neu erstellst werden bestehende Caches gelöscht und neu erstellt. Das kann je nach Last, Umfang und Frequenz unangenehm werden.