# Defining Workflows
# Creating a workflow definition
Now you're ready to create your first workflow. Workflows are defined as classes that extend from AbstractWorkflow
. The AbstractWorkflow
class defines an abstract method definition
that you have to implement.
To continue with our example of publishing a podcast, let's create a class called PublishPodcastWorkflow
.
use App\Podcast;
use Sassnowski\Venture\Facades\Workflow;
use Sassnowski\Venture\AbstractWorkflow;
use Sassnowski\Venture\WorkflowDefinition;
class PublishPodcastWorkflow extends AbstractWorkflow
{
private Podcast $podcast;
public function __construct(Podcast $podcast)
{
$this->podcast = $podcast;
}
public function definition(): WorkflowDefinition
{
return Workflow::define('Publish new podcast')
->addJob(new ProcessPodcast($this->podcast))
->addJob(new OptimizePodcast($this->podcast));
}
}
The define
method on the Workflow
facades returns a new WorkflowDefinition
with the provided name. The definition object can now be used to add jobs to the workflow.
# Adding jobs to a workflow
To add a job to a workflow, call the addJob
method on the definition instance.
Workflow::define('Publish new podcast')
->addJob(new ProcessPodcast($this->podcast));
Venture provides a fluent interface to define workflows, so you can simply keep chaining method calls to build up your workflow.
# Jobs with dependencies
Venture really starts to shine once you start adding jobs that have dependencies. To define a job's dependencies, pass in an array of class names as the second parameter to the addJob
method.
Workflow::define('Publish new podcast')
->addJob(new ProcessPodcast($podcast))
->addJob(new OptimizePodcast($podcast))
->addJob(new PublishPodcastOnTransistorFM($podcast), [
ProcessPodcast::class,
OptimizePodcast::class
]);
In this example you're telling Venture that the PublishPodcastOnTransistorFM
job can only run once both ProcessPodcast
and OptimizePodcast
have finished.
The workflow we have configured so far would look like this.
From this point, you can keep adding jobs to the workflow and it will keep track of all dependencies. All you need to do is to define a job's direct dependencies.
Workflow::define('Publish new podcast')
->addJob(new ProcessPodcast($podcast))
->addJob(new OptimizePodcast($podcast))
->addJob(new PublishPodcastOnTransistorFM($podcast), [
ProcessPodcast::class,
OptimizePodcast::class
])
->addJob(new PublishPodcastOnApplePodcasts($podcast), [
ProcessPodcast::class,
OptimizePodcast::class
])
->addJob(new CreateAudioTranscription($podcast), [
ProcessPodcast::class,
])
->addJob(new TranslateAudioTranscription($podcast), [
CreateAudioTranscription::class,
]);
Direct and transitive dependencies
If we look at the diagram above, TranslateAudioTranscription
has a direct dependency on CreateAudioTranscription
. CreateAudioTranscription
in turn has a dependency on ProccessPodcast
. This makes ProcessPodcast
a transitive dependency of TranslateAudioTranscription
(think dependency of a dependency).
Luckily, you don't have to worry about how exactly a job fits into a workflow. All you need to know is what other jobs a job is directly dependant on and Venture will figure out the rest.
Note
There are few caveats for defining worklows. For example you can't have multiple instances of the same job within a workflow. Check the Caveats and limiations page for more information.
# Naming jobs
If you want, you can provide an optional name for a job that will be saved in the database. If not, the class name will be used.
Workflow::define('Publish new podcast')
->addJob(new ProcessPodcast($podcast), [], 'Process podcast');
You access a step's name via its $name
property.
$workflow->jobs[0]->name;
// "Process podcast"
Tip
If you plan on displaying the workflow jobs to your users, you could pass in a translation string as the job name. This way you would be able to display the localized name of the job in the UI by using Laravel's built-in localization features.
# Delaying a job
If you don't want to immediately execute a job as soon as it can be run, you can define a delay for it. Laravel jobs already ship with this feature, so you could simply call the delay
method on your job instance like this.
$workflow = Workflow::new('Publish new podcast')
->addJob((new ProcessPodcast($podcast))->delay(now()->addDay()));
However, this can quickly become quite cluttered and difficult to read. For this reason, Venture provides you with a addJobWithDelay
method.
$workflow = Workflow::new('Publish new podcast')
->addJobWithDelay(new ProcessPodcast($podcast), now()->addDays(2));
NOTE
Your job class needs to be using the Illuminate\Bus\Queueable
trait to support this. For more information check Laravel's documentation on the topic.
# Starting a workflow
Now that we have defined our workflow, we can start it from somewhere within our application by calling its static start
method.
use App\Workflows\PublishPodcastWorkflow;
PublishPodcastWorkflow::start($podcast);
Any parameter you pass to the start
method will be passed to the workflow's constructor.
Venture will now figure out which jobs can be immediately dispatched (because they don't have any dependencies) and process them in parallel. Every time a job finishes, it will check if any of the job's dependant jobs are now ready to be run. If so, it will dispatch them.
Dependant jobs and dependencies
A dependant job is a job that is waiting for another job to finish. In other words, if JobB
needs JobA
to finish before it can run, JobB
is a dependant on JobA
.