DrupalCon 2015: Routes, controllers and responses: The Basic Lifecycle of a D8 Request

DrupalCon 2015: Routes, controllers and responses: The Basic Lifecycle of a D8 Request

Whether your viewing a node on the frontend, consuming a JSON endpoint or using the admin interface, every request in D8 follows the same workflow: routing, a controller and a response. Woh!

In this talk, we'll uncover the basic lifecycle of a D8 request, by creating a custom page using routes, a controller and a response. And you'll see just how simple life is. Once we're there, we'll dive deeper to see how Drupal uses this *exact* same stuff to fuel node pages, the admin interface and everything else. If you want to be able to create custom pages or open up the hood and really "get" D8, this is your first step.

F5dfeeef276fcfd4751f4063487a5a3f?s=128

weaverryan

May 13, 2015
Tweet

Transcript

  1. routes, controllers and responses: THE BASIC LIFECYCLE OF A D8

    REQUEST
  2. > Husband of the much more talented @leannapelham knplabs.com twitter.com/weaverryan

    Hallo! > Lead contributor to the Symfony documentation
 > KnpLabs US - Symfony consulting, training & kumbaya > Writer for KnpUniversity.com awesome amazing PHP Tutorials!!!
  3. Act 1 I feel like making a page! @weaverryan

  4. Module Route Controller @weaverryan ! ! !

  5. @weaverryan name: Dino ROAR
 type: module
 description: "ROOOOOAR AT YOU!"


    package: Sample
 core: 8.x
  6. @weaverryan drush pm-enable dino_roar

  7. Module Route Controller @weaverryan ! ! "

  8. @weaverryan dino_says:
 path: /the/dino/says
 defaults:
 _controller: Drupal\dino_roar\Controller\RoarController::roar
 requirements:
 _permission: 'access

    content' hook_menu anyone? page callback???
  9. Module Route Controller @weaverryan ! " "

  10. @weaverryan <?php
 
 namespace Drupal\dino_roar\Controller;
 
 use Symfony\Component\HttpFoundation\Response;
 
 class

    RoarController
 {
 public function roar()
 {
 return new Response('ROOOOAR');
 }
 }
  11. Module Route Controller @weaverryan " " "

  12. @weaverryan

  13. Request -> Response Framework Response: ROOOAR! Routing: Determine a function

    that can create this page (the controller) Request: GET /the/dino/says The Controller: Our code: constructs the page @weaverryan
  14. @weaverryan dino_says:
 path: /the/dino/says/{count}
 defaults:
 _controller: Drupal\dino_roar\Controller\RoarController::roar
 requirements:
 _permission: 'access

    content'
  15. @weaverryan class RoarController
 {
 public function roar($count)
 {
 $roar =

    'R'.str_repeat('O', $count).'AR';
 return new Response($roar);
 }
 }
  16. Act 2 Our Debugging Arsenal @weaverryan

  17. Every page has a route & controller @weaverryan

  18. Can we get a list of all routes? @weaverryan

  19. None
  20. @weaverryan

  21. @weaverryan

  22. Can I get this right on the web? @weaverryan

  23. Say THANKS!

  24. Install that module! download 2 JS files Download it! @weaverryan

  25. @weaverryan

  26. None
  27. None
  28. For a good time, click all the pretty checkboxes

  29. None
  30. None
  31. None
  32. None
  33. How does the /admin/structure page work? @weaverryan

  34. None
  35. Act 3 Services and the “container” @weaverryan

  36. Services == Useful Objects @weaverryan

  37. The container == the object that contains all the services

    @weaverryan
  38. In Drupal 8 The container is pre-loaded with many useful

    services (objects)
  39. … a short list of 485 …

  40. @weaverryan parameters:
 factory.keyvalue:
 default: keyvalue.database
 services:
 state:
 class: Drupal\Core\State\State
 arguments:

    ['@keyvalue']
 keyvalue:
 class: Drupal\Core\KeyValueStore\KeyValueFactory
 arguments: ['@service_container', '%factory.keyvalue%']
  41. Request -> Response Framework The Controller: Our code: constructs the

    page Response: Hello Drupal! Container (with services) Routing: Determine a function that can create this page (the controller) Request: GET /hello/Drupal! @weaverryan
  42. Act 4 Events @weaverryan

  43. Just like “hooks”, D8 has events @weaverryan

  44. “Hi! When event XXXXX happens, execute this function. kthxbai” YOU

    CAN TELL D8 @weaverryan
  45. EVENTS kernel.view kernel.response Request -> Response Framework The Controller: Our

    code: constructs the page Container (with services) EVENT kernel.controller Response: Hello Drupal! Routing: Determine a function that can create this page (the controller) EVENT kernel.request @weaverryan Request: GET /hello/Drupal!
  46. None
  47. Act 5 Request attributes @weaverryan

  48. @weaverryan

  49. @weaverryan

  50. @weaverryan dino_says:
 path: /the/dino/says/{count}
 defaults:
 _controller: Drupal\dino_roar\Controller\RoarController::roar
 requirements:
 _permission: 'access

    content'
  51. @weaverryan class RoarController
 {
 public function roar($count)
 {
 $roar =

    'R'.str_repeat('O', $count).'AR';
 return new Response($roar);
 }
 }
  52. @weaverryan class RoarController
 {
 public function roar($_route, $_access_result)
 {
 $roar

    = 'R'.str_repeat('O', $count).'AR';
 return new Response($roar);
 }
 }
  53. @weaverryan use Symfony\Component\HttpFoundation\Request;
 
 class RoarController
 {
 public function roar(Request

    $request)
 {
 $count = $request->attributes->get('count'); 
 $roar = 'R'.str_repeat('O', $count).'AR';
 return new Response($roar);
 }
 }
  54. @weaverryan

  55. EVENTS kernel.view kernel.response Request -> Response Framework The Controller: Our

    code: constructs the page Container (with services) EVENT kernel.controller Response: Hello Drupal! Routing: Determine a function that can create this page (the controller) EVENT kernel.request @weaverryan Arguments $request->attributes Request: GET /hello/Drupal!
  56. Act 6 Up-casting nodes @weaverryan

  57. @weaverryan alternate_node_view:
 path: /the/dino/{node}
 defaults:
 _controller: Drupal\dino_roar\Controller\RoarController::node
 requirements:
 _permission: 'access

    content'
  58. @weaverryan class RoarController
 {
 public function node($node)
 {
 return new

    Response('The node is '.$node);
 }
 } #
  59. @weaverryan

  60. @weaverryan alternate_node_view:
 path: /the/dino/{node}
 defaults:
 _controller: Drupal\dino_roar\Controller\RoarController::node
 requirements:
 _permission: 'access

    content'
 options:
 parameters:
 node:
 type: entity:node
  61. @weaverryan public function node($node)
 {
 return new Response(‘node '.$node->getTitle());
 }

  62. @weaverryan

  63. paramconverter @weaverryan

  64. @weaverryan services:
 paramconverter.entity:
 class: Drupal\Core\ParamConverter\EntityConverter
 tags:
 - { name: paramconverter

    }
 arguments: ['@entity.manager']
  65. @weaverryan interface ParamConverterInterface {
 
 public function convert($value, $definition, $name,

    array $defaults);
 
 public function applies($definition, $name, Route $route);
 
 }
  66. @weaverryan interface ParamConverterInterface {
 
 public function convert($value, $definition, $name,

    array $defaults);
 
 public function applies($definition, $name, Route $route);
 
 } alternate_node_view:
 path: /the/dino/{node}
 defaults:
 _controller: Drupal\dino_roar\Controller\RoarController::node
 requirements:
 _permission: 'access content'
 options:
 parameters:
 node:
 type: entity:node
  67. EVENTS kernel.view kernel.response Request -> Response Framework The Controller: Our

    code: constructs the page Container (with services) EVENT kernel.controller Response: Hello Drupal! Routing: Determine a function that can create this page (the controller) EVENT kernel.request @weaverryan Arguments $request->attributes paramconverters Request: GET /hello/Drupal!
  68. @weaverryan alternate_node_view:
 path: /the/dino/{node}
 defaults:
 _controller: Drupal\dino_roar\Controller\RoarController::node
 requirements:
 _permission: 'access

    content'
  69. @weaverryan class RoarController
 {
 public function node(EntityInterface $node)
 {
 return

    new Response('The node '.$node->getTitle());
 }
 }
  70. Act 7 The render array @weaverryan

  71. @weaverryan class RoarController
 {
 public function node(Node $node)
 {
 return

    [
 '#title' => $node->getTitle()
 ];
 }
 }
  72. @weaverryan THIS SHOULD NOT WORK…

  73. @weaverryan Who created the Response?

  74. EVENTS kernel.view kernel.response Request -> Response Framework The Controller: Our

    code: constructs the page Container (with services) EVENT kernel.controller Response: Hello Drupal! Routing: Determine a function that can create this page (the controller) Request: GET /hello/Drupal! EVENT kernel.request @weaverryan Arguments $request->attributes paramconverters
  75. @weaverryan the kernel.view event MainContentViewSubscriber

  76. render.main_content_renderer @weaverryan Turn my “array” into a Response for some

    format
  77. @weaverryan services:
 main_content_renderer.html:
 class: Drupal\Core\Render\MainContent\HtmlRenderer
 arguments: ['@title_resolver', '...' ]
 tags:


    - { name: render.main_content_renderer, format: html }
  78. class HtmlRenderer implements MainContentRendererInterface { public function renderResponse(array $main_content, Reques

    // ...
 $this->buildPageTopAndBottom($html);
 
 // ...
 $content = $this->renderer->render($html);
 
 $response = new CacheableResponse($content, 200,[
 'Content-Type' => 'text/html; charset=UTF-8',
 ]);
 
 return $response;
 }
 }
  79. EVENTS kernel.view kernel.response Request -> Response Framework The Controller: Our

    code: constructs the page Container (with services) EVENT kernel.controller Response: Hello Drupal! Routing: Determine a function that can create this page (the controller) EVENT kernel.request @weaverryan Arguments $request->attributes paramconverters “content renderers” Request: GET /hello/Drupal!
  80. Act 8 Question Everything @weaverryan

  81. @weaverryan some_route:
 path: /building/a/page
 defaults:
 _form: Drupal\dino_roar\Form\SomeForm
 requirements:
 _permission: 'access

    content' Where’s my _controller?
  82. @weaverryan class ContentControllerSubscriber implements EventSubscr public function onRequestDeriveFormWrapper(GetResponse $request =

    $event->getRequest();
 
 if ($request->attributes->has('_form')) {
 $request->attributes->set(
 '_controller',
 'controller.form:getContentResult'
 );
 }
 }
 } $ Oh, it’s in this listener
  83. DIG DEEP You have the tools @weaverryan

  84. None
  85. None
  86. WEBPROFILER: EVENTS

  87. WEBPROFILER: TIMELINE

  88. WEBPROFILER: SERVICES

  89. @weaverryan WEBPROFILER: REQUEST

  90. @weaverryan parameters:
 factory.keyvalue:
 default: keyvalue.database
 services:
 state:
 class: Drupal\Core\State\State
 arguments:

    ['@keyvalue']
 keyvalue:
 class: Drupal\Core\KeyValueStore\KeyValueFactory
 arguments: ['@service_container', '%factory.keyvalue%']
  91. EVENTS kernel.view kernel.response Request -> Response Framework The Controller: Our

    code: constructs the page Container (with services) EVENT kernel.controller Response: Hello Drupal! Routing: Determine a function that can create this page (the controller) EVENT kernel.request @weaverryan Arguments $request->attributes paramconverters “content renderers” Request: GET /hello/Drupal!
  92. Ryan Weaver @weaverryan THANK YOU! http://DrupalConsole.org http://bit.ly/drupal-webprofiler @jmolivas @lussoluca