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

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.

weaverryan

May 13, 2015
Tweet

More Decks by weaverryan

Other Decks in Technology

Transcript

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

    View Slide

  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!!!

    View Slide

  3. Act 1
    I feel like making a page!
    @weaverryan

    View Slide

  4. Module
    Route
    Controller
    @weaverryan
    !
    !
    !

    View Slide

  5. @weaverryan
    name: Dino ROAR

    type: module

    description: "ROOOOOAR AT YOU!"

    package: Sample

    core: 8.x

    View Slide

  6. @weaverryan
    drush pm-enable dino_roar

    View Slide

  7. Module
    Route
    Controller
    @weaverryan
    !
    !
    "

    View Slide

  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???

    View Slide

  9. Module
    Route
    Controller
    @weaverryan
    !
    "
    "

    View Slide

  10. @weaverryan

    namespace Drupal\dino_roar\Controller;


    use Symfony\Component\HttpFoundation\Response;


    class RoarController

    {

    public function roar()

    {

    return new Response('ROOOOAR');

    }

    }

    View Slide

  11. Module
    Route
    Controller
    @weaverryan
    "
    "
    "

    View Slide

  12. @weaverryan

    View Slide

  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

    View Slide

  14. @weaverryan
    dino_says:

    path: /the/dino/says/{count}

    defaults:

    _controller: Drupal\dino_roar\Controller\RoarController::roar

    requirements:

    _permission: 'access content'

    View Slide

  15. @weaverryan
    class RoarController

    {

    public function roar($count)

    {

    $roar = 'R'.str_repeat('O', $count).'AR';

    return new Response($roar);

    }

    }

    View Slide

  16. Act 2
    Our Debugging Arsenal
    @weaverryan

    View Slide

  17. Every page has
    a route &
    controller
    @weaverryan

    View Slide

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

    View Slide

  19. View Slide

  20. @weaverryan

    View Slide

  21. @weaverryan

    View Slide

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

    View Slide

  23. Say THANKS!

    View Slide

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

    View Slide

  25. @weaverryan

    View Slide

  26. View Slide

  27. View Slide

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

    View Slide

  29. View Slide

  30. View Slide

  31. View Slide

  32. View Slide

  33. How does the
    /admin/structure
    page work?
    @weaverryan

    View Slide

  34. View Slide

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

    View Slide

  36. Services == Useful Objects
    @weaverryan

    View Slide

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

    View Slide

  38. In Drupal 8
    The container is pre-loaded
    with many useful services
    (objects)

    View Slide

  39. … a short list of 485 …

    View Slide

  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%']

    View Slide

  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

    View Slide

  42. Act 4
    Events
    @weaverryan

    View Slide

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

    View Slide

  44. “Hi! When event XXXXX
    happens, execute this
    function. kthxbai”
    YOU CAN TELL D8
    @weaverryan

    View Slide

  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!

    View Slide

  46. View Slide

  47. Act 5
    Request attributes
    @weaverryan

    View Slide

  48. @weaverryan

    View Slide

  49. @weaverryan

    View Slide

  50. @weaverryan
    dino_says:

    path: /the/dino/says/{count}

    defaults:

    _controller: Drupal\dino_roar\Controller\RoarController::roar

    requirements:

    _permission: 'access content'

    View Slide

  51. @weaverryan
    class RoarController

    {

    public function roar($count)

    {

    $roar = 'R'.str_repeat('O', $count).'AR';

    return new Response($roar);

    }

    }

    View Slide

  52. @weaverryan
    class RoarController

    {

    public function roar($_route, $_access_result)

    {

    $roar = 'R'.str_repeat('O', $count).'AR';

    return new Response($roar);

    }

    }

    View Slide

  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);

    }

    }

    View Slide

  54. @weaverryan

    View Slide

  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!

    View Slide

  56. Act 6
    Up-casting nodes
    @weaverryan

    View Slide

  57. @weaverryan
    alternate_node_view:

    path: /the/dino/{node}

    defaults:

    _controller: Drupal\dino_roar\Controller\RoarController::node

    requirements:

    _permission: 'access content'

    View Slide

  58. @weaverryan
    class RoarController

    {

    public function node($node)

    {

    return new Response('The node is '.$node);

    }

    }
    #

    View Slide

  59. @weaverryan

    View Slide

  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

    View Slide

  61. @weaverryan
    public function node($node)

    {

    return new Response(‘node '.$node->getTitle());

    }

    View Slide

  62. @weaverryan

    View Slide

  63. paramconverter
    @weaverryan

    View Slide

  64. @weaverryan
    services:

    paramconverter.entity:

    class: Drupal\Core\ParamConverter\EntityConverter

    tags:

    - { name: paramconverter }

    arguments: ['@entity.manager']

    View Slide

  65. @weaverryan
    interface ParamConverterInterface {


    public function convert($value, $definition, $name, array $defaults);


    public function applies($definition, $name, Route $route);


    }

    View Slide

  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

    View Slide

  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!

    View Slide

  68. @weaverryan
    alternate_node_view:

    path: /the/dino/{node}

    defaults:

    _controller: Drupal\dino_roar\Controller\RoarController::node

    requirements:

    _permission: 'access content'

    View Slide

  69. @weaverryan
    class RoarController

    {

    public function node(EntityInterface $node)

    {

    return new Response('The node '.$node->getTitle());

    }

    }

    View Slide

  70. Act 7
    The render array
    @weaverryan

    View Slide

  71. @weaverryan
    class RoarController

    {

    public function node(Node $node)

    {

    return [

    '#title' => $node->getTitle()

    ];

    }

    }

    View Slide

  72. @weaverryan
    THIS SHOULD NOT WORK…

    View Slide

  73. @weaverryan
    Who created the Response?

    View Slide

  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

    View Slide

  75. @weaverryan
    the kernel.view event
    MainContentViewSubscriber

    View Slide

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

    View Slide

  77. @weaverryan
    services:

    main_content_renderer.html:

    class: Drupal\Core\Render\MainContent\HtmlRenderer

    arguments: ['@title_resolver', '...' ]

    tags:

    - { name: render.main_content_renderer, format: html }

    View Slide

  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;

    }

    }

    View Slide

  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!

    View Slide

  80. Act 8
    Question Everything
    @weaverryan

    View Slide

  81. @weaverryan
    some_route:

    path: /building/a/page

    defaults:

    _form: Drupal\dino_roar\Form\SomeForm

    requirements:

    _permission: 'access content'
    Where’s my _controller?

    View Slide

  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

    View Slide

  83. DIG DEEP
    You have the tools
    @weaverryan

    View Slide

  84. View Slide

  85. View Slide

  86. WEBPROFILER: EVENTS

    View Slide

  87. WEBPROFILER: TIMELINE

    View Slide

  88. WEBPROFILER: SERVICES

    View Slide

  89. @weaverryan
    WEBPROFILER: REQUEST

    View Slide

  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%']

    View Slide

  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!

    View Slide

  92. Ryan Weaver
    @weaverryan
    THANK YOU!
    http://DrupalConsole.org
    http://bit.ly/drupal-webprofiler
    @jmolivas @lussoluca

    View Slide