Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This course is archived!
This tutorial is built using Drupal 8.0. The fundamental concepts of Drupal 8 - like services & routing - are still valid, but newer versions of Drupal *do* have major differences.

Event Arguments and the Request

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

We now know that when you listen to this event, it passes you an event object called GetResponseEvent. Type-hint the argument to enjoy auto-completion:

... lines 1 - 5
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
... lines 7 - 8
class DinoListener implements EventSubscriberInterface
{
public function onKernelRequest(GetResponseEvent $event)
{
... lines 13 - 18
}
... lines 20 - 27
}

Using the Event Argument

This event object has a getRequest() method, use that to set a new $requestvariable:

... lines 1 - 10
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
... lines 14 - 18
}
... lines 20 - 28

This is Drupal's - and Symfony's - Request object. If you want to read some GET params, POST params, headers or session stuff, this is your friend. You can of course get the Request object inside a controller.

Here's the goal: if the URL has a ?roar=1 on it, then I want to log a message. If not, we'll do nothing. Make a new variable called $shouldRoar. To access the GET, or query parameters on the request, use $request->query->get('roar'):

... lines 1 - 10
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
$shouldRoar = $request->query->get('roar');
... lines 15 - 18
}
... lines 20 - 28

If it's not there, this returns null:

Next, if ($shouldRoar), just var_dump('ROOOOAR') and die to test things:

... lines 1 - 10
public function onKernelRequest(GetResponseEvent $event)
{
... lines 13 - 15
if ($shouldRoar) {
var_dump('ROOOOOOOOAR');die;
}
}
... lines 20 - 28

Since we didn't touch any configuration, we can refresh without clearing anything.

Page not found! We're on the profiler page for a past request, and this information is stored in the cache... which we just cleared. So go find a real page. Ok, it works perfectly. Now add ?roar=1. It hits! And this will work on any page.

Dependency Inject All the Things

How can we log something? We faced this problem earlier when we wanted to use the keyvalue store inside RoarGenerator. We solved it with dependency injection: create a __construct() method, pass in what you need, and set it on a property. This is no different.

Add public function __construct(). Look for the logger in container:debug:

drupal container:debug | grep log

The logger.factory is an instance of LoggerChannelFactory. Type-hint using that. And like with other stuff, this has an interface, which is a trendier option. I'll hit option+enter to add the property and set it:

... lines 1 - 4
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
... lines 6 - 9
class DinoListener implements EventSubscriberInterface
{
private $loggerChannel;
public function __construct(LoggerChannelFactoryInterface $loggerChannel)
{
$this->loggerChannel = $loggerChannel;
}
... lines 18 - 36
}

Below, if you wanna roar, do it! $this->loggerFactory->get() - and use the default channel. Then add a debug message: 'Roar requested: ROOOOOAR':

... lines 1 - 9
class DinoListener implements EventSubscriberInterface
{
... lines 12 - 18
public function onKernelRequest(GetResponseEvent $event)
{
... lines 21 - 23
if ($shouldRoar) {
$channel = $this->loggerChannel->get('default')
->debug('ROOOOOOOOAR');
}
}
... lines 29 - 36
}

That's it guys! We didn't touch any config, so just refresh. Oh no, an error:

Argument 1 passed to DinoListener::__construct() must implement LoggerChannelFactoryInterface, none given.

Can you spot the problem? We saw this once before. We forgot to tell the container about the new argument. In dino_roar.services.yml, add @logger.factory:

... lines 1 - 3
services:
... lines 5 - 10
dino_roar.dino_listener:
... line 12
arguments: ['@logger.factory']
... lines 14 - 16

This is a single-line YAML-equivalent to what I did for the other service.

Rebuild the cache:

drupal cache:rebuild

Refresh. No error! Head into "Reports" and then "Recent Log Messages". There's the "Roar Requested".

You're now an event listener pro. We listened to the kernel.reqeust event, but you can add a listener to any event that Drupal or a third-party module exposes.

Leave a comment!

10
Login or Register to join the conversation
Default user avatar
Default user avatar Muhammad Al Maudoodi | posted 5 years ago

Hello weaverryan,

It's an interesting topic here, and thank you for explaining this dangerous skills in event subscribe thingy.
I have 1 question, which I tried myself but I am still blur on this.

I have a webform, which I want to subscribe the even when any webform submission edited (as approved) by me.
I don't know which exact listener to put actually. I tried with

KernelEvents::FINISH_REQUEST

I can see the params to manipulate, in the dump, but when I tried to use it, it returns null as cannot access it in the class.

Well, the strugle is quite overkill now, May I know if there any easier to workout with putting subscriber for specific event like webform update or node update?

Or do we have to use hook instead..

Reply

Hey Muhammad Al Maudoodi

Maybe this section about the request lifecycle can help you out figuring which event fits better to your needs.
If you still have more questions feel free to message us :)

Cheers!

Reply
Default user avatar
Default user avatar Muhammad Al Maudoodi | MolloKhan | posted 5 years ago

Hi Diego and KNPU Team. Thank you for the reply.

At first I was confused about some particular event thingy, later I tried out a module https://www.drupal.org/proj... and it has a specific event to use easily, in my case for entity update.

So intstead of using Kernel Event, I use this and it works! But anyway it still a great tutorial that I learned to subscribe event via this course, and then I realized I would like to dig more on the event options, without this course I don't event know what I missing.

Another thing is I was mistakenly grab the $event parameters directly, when it got layers of properties. So I learned that for each $event type that returned, it showing up in the var_dump or etc, but when calling it's sub param, I cannot call directly as it will return null. Each sub $event has other $object must use the it's ObjectClass methods to call and it will return the value as it should.

I am enjoying learning this, I did replay lots of time on the vids. It is simple but lots of dangerous tips along the tutorial, that brings it a very interesting part.

Reply

Hey Muhammad Al Maudoodi

I'm happy to hear that you are loving our tutorials, and that you are actually improving your programming skills.
If you have more questions or feedback, feel free to contact us again :)

Cheers!

Reply
Default user avatar
Default user avatar Habibun Noby | posted 5 years ago | edited

I get same error message:
TypeError: Argument 1 passed to Drupal\dino_roar\Jurassic\DinoListener::__construct() must implement interface Drupal\Core\Logger\LoggerChannelFactoryInterface, array given


  dino_roar.dino_listener:
    class: Drupal\dino_roar\Jurassic\DinoListener
    arguments: ['@logger.factory']
    tags:
      - { name: event_subscriber }

myController:


    public function __construct(LoggerChannelFactoryInterface $loggerChannelFactory)
    {
        $this->loggerChannelFactory = $loggerChannelFactory;
    }
Reply

Hey Habibun Noby!

Hmm, interesting! So, your __construct() function (I assume this is from your DinoListener class?) and your services YAML code looks perfect to me: Drupal should be passing the LoggerChannelFactory into your DinoListener. So, let's debug!

1) Have you definitely cleared all your caches? Of course, I always need to ask this first :).
2) Do you have a full stack trace that you can take a screenshot of? I'm curious to see if possibly someone else is instantiating the DinoListener class, which is causing the confusion.

But definitely, you're close - I see no issues at all with the code you have here.

Cheers!

Reply
Default user avatar

hey weaverryan,

i used drupal console for clearing cache.but some how it's not worked.So after that i manually delete all files under sites/default/files/* and rebuild again. now it's working....i don't know may be there is a issue with drupal console.

your mentioned debug tricks help me to find out the problem....

thanks

Reply

Very happy it worked in the end - I thought your code looked perfect! :)

Reply
Default user avatar
Default user avatar joeyr33 | weaverryan | posted 5 years ago | edited

I think it's an issue with drupal console. If I try to load a page after adding the __constructor argument, but before doing drupal cache:rebuild then the the error weaverryan mentioned starts occuring. In fact, once that error gets thrown on page load it also prevents drupal cache:rebuild from running, but drush cr; works.

Reply

That's totally possible - it's one of the tricky things about Drupal's container cache, which has bitten me a few times. With Drupal console, I believe it actually uses your Drupal container in order to do things. If your container is in a "broken" state, then you might not be able to rebuild it with drupal console. I don't know the specifics about this - I'm sure it's something they're aware of and there might be proper workarounds (though Drush is itself a very proper workaround!)

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial is built using Drupal 8.0. The fundamental concepts of Drupal 8 - like services & routing - are still valid, but newer versions of Drupal *do* have major differences.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "composer/installers": "^1.0.21", // v1.0.21
        "wikimedia/composer-merge-plugin": "^1.3.0" // dev-master
    }
}
userVoice