Download the PHP package matheus-rosa/php-interactor without Composer
On this page you can find all versions of the php package matheus-rosa/php-interactor. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download matheus-rosa/php-interactor
More information about matheus-rosa/php-interactor
Files in matheus-rosa/php-interactor
Package php-interactor
Short Description A single purpose object library built for PHP
License
Informations about the package php-interactor
php-interactor
A single purpose object library built for PHP. Heavily inspired by the interactor gem for Ruby.
Requirements
- PHP >= 5.6
What is an Interactor?
An Interactor is, in simple words, a single purpose object. That means a class having a single responsibility according to what SOLID principles describes. An Interactor usually represents an action, such as SaveUser, BuildAttributes, GetExternalAPIResource and so forth. A SaveUser interactor would only literally mean saving a user record in some storage (a database, for instance) therefore it won't be responsible for doing anything else.
OK, but why is a big deal to use it?
You may be asking yourself why this library is relevant to you. Of course, you can go ahead and start creating your own single-purpose object implementation though we all know that not establishing a well-designed pattern in the very beginning of a development cycle can result in totally chaos where everybody does whatever they want without a pattern. Plus, why reinvent the wheel whilst someone has done the heavy job for you?
By giving a chance to this library you will find yourself creating straight forward and maintainable services while adopting a conventional pattern among them. I assure you that you will save some considerable time. Give it a shot! :)
Installation
Creating your first Interactor
Say we're about to create a component responsible for saving a User
model record:
To make it an Interactor, all you need to do is to simply import the Interactable
trait.
To do that, you only need to simply import it in your class:
There we go! You can put all of your business logic within the execute
method. The SaveUser
can be invoked like this:
Note we have just passed an array as an argument to the static call
method. You can pass any values to your associative array
or even leaving it as blank (not passing anything to it at all, e.g. SaveUser::call()
).
Note 2: think of call
method as a public API while execute
method is how your business logic will be handled internally.
Each Interactor
needs to implement the execute
method.
You can retrieve the informed parameters in your SaveUser
class like this:
Checking Interactor success
If an Interactor does not call the fail
method with an error message, it is considered as a success scenario.
You can check it by invoking the success
method from the returned context:
Failing an Interactor
Interactors can be set as failure like this:
Once the fail
method is invoked the execution flow will immediately stop. That means any code after the if
condition in the example above
will become unreachable.
By default, the fail
method does not throw any exception though you can change its behavior by setting its second argument ($strict
)
as true:
That way, from now on the ContextFailureException
will be raised.
The errors itself can be retrievable like this:
Hooks
Interactors contain a set of hooks that can run in some circumstances:
around
Think of it like a middleware that will run even before of what's defined in your execute
method.
You can totally prevent an Interactor to run if some particular rule is not satisfactory. This comes handy when needing to define a bunch of guards
preventing your code to execute:
before
As the name says for itself, the before
hook is something that will execute right before what's defined in your execute
method.
Important to notice that this method has a lesser priority than the around
method.
after
Use the after
method if you want to run anything after the execute
method.
Hook precedence
To clarify it even more, the execution order can be represented like below:
around -> before -> execute -> after
Full example of an Interactor with all hooks
Will output:
Organizers
Sometimes a single-purpose Interactor is not enough to embrace everything your business logic requires.
Say you're about to handle a custom flow that will need to do a lot of things. Of course you can call Interactors within themselves although Organizer exists to make it way easier. With an Organizer you can define a pipeline of Interactors to run in a consecutive order.
To create an Organizer, all you have to do is to use the Organizable
trait like that:
All right! And then within the organize
method you can define the execution order of your Interactors:
Done! Now you've defined your chain and each Interactor will execute in the defined order. Your Organizer can be called the same way you'd call a single Interactor:
If you want, you can use the very same hooks present in Interactor
within your Organizer
:
Failure within an Organizer pipeline
By default, an Organizer pipeline flow will immediately stop if any Interactor defined on it fails.
When that happens, each Interactor which had run has a chance to rollback
its applied changes. This will happen in a reversed order (from the last to the first Interactor):
Continue an Organizer flow regardless if an Interactor failed
You can totally replace the default behaviour of your organizer by overriding the continueOnFailure
method:
Examples
If you're still not sure how to use it or how it can become valuable to your engineering team, feel free to check out all examples under the examples/ directory. Hopefully some of them can clarify the usage better, with real world examples.