If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeHere's the challenge: add a function that will be called on every request, right at the beginning, before any controller is called. This means we should hook into the kernel.request
event.
In the Jurassic
directory - well, it doesn't matter where - add a new DinoListener
class. Next, make this class implement EventSubscriberInterface
:
namespace Drupal\dino_roar\Jurassic; | |
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
... lines 6 - 7 | |
class DinoListener implements EventSubscriberInterface | |
{ | |
... lines 10 - 21 | |
} |
If you're not super cool with interfaces yet, hit pause and go practice with them in the Object Oriented Level 3 course.
This interface forces us to have 1 method: getSubcribedEvents()
. In PhpStorm, hit command+n
to bring up the Generate menu, select "Implement Methods", and highlight this. That's a nice shortcut to add the one method we need:
... lines 1 - 7 | |
class DinoListener implements EventSubscriberInterface | |
{ | |
... lines 10 - 14 | |
public static function getSubscribedEvents() | |
{ | |
... lines 17 - 19 | |
} | |
} |
We need to tell Drupal which event we want to listen to and what method to call when that event happens. This method returns an array that says exactly that. Use KernelEvents::REQUEST
as the key and hit tab
to auto-complete and get the use
statement. But hold on, that is just a constant that means kernel.request
. You can totally use the string kernel.request
if you want to. The value is the method to call. Use, onKernelRequest()
:
... lines 1 - 5 | |
use Symfony\Component\HttpKernel\KernelEvents; | |
... line 7 | |
class DinoListener implements EventSubscriberInterface | |
{ | |
... lines 10 - 14 | |
public static function getSubscribedEvents() | |
{ | |
return [ | |
KernelEvents::REQUEST => 'onKernelRequest', | |
]; | |
} | |
} |
Up top, create that method: public function onKernelRequest()
. Every event listener is passed exactly one argument: an event object:
... lines 1 - 7 | |
class DinoListener implements EventSubscriberInterface | |
{ | |
public function onKernelRequest($event) | |
{ | |
... line 12 | |
} | |
... lines 14 - 21 | |
} |
The cool thing is that this object will hold any information your function will need. The tricky thing is that this is a different object depending on which event you're listening to.
No worries, let's just var_dump()
it and see what it is!
... lines 1 - 9 | |
public function onKernelRequest($event) | |
{ | |
var_dump($event);die; | |
} | |
... lines 14 - 22 |
Ok, this class is setup. The last step is to tell Drupal we have an event subscriber. How do you do that? Register this class as a service. Get used to that answer.
In dino_roar.services.yml
, add a new service - the name doesn't matter. Set the class to the full namespace. In this case, there are no constructor arguments. Add an arguments
key, but leave it blank with square brackets:
... lines 1 - 3 | |
services: | |
... lines 5 - 10 | |
dino_roar.dino_listener: | |
class: Drupal\dino_roar\Jurassic\DinoListener | |
arguments: [] | |
... lines 14 - 16 |
Congratulations! You've created a normal, boring service... but Drupal still doesn't know it's an event subscriber. Somehow, we need to tell Drupal:
Yo, this is not a normal service, this is an event subscriber! I want you to call the
getSubscribedEvents()
method so you know about mykernel.request
listener!
Whenever you want to raise your hand and scream "Drupal, this service is special, use it for this core purpose", you're going to use a tag. The syntax for a tag is ugly, but here it goes. Add tags
, add a new line, indent, add a dash, then a set of curly braces. Every tag has a name: this one is event_subscriber
:
... lines 1 - 3 | |
services: | |
... lines 5 - 10 | |
dino_roar.dino_listener: | |
class: Drupal\dino_roar\Jurassic\DinoListener | |
arguments: [] | |
tags: | |
- { name: event_subscriber } |
By doing this, you've now told Drupal's core that our DynoListener
service is an event subscriber. It knows to go in and find out what events we're subscribed to.
***seealso Tags work via a system called "Compiler Passes". These are shared with Symfony, and we talk a lot more about them here: Compiler Pass and Tags.
There are other tags that for other things. For example, if you want to add some custom functions to Twig, you'll create a class that extends Twig_Extension
, register it as a service, and tag it with twig.extension
. This tells Twig, "Yo, I have a Twig Extension here - use it!".
If you're using tags, then you're probably doing something geeky-cool, hooking into some core part of the system. And you don't need to know about all the tags. You'll Google "how do I register an event subscriber" and see that it uses a tag called event_subscriber
. You just need to understand how they work: that it's your way of making your service special.
Ok, back to the action. Rebuild the cache:
drupal cache:rebuild
Since the kernel.request
event happens on every request, we should be able to refresh and see the var_dump()
. Great Scott! There it is! Now, let's do something in this!
Not sure if it's something odd on my end but, fwiw, reloading the url of the webprofiler resulted in an error:
TypeError: Return value of
Drupal\webprofiler\Profiler\DatabaseProfilerStorage::read() must be an
instance of Symfony\Component\HttpKernel\Profiler\Profile or null, none
returned in Drupal\webprofiler\Profiler\DatabaseProfilerStorage->read() (line 92 of sites/all/modules/devel/webprofiler/src/Profiler/DatabaseProfilerStorage.php).
Perhaps that's due to some change since the video was created? If so, it would be nice if someone pointed that out.
I was able to see the results of the var_dump on the index page though.
Hey Aaron L.!
Hmm... this looks like a minor bug to me in devel/webprofiler!. Since Symfony 4.4, that "read()" method from the DatabaseProfilerStorage has a return type that says it should return "null" or a Profiler object.... but in the webprofiler module, it is returning "nothing" (no return statement). But, checking out the latest devel code, it is indeed possible for that method to return "nothing" - if the profiler token wasn't found in the database - https://gitlab.com/drupalfo... - which I believe would happen after you cleared cache. It's harmless, but annoying - they should return "null" in this situation.
I hope that explains at least!
Cheers!
Thanks for the reply! Not sure if it's you in the video or not but in regards to learning the new ways of doing things in Drupal, these videos (and the symfony ones as well) have been the most informative of all of the things I've been looking at. The fact that this video is so old and you've taken the time to reply is awesome as well so thanks for that!!
It's all starting to make sense now!
Hi Aaron L.!
Ah... thank you for the really nice words! Yea... this tutorial is (unfortunately) quite old now... but I'm absolutely thrilled that it can still be useful - I really love this topic :). Keep up the good work!
Cheers!
// composer.json
{
"require": {
"composer/installers": "^1.0.21", // v1.0.21
"wikimedia/composer-merge-plugin": "^1.3.0" // dev-master
}
}
its worth noting that its an amazingly good idea to install the drupal symfony bridge plugin into phpstorm, and then enable the symfony toolbar.
look here for all the goodies.
https://confluence.jetbrain...