In some situations you might want to change a request before it's send out from a Guzzle client, or change the response that comes back from a Guzzle client.
This bundle allows you to do just that using Symfony event dispatch component.
Let's assume you've configured the following clients in your application:
eight_points_guzzle:
clients:
payment:
base_url: 'http://api.payment.example'
crm:
base_url: 'http://api.crm.tld'And suppose that payment requires authorization using some token in the request header.
How can we do that?
First of all we have to write an event listener, that will see all requests from our payment
client, and can modify them.
namespace App\EventListener;
use EightPoints\Bundle\GuzzleBundle\Events\PreTransactionEvent;
class PaymentApiGuzzleEventListener
{
/**
* @param PreTransactionEvent $event
*/
public function onPreTransaction(PreTransactionEvent $event)
{
// get request from the event
$request = $event->getTransaction();
// setup new header to request
$modifiedRequest = $request->withHeader('Authorization', 'Bearer longLongLongToken');
// replace request in event
$event->setTransaction($modifiedRequest);
}
}It's important to note that Response is immutable. As such, withHeader method does not change the request
by reference, but rather clones it, changes it and then returns it.
Now, just writing this class won't make it fire for every request on the payment client.
For that we need to register it in the Symfony configuration as an event listener, as follows:
services:
App\EventListener\PartnersApiGuzzleEventListener:
class: App\EventListener\PaymentApiGuzzleEventListener
tags:
- { name: kernel.event_listener, event: eight_points_guzzle.pre_transaction.payment, method: onPreTransaction }Because this listener listens for the event eight_points_guzzle.pre_transaction.payment it will only receive
events regarding the payment client, no other clients. If you want a listener that receives events for all clients,
subscribe to the eight_points_guzzle.pre_transaction event instead. This can be useful for logging, auditing, etc.
Note that if a generic listener and a client specific listener both change a request in the same way (for example, both add the same header), the value from the client specific listener overrides the value from the generic listener.
In previous step we intercepted the request and changed it, but we want to track the response too.
For example we can invalidate token if the payment API rejected it.
Let's subscribe our service to one more event:
services:
App\EventListener\PartnersApiGuzzleEventListener:
class: App\EventListener\PaymentApiGuzzleEventListener
tags:
- { name: kernel.event_listener, event: eight_points_guzzle.pre_transaction.payment, method: onPreTransaction }
- { name: kernel.event_listener, event: eight_points_guzzle.post_transaction.payment, method: onPostTransaction }Again, because this listener listens for the event eight_points_guzzle.post_transaction.payment it will only receive
events regarding the payment client, no other clients. Just like with pre transaction events, if you want a listener that
receives events for all clients, subscribe to the eight_points_guzzle.post_transaction event instead.
This can be useful for logging, auditing, etc.
Note that if a generic listener and a client specific listener both change a response in the same way (for example, both add the same header), the value from the client specific listener overrides the value from the generic listener.
Now we can implement the onPostTransaction method on our service:
namespace App\EventListener;
use EightPoints\Bundle\GuzzleBundle\Events\PostTransactionEvent;
class PaymentApiGuzzleEventListener
{
// ...
/**
* @param PostTransactionEvent $event
*/
public function onPostTransaction(PostTransactionEvent $event)
{
// get response from the event
$response = $event->getTransaction();
// check if response status code is 403
if ($response->getStatusCode() === Response::HTTP_FORBIDDEN) {
// invalidate token
}
}
}If you want to you can also use an event subscriber to the same as above
namespace App\EventSubscriber;
use EightPoints\Bundle\GuzzleBundle\Events\GuzzleEvents;
use EightPoints\Bundle\GuzzleBundle\Events\PreTransactionEvent;
use EightPoints\Bundle\GuzzleBundle\Events\PostTransactionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class PaymentApiGuzzleEventSubscriber implements EventSubscriberInterface
{
/**
* @param PreTransactionEvent $event
*/
public function onPreTransaction(PreTransactionEvent $event)
{
// get request from the event
$request = $event->getTransaction();
// setup new header to request
$modifiedRequest = $request->withHeader('Authorization', 'Bearer longLongLongToken');
// replace request in event
$event->setTransaction($modifiedRequest);
}
/**
* @param PostTransactionEvent $event
*/
public function onPostTransaction(PostTransactionEvent $event)
{
// get response from the event
$response = $event->getTransaction();
// check if response status code is 403
if ($response->getStatusCode() === Response::HTTP_FORBIDDEN) {
// invalidate token
}
}
public static function getSubscribedEvents()
{
return [
GuzzleEvents::preTransactionFor('payment') => 'onPreTransaction',
GuzzleEvents::postTransactionFor('payment') => 'onPostTransaction'
];
}
}And configure the Symfony service as usual for event subscribers:
services:
App\EventSubscriber\PartnersApiGuzzleEventSubscriber:
class: App\EventSubscriber\PartnersApiGuzzleEventSubscriber
tags:
- { name: kernel.event_subscriber }(This is not required when your project uses autoconfiguration, it will be tagged automatically)