Slide 1

Slide 1 text

Sulu CMS Batteries included! Thanks to Symfony.

Slide 2

Slide 2 text

I'm Thomas Schedler @chirimoya | https://github.com/chirimoya ... head of development and technical consultant. Young father trying to master Heston Blumenthal recipes.

Slide 3

Slide 3 text

Who knows Sulu?

Slide 4

Slide 4 text

Sulu – Content Management Platform – Full-Stack Symfony – Made for businesses – Simple UI – High Performance – Open Source

Slide 5

Slide 5 text

For business – Built with the needs of business and industry in mind – Enterprise features without ridiculous license fees – Supports multi-language, multi-portal and multi-channel – Easy to integrate data from external resources

Slide 6

Slide 6 text

For editors – Really simple and very fast user interface – Web-based, no installation required – Edit forms that validate content & ensure correct semantics – Live preview content as you type it – Switch between different devices (Smartphone, Tablet or Desktop)

Slide 7

Slide 7 text

For developers – Full-Stack Symfony environment – Semantic configuration of templates – Easy transition from data to HTML – Build applications around content management – Add/Remove functionality with Symfony Bundles

Slide 8

Slide 8 text

Where we see us Bicycles Everyone can ride them, many can repair it
 (WordPress etc.) Cars Many can ride them, some can repair it
 (TYPO3 etc.) Supertanker Need highly specialized staff, expensive and very complex
 (Hybris, OpenText, AEM etc.) – Trucks Need a special license, must be configured to your needs
 (eZ Publish, Pimcore etc.)

Slide 9

Slide 9 text

When to use Sulu? – Complex website scenarios – News- and media platforms – Brand and corporate presences – Social and collaborative sites – E-Business projects – Handling external data resources – Speed is a critical success factor – Skilled PHP/Symfony developers – Web standards

Slide 10

Slide 10 text

When not to use Sulu? – Sulu is not Wordpress – We don’t recommend to use Sulu for: – Low budget marketing sites – Simple blogs – Hobby websites – (Very) small business websites

Slide 11

Slide 11 text

Getting Started The basic architecture and concepts of Sulu.

Slide 12

Slide 12 text

PHPCR DOCTRINE MySQL, PostgreSQL, Jackrabbit, ... Framework Symfony Symfony CMF Contact Media Content ... Sulu

Slide 13

Slide 13 text

REST API Single-Page Application Your Application Website Admin Symfony Symfony CMF Contact Media Content ... Sulu

Slide 14

Slide 14 text

Webspaces – One single content-structure / page tree – The structure represents one or more websites – Multiple languages implemented as dimensions – Multiple webspaces support Webspace ...

Slide 15

Slide 15 text

Content Tree …

Slide 16

Slide 16 text

We XML

Slide 17

Slide 17 text

example.com example example.com example example.com/{localization} {host}/{localization}

Slide 18

Slide 18 text

Templates & Content Types – The structure of the page – How that structure is rendered – Each page template is defined by two files: – an XML file that contains the page structure – a Twig file that contains the HTML code – A page structure consists of properties, each of which has a content type

Slide 19

Slide 19 text

Content Types ... Live Preview ...

Slide 20

Slide 20 text

default templates/default SuluWebsiteBundle:Default:index 2400

Slide 21

Slide 21 text

{% extends "master.html.twig" %} {% block content %}

{{ content.title }}

{{ content.article|raw }}
{% endblock %}

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Router The main entry point to your Symfony application.

Slide 24

Slide 24 text

/ /services /blog Request Response Kernel Controller Router Request URI Controller & Action Front Controller indexAction() servicesAction() blogAction() Response Response Response Model View Services

Slide 25

Slide 25 text

Chain Router Router / /services /blog Request Response Kernel Controller Request URI Controller & Action Front Controller indexAction() servicesAction() blogAction() Response Response Response Model View Services Dynamic Router

Slide 26

Slide 26 text

Sulu routing summarized – CMF ChainRouter replaces the default routing system – and works by accepting a set of prioritized Routers – The Symfony default Router is registered with the highest priority – DynamicRouters handle all the dynamically defined routes (pages, redirects, …)

Slide 27

Slide 27 text

// app/WebsiteKernel.php
 
 class WebsiteKernel extends AbstractKernel
 {
 /**
 * {@inheritdoc}
 */
 protected $name = 'website';
 
 /**
 * @param string $environment
 * @param bool $debug
 */
 public function __construct($environment, $debug)
 {
 parent::__construct($environment, $debug);
 $this->setContext(self::CONTEXT_WEBSITE);
 }
 
 /**
 * {@inheritdoc}
 */
 public function registerBundles()
 {
 $bundles = parent::registerBundles();
 $bundles[] = new Symfony\Cmf\Bundle\RoutingBundle\CmfRoutingBundle();
 return $bundles;
 }
 } $bundles[] = new AppBundle\AppBundle();

Slide 28

Slide 28 text

// app/config/website/routing.yml app:
 resource: "@AppBundle/Controller/"
 type: annotation
 prefix: /app // src/AppBundle/Controller/DefaultController.php namespace AppBundle\Controller;
 
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
 
 class DefaultController extends Controller
 {
 /**
 * @Route("/")
 */
 public function indexAction()
 {
 return $this->render('AppBundle:Default:index.html.twig');
 }
 } // src/AppBundle/Resources/views/Default/index.html.twig Hallo World!

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Controller & View Add your custom logic within your own content Controller.

Slide 31

Slide 31 text

Controller Chain Router Router / /services /blog Request Response Kernel Request URI Controller & Action Front Controller indexAction() servicesAction() blogAction() Response Response Response Model View Services Dynamic Router Default Controller

Slide 32

Slide 32 text

// app/Resources/templates/pages/default.xml 
 
 
 default
 
 templates/default 
 2400
 
 
 Default
 Standard
 
 
 ... 
 AppBundle:Custom:index

Slide 33

Slide 33 text

// src/AppBundle/Controller/CustomController.php
 
 namespace AppBundle\Controller;
 
 use Sulu\Bundle\WebsiteBundle\Controller\WebsiteController;
 use Sulu\Component\Content\Compat\StructureInterface;
 
 class CustomController extends WebsiteController
 {
 /**
 * My custom controller action.
 *
 * @param StructureInterface $structure
 * @param bool $preview
 * @param bool $partial
 *
 * @return Response
 */
 public function indexAction(StructureInterface $structure, $preview = false, $partial = false)
 {
 $response = $this->renderStructure(
 $structure,
 [
 // here you can add some custom data for your template
 'myData' => $this->get('my_custom_service')->getMyData(),
 ],
 $preview,
 $partial
 );
 
 return $response;
 }
 }

Slide 34

Slide 34 text

Response Format HTML, XML or JSON

Slide 35

Slide 35 text

// app/Resources/templates/pages/default.xml 
 
 
 default
 
 AppBundle:Custom:index
 AppBundle:Custom:index
 2400
 
 ... // src/AppBundle/Resources/views/Custom/index.html.twig
 
 {% extends "master.html.twig" %}
 
 {% block content %}


{{ content.title }}


 


 {{ content.article|raw }}


 {% endblock %} // src/AppBundle/Resources/views/Custom/index.json.twig {{ content|json_encode|raw }} .html.twig

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

HTTP Cache & ESI HTTP Standards FTW!

Slide 38

Slide 38 text

Reverse Proxy Caches – A HTTP Cache is a full page cache – It bypasses your application entirely, if the cache entry is valid – HTTP cache headers are used to mark a response cacheable and for how long – Symfony comes with a reverse proxy written in PHP – Switch to something more robust like Varnish without any problem https://tomayko.com/blog/2008/things-caches-do

Slide 39

Slide 39 text

“ Caching entire responses isn't always possible for highly dynamic sites, or is it?

Slide 40

Slide 40 text

ESI - Edge Side Includes – The ESI specification describes tags to communicate with the gateway cache – In Symfony the is implemented – If the response contains ESI tags, the cache either requests the page fragment from the backend or embeds the fresh cache entry

Slide 41

Slide 41 text

// app/config/config.yml framework:
 ... esi: { enabled: true } // app/Resources/views/Default/index.html.twig
 
 {# you can use a controller reference #}
 {{ render_esi(controller('AppBundle:News:latest', { 'limit': 5 })) }}
 
 {# ... or a URL #}
 {{ render_esi(url('latest_news', { 'limit': 5 })) }}

Slide 42

Slide 42 text

Model Customize what you need.

Slide 43

Slide 43 text

Customizing Models – Doctrine doesn't support model customization – Inheritance leads to multiple tables for the same data structure – Sulu’s PersistenceBundle allows to replace models via configuration – Inspired by Sylius ResourceBundle

Slide 44

Slide 44 text

// src/AppBundle/EntityTag.php // app/config/config.yml sulu_tag: objects: tag: model: AppBundle\Entity\Tag

Slide 45

Slide 45 text

Event Dispatcher Handle additional business logic within your Event Subscriber.

Slide 46

Slide 46 text

Symfony Events Kernel Events – kernel.request – kernel.response – kernel.controller – kernel.view – kernel.terminate … Doctrine Events Lifecycle Events – [pre|post]Remove – [pre|post]Persist – [pre|post]Update – [pre|on|post]Flush – onClear …

Slide 47

Slide 47 text

Sulu Document Manager Events +----------------------+ | Events | +----------------------+ | persist | | hydrate | | remove | | refresh | | copy | | move | | create | | clear | | find | | reorder | | publish | | unpublish | | remove_draft | | flush | | query.create | | query.create_builder | | query.execute | | configure_options | | metadata_load | | restore | +----------------------+ bin/adminconsole sulu:document:subscriber:debug

Slide 48

Slide 48 text

// src/AppBundle/Document/Subscriber/MailSubscriber.php
 
 namespace AppBundle\Document\Subscriber;
 
 use Sulu\Component\DocumentManager\Event\PublishEvent;
 use Sulu\Component\DocumentManager\Events;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 class MailSubscriber implements EventSubscriberInterface
 {
 ...
 
 /**
 * {@inheritdoc}
 */
 public static function getSubscribedEvents()
 {
 return [
 Events::PUBLISH => ['sendNotification', -1000],
 ];
 }
 
 public function sendNotification(PublishEvent $event)
 {
 $message = new \Swift_Message('Page Published', 'URL: ' . $event->getDocument()->getResourceSegment());
 
 $this->mailer->send($message);
 }
 }

Slide 49

Slide 49 text

Service Container The control center for all you application.

Slide 50

Slide 50 text

Service Container – Foundation for extensibility & customizability – Sulu heavily uses service definitions – Add new functionality (Modulnavigation, Content-Type, ...) – Extend existing (Smart-Content, Teaser, ...) – Overwrite services

Slide 51

Slide 51 text

// src/AppBundle/Admin/AppAdmin.php 
 namespace AppBundle\Admin;
 
 use Sulu\Bundle\AdminBundle\Admin\Admin;
 use Sulu\Bundle\AdminBundle\Navigation\Navigation;
 use Sulu\Bundle\AdminBundle\Navigation\NavigationItem;
 
 class AppAdmin extends Admin
 {
 public function __construct($title)
 {
 $rootNavigationItem = new NavigationItem($title);
 $section = new NavigationItem('navigation.modules');
 
 $myModule = new NavigationItem('app.my_module');
 $myModule->setIcon('custom');
 
 $item = new NavigationItem('app.my_module.title');
 $item->setAction('my_module/custom');
 $myModule->addChild($item);
 
 $rootNavigationItem->addChild($section);
 $section->addChild($myModule);
 
 $this->setNavigation(new Navigation($rootNavigationItem));
 }
 } %sulu_admin.name%

Slide 52

Slide 52 text

// src/AppBundle/DependencyInjection/AppCompilerPass.php
 
 namespace AppBundle\DependencyInjection;
 
 use AppBundle\Contact\CustomContactManager;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
 
 class AppCompilerPass implements CompilerPassInterface
 {
 public function process(ContainerBuilder $container)
 {
 $definition = $container->getDefinition('sulu_contact.contact_manager');
 $definition->setClass(CustomContactManager::class);
 $definition->addArgument(new Reference('app.my_custom_service'));
 }
 } // src/AppBundle/AppBundle.php
 
 namespace AppBundle;
 
 use AppBundle\DependencyInjection\AppCompilerPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
 
 class AppBundle extends Bundle
 {
 public function build(ContainerBuilder $container)
 {
 $container->addCompilerPass(new AppCompilerPass());
 }
 }

Slide 53

Slide 53 text

Thanks for watching! www.sulu.io