Slide 1

Slide 1 text

I installed this free plugin and now the whole website crashed! It can´t take that long to add this simple feature! Why is our website so slow?! Can we add a new language to the site by tomorrow? Sulu CMS Batteries included! Thanks to Symfony.

Slide 2

Slide 2 text

I'm Thomas Schedler @chirimoya | https://github.com/chirimoya

Slide 3

Slide 3 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 4

Slide 4 text

Router The main entry point to your Symfony application.

Slide 5

Slide 5 text

Request Response

Slide 6

Slide 6 text

/ /services /blog Request Response

Slide 7

Slide 7 text

/ /services /blog Request Response Front Controller

Slide 8

Slide 8 text

/ /services /blog Request Response Kernel Front Controller

Slide 9

Slide 9 text

/ /services /blog Request Response Kernel Router Request URI Controller & Action Front Controller

Slide 10

Slide 10 text

/ /services /blog Request Response Kernel Controller Router Request URI Controller & Action Front Controller

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

Router Chain 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 19

Slide 19 text

Sulu routing summarized

Slide 20

Slide 20 text

Sulu routing summarized – CMF ChainRouter replaces the default routing system

Slide 21

Slide 21 text

Sulu routing summarized – CMF ChainRouter replaces the default routing system – and works by accepting a set of prioritized Routers

Slide 22

Slide 22 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

Slide 23

Slide 23 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 24

Slide 24 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;
 }
 }

Slide 25

Slide 25 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 26

Slide 26 text

// app/config/website/routing.yml app:
 resource: "@AppBundle/Controller/"
 type: annotation
 prefix: /app

Slide 27

Slide 27 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'); } }

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

Slide 32

Slide 32 text

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 DefaultController

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 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 37

Slide 37 text

Response Format HTML, XML or JSON

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

// app/Resources/templates/pages/default.xml 
 
 
 default
 
 AppBundle:Custom:index
 AppBundle:Custom:index
 2400
 
 ... .html.twig

Slide 40

Slide 40 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 %} .html.twig

Slide 41

Slide 41 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 42

Slide 42 text

No content

Slide 43

Slide 43 text

HTTP Cache & ESI HTTP Standards FTW!

Slide 44

Slide 44 text

Reverse Proxy Caches

Slide 45

Slide 45 text

Reverse Proxy Caches – A HTTP Cache is a full page cache

Slide 46

Slide 46 text

Reverse Proxy Caches – A HTTP Cache is a full page cache – It bypasses your application entirely, if the cache entry is valid

Slide 47

Slide 47 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

Slide 48

Slide 48 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

Slide 49

Slide 49 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

Slide 50

Slide 50 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 51

Slide 51 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 52

Slide 52 text

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

Slide 53

Slide 53 text

ESI - Edge Side Includes

Slide 54

Slide 54 text

ESI - Edge Side Includes – The ESI specification describes tags to communicate with the gateway cache

Slide 55

Slide 55 text

ESI - Edge Side Includes – The ESI specification describes tags to communicate with the gateway cache – In Symfony the is implemented

Slide 56

Slide 56 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 57

Slide 57 text

// app/config/config.yml framework:
 ... esi: { enabled: true }

Slide 58

Slide 58 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 59

Slide 59 text

Model Customize what you need.

Slide 60

Slide 60 text

Customizing Models

Slide 61

Slide 61 text

Customizing Models – Doctrine doesn't support model customization

Slide 62

Slide 62 text

Customizing Models – Doctrine doesn't support model customization – Inheritance leads to multiple tables for the same data structure

Slide 63

Slide 63 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

Slide 64

Slide 64 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 65

Slide 65 text

// src/AppBundle/EntityTag.php

Slide 66

Slide 66 text

// src/AppBundle/EntityTag.php

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Event Dispatcher Handle additional business logic within your Event Subscriber.

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Symfony Events

Slide 71

Slide 71 text

Symfony Events Kernel Events – kernel.request – kernel.response – kernel.controller – kernel.view – kernel.terminate …

Slide 72

Slide 72 text

Symfony Events Kernel Events – kernel.request – kernel.response – kernel.controller – kernel.view – kernel.terminate … Doctrine Events

Slide 73

Slide 73 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 74

Slide 74 text

No content

Slide 75

Slide 75 text

Sulu Document Manager Events

Slide 76

Slide 76 text

Sulu Document Manager Events bin/adminconsole sulu:document:subscriber:debug

Slide 77

Slide 77 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 78

Slide 78 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 79

Slide 79 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 80

Slide 80 text

Service Container The control center for all you application.

Slide 81

Slide 81 text

Service Container

Slide 82

Slide 82 text

Service Container – Foundation for extensibility & customizability

Slide 83

Slide 83 text

Service Container – Foundation for extensibility & customizability – Sulu heavily uses service definitions

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 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 87

Slide 87 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));
 }
 }

Slide 88

Slide 88 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 89

Slide 89 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'));
 }
 }

Slide 90

Slide 90 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 91

Slide 91 text

Thanks for watching! www.sulu.io