Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Content-driven Applications mit Neos (German)

hlubek
September 03, 2015

Content-driven Applications mit Neos (German)

Meet Neos, Hamburg 2015 - Wie kann mit Neos aus einer Website eine Applikation entstehen? Ohne viel Code können viele Anforderungen effizient mit Nodes (Content) implementiert werden.

hlubek

September 03, 2015
Tweet

More Decks by hlubek

Other Decks in Programming

Transcript

  1. Christopher Hlubek Geschäftsführer / Leitung Entwicklung im Team seit 2008

    u.a. Content Editing, Content Rendering, TypoScript/Eel/ FlowQuery, Content Cache, Content Dimensions
  2. Product List Node Type Konfiguration 'Demo.ContentApps:Product.List':
 superTypes: ['TYPO3.Neos:Content']
 childNodes:
 'products':


    type: 'TYPO3.Neos:ContentCollection'
 constraints:
 nodeTypes:
 'Demo.ContentApps:Product': TRUE
 '*': FALSE
 ui:
 label: 'Product List'
 icon: 'icon-asterisk'
 inlineEditable: true
 Demo.ContentApp/Configuration/NodeTypes.yaml
  3. Product List Node Type Template <section>
 <div class="container">
 <div class="row">


    <div class="col-md-12">
 <div class="row">
 {products -> f:format.raw()}
 </div>
 </div>
 </div>
 </div>
 </section> Demo.ContentApp/Resources/Private/Templates/NodeTypes/Product.List.html
  4. Product List TypoScript prototype(Demo.ContentApps:Product.List) {
 products = ContentCollection {
 nodePath

    = 'products'
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  5. Product Node Type Konfiguration 'Demo.ContentApps:Product':
 superTypes: ['TYPO3.Neos:Content']
 childNodes:
 'teaserimages':
 type:

    'TYPO3.Neos:ContentCollection'
 constraints:
 nodeTypes:
 'TYPO3.Neos.NodeTypes:Image': TRUE
 '*': FALSE
 ui:
 label: 'Product'
 icon: 'icon-asterisk'
 inspector:
 groups:
 'general':
 label: 'Properties for product'
 position: 10
 properties:
 'title':
 type: string
 ui:
 label: 'Title'
 inlineEditable: true
 aloha:
 placeholder: '<span>Insert title</span>'
 'image':
 type: 'TYPO3\Media\Domain\Model\ImageInterface'
 ui:
 label: 'Image'
 reloadIfChanged: TRUE
 inspector:
 group: 'general'
 'size':
 type: integer
 ui:
 label: 'Size'
 reloadIfChanged: TRUE
 inspector:
 group: 'general' Demo.ContentApp/Configuration/NodeTypes.yaml
  6. Product Node Type Template {namespace neos=TYPO3\Neos\ViewHelpers}
 <div class="listing-item">
 <div class="overlay-container">


    {imageTag -> f:format.raw()}
 <a class="overlay-link" href="{popoverImageUri}">...</a>
 </div>
 <div class="container-fluid">
 {teaserimages -> f:format.raw()}
 </div>
 <div class="body">
 <h3>{neos:contentElement.editable(property: 'title')}</h3>
 <span class="product-size"><strong>Size:</strong> {size}m</span>
 </div>
 </div> Demo.ContentApp/Resources/Private/Templates/NodeTypes/Product.html
  7. Product TypoScript prototype(Demo.ContentApps:Product) {
 teaserimages = ContentCollection {
 attributes.class =

    'row'
 nodePath = 'teaserimages'
 
 prototype(TYPO3.Neos.NodeTypes:Image) {
 attributes.class = 'col-md-3'
 maximumWidth = 80
 maximumHeight = 80
 }
 }
 imageTag = ImageTag {
 maximumWidth = 400
 maximumHeight = 400
 asset = ${q(node).property('image')}
 }
 popoverImageUri = ImageUri {
 maximumWidth = 1200
 maximumHeight = 800
 asset = ${q(node).property('image')}
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  8. Product List Node Type Konfiguration 'Demo.ContentApps:Product.List':
 superTypes: ['TYPO3.Neos:Content']
 ui:
 label:

    'Product List'
 icon: 'icon-asterisk'
 inlineEditable: true
 Demo.ContentApp/Configuration/NodeTypes.yaml
  9. Product List TypoScript prototype(Demo.ContentApps:Product.List) {
 products = TYPO3.TypoScript:Collection {
 collection

    = ${q(site).find('[instanceof Demo.ContentApps:Product]')}
 itemName = 'node'
 itemRenderer = Demo.ContentApps:Product.Teaser
 }
 
 @cache {
 mode = 'cached'
 entryTags {
 1 = ${Caching.nodeTypeTag('Demo.ContentApps:Product')}
 }
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  10. Product List TypoScript prototype(Demo.ContentApps:Product.List) {
 products = TYPO3.TypoScript:Collection {
 collection

    = ${q(site).find('[instanceof Demo.ContentApps:Product]')}
 itemName = 'node'
 itemRenderer = Demo.ContentApps:Product.Teaser
 }
 
 @cache {
 mode = 'cached'
 entryTags {
 1 = ${Caching.nodeTypeTag('Demo.ContentApps:Product')}
 }
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  11. Product TypoScript prototype(PrimaryContent) {
 product {
 condition = ${q(node).is('[instanceof Demo.ContentApps:Product]')}


    renderer = Demo.ContentApps:Product
 }
 }
 
 prototype(Demo.ContentApps:Product) < prototype(TYPO3.Neos:Content) {
 teaserimages = ContentCollection {
 attributes.class = 'row'
 nodePath = 'teaserimages'
 
 prototype(TYPO3.Neos.NodeTypes:Image) {
 attributes.class = 'col-md-3'
 maximumWidth = 200
 maximumHeight = 200
 }
 }
 imageTag = ImageTag {
 maximumWidth = 800
 maximumHeight = 600
 asset = ${q(node).property('image')}
 }
 popoverImageUri = ImageUri {
 maximumWidth = 1200
 maximumHeight = 800
 asset = ${q(node).property('image')}
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  12. Product TypoScript prototype(PrimaryContent) {
 product {
 condition = ${q(node).is('[instanceof Demo.ContentApps:Product]')}


    renderer = Demo.ContentApps:Product
 }
 }
 
 prototype(Demo.ContentApps:Product) < prototype(TYPO3.Neos:Content) {
 teaserimages = ContentCollection {
 attributes.class = 'row'
 nodePath = 'teaserimages'
 
 prototype(TYPO3.Neos.NodeTypes:Image) {
 attributes.class = 'col-md-3'
 maximumWidth = 200
 maximumHeight = 200
 }
 }
 imageTag = ImageTag {
 maximumWidth = 800
 maximumHeight = 600
 asset = ${q(node).property('image')}
 }
 popoverImageUri = ImageUri {
 maximumWidth = 1200
 maximumHeight = 800
 asset = ${q(node).property('image')}
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  13. Product TypoScript prototype(PrimaryContent) {
 product {
 condition = ${q(node).is('[instanceof Demo.ContentApps:Product]')}


    renderer = Demo.ContentApps:Product
 }
 }
 
 prototype(Demo.ContentApps:Product) < prototype(TYPO3.Neos:Content) {
 teaserimages = ContentCollection {
 attributes.class = 'row'
 nodePath = 'teaserimages'
 
 prototype(TYPO3.Neos.NodeTypes:Image) {
 attributes.class = 'col-md-3'
 maximumWidth = 200
 maximumHeight = 200
 }
 }
 imageTag = ImageTag {
 maximumWidth = 800
 maximumHeight = 600
 asset = ${q(node).property('image')}
 }
 popoverImageUri = ImageUri {
 maximumWidth = 1200
 maximumHeight = 800
 asset = ${q(node).property('image')}
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  14. Product Teaser TypoScript 
 prototype(Demo.ContentApps:Product.Teaser) < prototype(Demo.ContentApps:Product) {
 templatePath =

    'resource://Demo.ContentApps/Private/Templates/NodeTypes/ Product.Teaser.html'
 
 imageTag = ImageTag {
 maximumWidth = 400
 maximumHeight = 400
 }
 
 teaserimages {
 prototype(TYPO3.Neos.NodeTypes:Image) {
 maximumWidth = 80
 maximumHeight = 80
 }
 }
 }
 Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  15. Ausgabe unterschiedlicher Formate (XML, JSON, …) prototype(TYPO3.Neos.Seo:XmlSitemap) < prototype(TYPO3.TypoScript:Http.Message) {


    doctype = '<?xml version="1.0" encoding="UTF-8"?>'
 httpResponseHead.headers.Content-Type = 'text/xml'
 
 body = Menu {
 root = ${site}
 entryLevel = 0
 maximumLevels = 999
 renderHiddenInIndex = TRUE
 templatePath = 'resource://TYPO3.Neos.Seo/Private/Templates/Page/XmlSiteMap.xml'
 startingPoint = ${site}
 
 @cache.entryTags.1 = ${'DescendantOf_' + this.startingPoint.identifier}
 }
 }
 
 root.xmlSitemap {
 condition = ${request.format == 'xml.sitemap'}
 type = 'TYPO3.Neos.Seo:XmlSitemap'
 }
  16. Konfigurator Plugin prototype(Demo.ContentApps:Product) < prototype(TYPO3.Neos:Content) {
 buildPlugin = Plugin {


    package = 'Demo.ContentApps'
 controller = 'Build'
 action = 'index'
 }
 ...
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2
  17. Konfigurator Plugin prototype(Demo.ContentApps:Product) < prototype(TYPO3.Neos:Content) {
 buildPlugin = Plugin {


    package = 'Demo.ContentApps'
 controller = 'Build'
 action = 'index'
 }
 ...
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Product.ts2 Easy!
  18. Konfigurator Controller class BuildController extends \TYPO3\Flow\Mvc\Controller\ActionController {
 
 protected $defaultViewObjectName

    = 'Demo\ContentApps\View\PluginTypoScriptView';
 
 /**
 * Display a start page for the product
 */
 public function indexAction() {
 /** @var NodeInterface $node */
 $node = $this->request->getInternalArgument('__node');
 
 if (!$node->getNodeType()->isOfType('Demo.ContentApps:Product')) {
 throw new InvalidNodeException(sprintf('Build plugin needs node of type "Demo.ContentApps:Product", got "%s"', $node->getNodeType()->getName()), 1432633804);
 }
 
 $this->view->assign('node', $node);
 } ... } Demo\ContentApps\Controller\BuildController
  19. Konfigurator Controller class BuildController extends \TYPO3\Flow\Mvc\Controller\ActionController {
 
 protected $defaultViewObjectName

    = 'Demo\ContentApps\View\PluginTypoScriptView';
 
 /**
 * Display a start page for the product
 */
 public function indexAction() {
 /** @var NodeInterface $node */
 $node = $this->request->getInternalArgument('__node');
 
 if (!$node->getNodeType()->isOfType('Demo.ContentApps:Product')) {
 throw new InvalidNodeException(sprintf('Build plugin needs node of type "Demo.ContentApps:Product", got "%s"', $node->getNodeType()->getName()), 1432633804);
 }
 
 $this->view->assign('node', $node);
 } ... } Demo\ContentApps\Controller\BuildController Plugin nutzt TypoScript für View Derzeit Custom
  20. Konfigurator Controller class BuildController extends \TYPO3\Flow\Mvc\Controller\ActionController {
 
 protected $defaultViewObjectName

    = 'Demo\ContentApps\View\PluginTypoScriptView';
 
 /**
 * Display a start page for the product
 */
 public function indexAction() {
 /** @var NodeInterface $node */
 $node = $this->request->getInternalArgument('__node');
 
 if (!$node->getNodeType()->isOfType('Demo.ContentApps:Product')) {
 throw new InvalidNodeException(sprintf('Build plugin needs node of type "Demo.ContentApps:Product", got "%s"', $node->getNodeType()->getName()), 1432633804);
 }
 
 $this->view->assign('node', $node);
 } ... } Demo\ContentApps\Controller\BuildController Übergabe von Werten via TypoScript Plugin nutzt TypoScript für View Derzeit Custom
  21. Konfigurator Controller class BuildController extends \TYPO3\Flow\Mvc\Controller\ActionController {
 ...
 
 /**


    * @param NodeInterface $step
 * @param array $selections An array from element identifier to option identifier
 */
 public function selectAction(NodeInterface $step, array $selections = array()) {
 /** @var NodeInterface $node */
 $node = $this->request->getInternalArgument('__node');
 
 foreach ($selections as $elementIdentifier => $optionIdentifiers) {
 $element = $node->getContext()->getNodeByIdentifier($elementIdentifier);
 if (!is_array($optionIdentifiers)) {
 $optionIdentifiers = array($optionIdentifiers);
 }
 $optionNodes = array_map(function($optionIdentifier) use ($node) {return $node->getContext()->getNodeByIdentifier($optionIdentifier);}, $optionIdentifiers);
 $this->build->addSelection($step, $element, $optionNodes);
 }
 
 $fq = new \TYPO3\Eel\FlowQuery\FlowQuery(array($step));
 $nextStep = $fq->next('[instanceof Demo.ContentApps:Step]')->get(0);
 
 if ($nextStep === NULL) {
 $this->forward('finish');
 } else {
 $this->forward('step', NULL, NULL, array('step' => $nextStep));
 }
 } } Demo\ContentApps\Controller\BuildController
  22. Konfigurator Controller class BuildController extends \TYPO3\Flow\Mvc\Controller\ActionController {
 ...
 
 /**


    * @param NodeInterface $step
 * @param array $selections An array from element identifier to option identifier
 */
 public function selectAction(NodeInterface $step, array $selections = array()) {
 /** @var NodeInterface $node */
 $node = $this->request->getInternalArgument('__node');
 
 foreach ($selections as $elementIdentifier => $optionIdentifiers) {
 $element = $node->getContext()->getNodeByIdentifier($elementIdentifier);
 if (!is_array($optionIdentifiers)) {
 $optionIdentifiers = array($optionIdentifiers);
 }
 $optionNodes = array_map(function($optionIdentifier) use ($node) {return $node->getContext()->getNodeByIdentifier($optionIdentifier);}, $optionIdentifiers);
 $this->build->addSelection($step, $element, $optionNodes);
 }
 
 $fq = new \TYPO3\Eel\FlowQuery\FlowQuery(array($step));
 $nextStep = $fq->next('[instanceof Demo.ContentApps:Step]')->get(0);
 
 if ($nextStep === NULL) {
 $this->forward('finish');
 } else {
 $this->forward('step', NULL, NULL, array('step' => $nextStep));
 }
 } } Demo\ContentApps\Controller\BuildController Node als Argument
  23. Konfigurator Controller class BuildController extends \TYPO3\Flow\Mvc\Controller\ActionController {
 ...
 
 /**


    * @param NodeInterface $step
 * @param array $selections An array from element identifier to option identifier
 */
 public function selectAction(NodeInterface $step, array $selections = array()) {
 /** @var NodeInterface $node */
 $node = $this->request->getInternalArgument('__node');
 
 foreach ($selections as $elementIdentifier => $optionIdentifiers) {
 $element = $node->getContext()->getNodeByIdentifier($elementIdentifier);
 if (!is_array($optionIdentifiers)) {
 $optionIdentifiers = array($optionIdentifiers);
 }
 $optionNodes = array_map(function($optionIdentifier) use ($node) {return $node->getContext()->getNodeByIdentifier($optionIdentifier);}, $optionIdentifiers);
 $this->build->addSelection($step, $element, $optionNodes);
 }
 
 $fq = new \TYPO3\Eel\FlowQuery\FlowQuery(array($step));
 $nextStep = $fq->next('[instanceof Demo.ContentApps:Step]')->get(0);
 
 if ($nextStep === NULL) {
 $this->forward('finish');
 } else {
 $this->forward('step', NULL, NULL, array('step' => $nextStep));
 }
 } } Demo\ContentApps\Controller\BuildController Node als Argument Kann Session benutzen
  24. Konfigurator Controller class BuildController extends \TYPO3\Flow\Mvc\Controller\ActionController {
 ...
 
 /**


    * @param NodeInterface $step
 * @param array $selections An array from element identifier to option identifier
 */
 public function selectAction(NodeInterface $step, array $selections = array()) {
 /** @var NodeInterface $node */
 $node = $this->request->getInternalArgument('__node');
 
 foreach ($selections as $elementIdentifier => $optionIdentifiers) {
 $element = $node->getContext()->getNodeByIdentifier($elementIdentifier);
 if (!is_array($optionIdentifiers)) {
 $optionIdentifiers = array($optionIdentifiers);
 }
 $optionNodes = array_map(function($optionIdentifier) use ($node) {return $node->getContext()->getNodeByIdentifier($optionIdentifier);}, $optionIdentifiers);
 $this->build->addSelection($step, $element, $optionNodes);
 }
 
 $fq = new \TYPO3\Eel\FlowQuery\FlowQuery(array($step));
 $nextStep = $fq->next('[instanceof Demo.ContentApps:Step]')->get(0);
 
 if ($nextStep === NULL) {
 $this->forward('finish');
 } else {
 $this->forward('step', NULL, NULL, array('step' => $nextStep));
 }
 } } Demo\ContentApps\Controller\BuildController Node als Argument FlowQuery auch in PHP Kann Session benutzen
  25. Plugin TypoScript View plugins.Build.index = TYPO3.TypoScript:Template {
 templatePath = 'resource://Demo.ContentApps/Private/Templates/Build/Index.html'


    
 node = ${node}
 firstStep = ${q(node).children('[instanceof Demo.ContentApps:Step]').first().get(0)}
 
 customerInformation = TYPO3.Neos:ContentCollection {
 nodePath = 'customerinformation'
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Plugin.ts2
  26. Plugin TypoScript View plugins.Build.index = TYPO3.TypoScript:Template {
 templatePath = 'resource://Demo.ContentApps/Private/Templates/Build/Index.html'


    
 node = ${node}
 firstStep = ${q(node).children('[instanceof Demo.ContentApps:Step]').first().get(0)}
 
 customerInformation = TYPO3.Neos:ContentCollection {
 nodePath = 'customerinformation'
 }
 } Demo.ContentApp/Resources/Private/TypoScript/NodeTypes/Plugin.ts2 Rendering von Content aus dem Plugin!
  27. ?

  28. Neos Frontend Rendering Neos TypoScript View Build Plugin Controller Plugin

    TypoScript View Neos Content Rendering Plugin Template