Lesson 5: Interceptors
PHP Middlewares Interceptors
Last updated
Was this helpful?
PHP Middlewares Interceptors
Last updated
Was this helpful?
Ecotone
provide us with possibility to handle via Interceptors
.
Interceptor
as name suggest, intercepts the process of handling the message.
You may enrich the , stop or modify usual processing cycle, call some shared functionality, add additional behavior to existing code without modifying the code itself.
After one of our administrators went for holiday, the others found out, they can't change cost of the product and this become really problematic for them.
Administrators should be able to change the cost of a product
We could copy paste the logic from product.register
to product.changePrice
but we want to avoid code duplication, especially logic that may happen more often. Let's intercept our Command Handlers.
Let's start by creating Annotation
called RequireAdministrator
in new namepace App\Infrastructure\RequireAdministrator
Let's create our first Before Interceptor.
Start by removing old UserService
and create new one in different namespace App\Infrastructure\RequireAdministrator
. Remember to mark return type as void
, we will see why it is so important soon.
Before
- marks method as Interceptor
, so it can be be found by Ecotone.
Pointcut
- describes what should be intercepted.
CLASS_NAME
- indicates what should be intercepted using specific Class Name
or Attribute Name
annotated at the level of method or class
expression||expression
- Indicating one expression or another e.g. Product\*||Order\*
Now we need to annotate our Command Handlers:
We told Before Interceptor
that it should intercept all endpoints with annotation RequireAdministrator.
Now, whenever we will call our command handlers, they will be intercepted by UserService
.
You can try it out, by providing different userId
.
Let's change our testing class to remove metadata and add the Interceptor
.
changeHeaders
- Tells Ecotone
if this Interceptor modifies payload
or headers
. The default is payload
.
If changeHeaders=true
thenheaders
are picked and associative array must be returned. The returned value is merged with current headers.
If changeHeaders=false
then payload
is picked and current payload is replaced by returned value, the headers stays the same.
You may of course inject current payload and headers into the method if needed, as with usual endpoint.
precedence
- Tells Ecotone
in what order interceptors should be called. The lower the value is the quicker interceptor will be called. The order exists within interceptor type: before/around/after.
We want to call AddUserId Interceptor
before RequireAdministrator Interceptor
as it require userId
to exists, in order to verify.
AddUserIdService
has precedence of 0
as default, so UserService
must have at least 1
.
Let's annotate Product
aggregate
If we annotate aggregate on the class level. Then it does work like each of the method would be annotated with specific annotation in this case @AddUserId.
Let's run our testing command:
If during Before
or Around
you decide to break the flow, return null
. Null
indiciates, that there is no message and the current flow ends.
Null can not be returned in header changing interceptor, it does work only for payload changing interceptor.
The Around Interceptor
is closet to actual endpoint's method call. Thanks to that, it has access to Method Invocation.
This does allow for starting some procedure and ending after the invocation is done.
Connection
to sqlite database using dbal library
This does create database table, if needed. It does create simple table structure containing id
of the aggregate, the class
type and serialized data
in JSON
. Take a look at createSharedTableIfNeeded
if you want more details.
Deserialize aggregate to PHP
Serialize aggregate to JSON
We want to intercept Command Bus Gateway
with transaction. So whenever we call it, it will invoke our Command Handler within transaction.
pointcut="Ecotone\Modelling\CommandBus"
This pointcut will intercept CommandBus.
Let's run our testing command:
We do have two transactions started, because we call the Command Bus twice.
Great, we have just finished Lesson 5!
Interceptors are very powerful concept. Without extending any classes or interfaces from Ecotone
, we can build build up Authorization, Transactions, Delegate duplicated logic, Call some external service, Logging and Tracing before invoking endpoint, the amount of possibilities is endless.
In the next chapter, we will learn about scheduling and polling endpoints
NAMESPACE*
- Indicating all starting with namespace e.g. App\Domain\Product\*
Before
and After
interceptors are depending on the return type, to decide if they should modify or pass it through.
If return type is different than void, Message payload or headers can be enriched with data.
If return type is void then message will be passed through and the process of message flow can be interrupted by throwing exception only.
Instead of providing the userId
during calling the CommandBus
we will enrich with it before it will be handled by Command Handler
using Interceptor
.
We will add real database to our example using if you do not have extension installed, then you will need to install it first. Yet if you are using Quickstart's Docker
container, then you are ready to go.
Let's start by implementing repository, that will be able to handle any aggregate, by storing it in sqlite
database.
Before we do that, we need to remove our In Memory implementation class App\Domain\Product\InMemoryProductRepository
we will replace it with our new implementation.
We will create using new namespace for it App\Infrastructure\Persistence.
Besides we are going to use , as this is really helpful abstraction over the PDO.
And the :
Serializer
is registered by Ecotone.
Serializer can handle serialization using .
It this case it will know how to register Cost
class, as we already registered Converter for it.
Serializer give us access for conversion from PHP
type to specific Media Type or from specific Media Type to PHP
type. We will use it to easily serialize our Product
model into JSON
and store it in database.
Each of interceptors, can inject attribute, which was used for pointcut. Just type hint for it in method declaration.
Around interceptors can inject intercepted class instance. In above example it would be Command Bus.
In case of Command Bus it may seems not needed, but if we would intercept Aggregate, then it really useful as for example you may verify if executing user have access to it.
You may read more about interceptors in .