So we have a queue and a manager that gives us jobs from it, how do we actually work it?
The QueueManager uses a concept that we call an executor. An executor is a piece of code (a service) that processes a job's payload. The payload is the data of a job, and it is stored as a string. The QueueManager uses JSON to encode/decode the strings, so you can use array's as payload.
An executor is an instance of ExecutorInterface and as such it implements these methods:
class Executor implements ExecutorInterface
{
public function getName()
{
return 'user.mail.registration';
}
public function execute(array $payload)
{
$userId = array_shift($payload);
$user = $this->findUser($userId);
// send a registration mail
$this->mailer->sendRegistrationMail($user);
return true;
}
}
These executors need to be registered in the QueueManager. This is required
when adding a job or requesting one! When you create a service for this
executor and tag it with tree_house.worker.executor
, the executor gets
registered automatically.
Since a payload can be any string that we encode/decode, it is useful to make this more robust and configure the payload that an executor accepts. The OptionsResolver component is very useful for this. In our previous example we are passing around an id for a User object. We want to make sure that the given payload:
- is in fact a numeric value, and not
null
, for example - is a valid identifier
Using the OptionsResolver
we can ensure both requirements, and in fact even
transform the identifier into a User object!
use Symfony\Component\OptionsResolver\Exception\InvalidArgumentException;
class Executor implements ExecutorInterface
{
// ...
public function configurePayload(OptionsResolver $resolver)
{
$resolver->setRequired(0);
$resolver->setAllowedTypes(0, 'numeric');
$resolver->setNormalizer(0, function (Options $options, $value) {
if (null === $part = $this->findUser($value)) {
throw new InvalidArgumentException(sprintf('User with id "%d" does not exist', $value));
}
return $part;
});
}
}
Normally it is advised to use names for the options, instead of digits. However
since the ScheduleCommand
and ExecuteCommand
are not
equipped to input associative arrays we generally recommend against it. If
you're not using these commands, or using a workaround for this, you can use
names for your options.
We mentioned in the previous chapter that you can add jobs for objects. To do this, the executor needs to implement the ObjectPayloadInterface, which tells the QueueManager how to construct a payload from an object. Continuing from our previous example:
class Executor implements ExecutorInterface, ObjectPayloadInterface
{
// ...
public function supportsObject($object)
{
return $object instanceof User;
}
public function getObjectPayload($object)
{
return [$object->getId()];
}
}
Now that we have everything in place, lets work some jobs!