A comedic look into Drupal 8
Presented at DrupalCamp Toronto 2013 (2013-07-13). By @mparker17 and @emarchak
Rather than creating objects in our own code, we ask a DI Container to create them for us and hand us the finished project. Drupal 8 core’s main DI container is named ContainerBuilder.
Modules can ask ContainerBuilder to substitute certain objects with better ones. (This is analogous to the way the “Taxonomy node” module in D6 substituted nodes for taxonomy terms in a way transparent to the user.)
Also, it’s easier to test because you can use mock objects — lightweight objects that have the same interface as other objects, but don’t do anything.
The key to this is to use Interfaces instead of specific classes (as much as you can) when writing your code. Instead of referring to specific classes, refer to the interface or interfaces that class has.
Drupal uses DI mainly for services (e.g.: database connection, module handler). If your object provides a service, you can register it with ContainerBuilder and give it a Service ID. When you need to use an service, you ask ContainerBuilder to get you an object with that Service ID, and it chooses the best one.
Alternately, using specially-formatted comments in your objects, you can get ContainerBuilder to create objects and pass them into the constructor or setter functions.
- Kat Bailey's talk on DI in D8 at DrupalCon Portland 2013
- \Symfony\Component\DependencyInjection\ContainerBuilder
The new hook_menu(). Actually, technically just the part of hook_menu() that runs code when a certain path is requested.
Mainly because we wanted to create routes on more than just the path (e.g.: have one function generate HTML and another generate XML and JSON).
Simplifies things a bit too, because hook_menu() handled a few related but different features.
You put all your page callback functions into one or more Controller objects instead of the global namespace.
Then, you map paths to their corresponding functions in your module’s .routing.yml file, along with information about what the function returns (a form, HTML, JSON, etc.), and what permissions a user needs to see it.
When a request for a page comes in, Drupal’s new Kernel:
- Figures out what route to call based on it’s list.
- Checks permissions.
- Creates the Controller object (our old friend ContainerBuilder will pass in any helper objects it needs).
- Calls the specified function and gets it’s output.
- Based on the information about what the function returns, the Kernel gives that output to the appropriate handler to do any final preparations before sending it to the browser.
drupal_render_page()
- initial call back for page assembly. We still have access to
hook_page_alter
:(
- initial call back for page assembly. We still have access to
drupal_render()
- assembles overall page.
theme()
- if the element has a #theme_wrapper declared. If the item is configured to be cached, this is after this point where it is cached.
- this is where it checks for all theme hook suggestions (
preprocess_
ortheme_
functions)
twig_render_template
- loads the twig template files renders twig blocks and return the output as a string, if debug setting is enabled returns template suggestions in html comments.
- passes all children through
render()
function, which is a wrapper fordrupal_render()
. twig_render_var()
handles all parsing of twig variables
Twig_Template
- is the object that assembles all twig blocks and renders them out.
- this can be extended to include more functionality for twig as needed.
- located in twig.engine, is a wrapper for
render()
function. This allows objects to be casted to strings, and arrays to be passed throughrender()
so we no longer have inconsistent types within the tpl files.
- there are still both theme and preprocess functions, we haven't stripped them both out of core (that was a goal of the twig initiative, they're still there)
theme_
functions still assemble render arrays, and the return the array passed throughdrupal_render()
. We're not totally free from the render arrays of doom, but they have been pushed further back into the development "stack".- the
drupal_render()
function itself appears to remain the same from D7 to D8.
- the
hide()
andshow()
functions still work as expected, but are now called using twig syntax{% hide(content.links) %}
- there is two way communication between the template files and the admin UI because D8 is able to parse the .html.twig files and assemble options for the content admin.
- twig sanitizes variables that it prints out, there is no need for additional
check_plain()
. - it can also modify variables using the pipe (
|
), so it introduces filters for things such as capitalization, json encoding/decoding, conversion of new lines to html line breaks, url encoding and more! for
/if
/else
control loops work as expected, nothing insane. You can still loop through elements as we have before.
- you can include additional templates using the
{% include 'sidebar.html' %}
syntax, and they inherit context passed to them such as context included from a for loop. - you can override certain block elements within a global template. You can define Twig blocks that can be overridden in child templates.
- for example, you can define a sidebar block as
{% block sidebar %} … {% endblock %}
in a global page template. - To override that block in a specific node-page template, you need to include
{% extends "page.twig.html" %}
in your template and access the blocks using the same markup of{% block sidebar %} … {% endblock %}
- for example, you can define a sidebar block as
- Kim Pepper's article on Using Drupal 8's New Route Controllers
- Many Drupal 8 core files.
- Larry Garfield's talk on Drupal8 at Symfony Live Portland 2013.