Venture for Laravel

website·Indexed 31 days ago

https://laravel-venture.com

A package that extends Laravel's queue system to build complex workflows from regular Laravel jobs. It provides tools for defining job dependencies, managing workflow states, nesting workflows, and testing definitions with dedicated assertions like assertJobExists and assertWorkflowExists.

Tokens
15.1K
Snippets
83
Records
90
Agent score
53%

What's inside Venture

  1. Overview of Venture for Laravel

    Venture is a package built on top of Laravel's queue system that allows developers to create complex workflows using regular Laravel jobs. It manages the execution order by parallelizing jobs without interdependencies and ensuring jobs only start after their defined dependencies are resolved. Key features include defining job dependencies, conditional job addition, and manual gates.
  2. Overview of Venture workflow testing

    Venture provides a set of first-class test helpers designed to allow developers to reason about workflows without the need to execute them fully or dispatch numerous jobs. The two primary testing patterns for workflows are asserting that a workflow was started and testing the specific definition of a workflow.
  3. Understand how job failures affect workflow execution

    In a workflow, if a job fails to execute successfully, only the jobs in that specific subtree (those with a direct or transitive dependency on the failed job) are prevented from running. Jobs that do not depend on the failed job will continue to execute normally, ensuring the workflow is robust and not 'all-or-nothing'.
  4. Embed a nested workflow in a workflow definition

    To reuse a subset of jobs across different workflows, you can embed one workflow inside another using the addWorkflow method on the WorkflowDefinition instance. The outer workflow treats the nested workflow as if its individual jobs were added directly.
    <?php
    
    namespace App\Workflows;
    
    use Sassnowski\Venture\AbstractWorkflow;
    use Sassnowski\Venture\WorkflowDefinition;
    
    class PublishPodcastWorkflow extends AbstractWorkflow
    {
        public function __construct(private Podcast $podcast)
        {
        }
    
        public function definition(): WorkflowDefinition
        {
            return $this->define('Publish Podcast')
                ->addWorkflow(new OptimizePodcastWorkflow($this->podcast));
        }
    }
  5. Update WorkflowDefinition and Workflow::define for Venture 4.0

    The WorkflowDefinition constructor and the Workflow::define method now require an instance of AbstractWorkflow as the first parameter. In a workflow class, you should now pass $this to the define method, or use the convenience define method available on the AbstractWorkflow class.
    class PublishPodcastWorkflow implements AbstractWorkflow
    {
    	public function definition(): WorkflowDefinition
    	{
    - 		return Workflow::define('Publish podcast');
    + 		return Workflow::define($this, 'Publish podcast');
    + 		// OR use the convenience method:
    + 		// return $this->define('Publish podcast');
    	}
    }
  6. Add jobs and define dependencies in a workflow

    Jobs are added to a workflow using the addJob method on the WorkflowDefinition instance. To define dependencies, pass an array of class names as the second parameter to addJob. A job will only execute once all its specified dependencies have successfully finished. If no dependencies are provided, jobs will start in parallel.
    $this->define('Publish new podcast')
        ->addJob(new ProcessPodcast($this->podcast))
        ->addJob(new OptimizePodcast($this->podcast))
        ->addJob(new PublishPodcastOnTransistorFM($this->podcast), [
            ProcessPodcast::class,
            OptimizePodcast::class
        ]);
  7. Hook into Venture events using PluginContext

    Within a plugin's install method, use the PluginContext to register event listeners. You can use closures or Laravel-compatible event handlers (class strings, arrays, or string syntax). Handlers registered via class strings are resolved from Laravel's container, allowing for constructor dependency injection. Multiple handlers can be registered for the same event.
    public function install(PluginContext $context): void
    {
        // Using a closure
        $context->onWorkflowCreating(function (WorkflowCreating $event) {
           // logic here
        });
    
        // Using a class string (resolved via container)
        $context->onWorkflowCreating(MyEventListener::class);
    
        // Using array syntax
        $context->onWorkflowCreating([MyEventListener::class, 'execute']);
    
        // Using string syntax
        $context->onWorkflowCreating('App\Listeners\MyEventListener@execute');
    }
  8. Upgrade Laravel Venture to version 3.0

    Upgrading to Venture 3.0 requires PHP 8.0. The addJobWithDelay method has been removed from WorkflowDefinition; use the addJob method with PHP 8 named parameters instead.
    // Old way (removed)
    Workflow::define('Workflow name')
        ->addJobWithDelay(new Job(), now()->addMinute());
    
    // New way (PHP 8 named parameters)
    Workflow::define('Workflow name')
        ->addJob(new Job(), delay: now()->addMinute());
  9. Manage plugin dependencies and custom setup

    Because plugins are resolved via Laravel's container, you can typehint dependencies in the plugin's constructor. Additionally, the install method can be used for general Venture configuration, such as defining custom workflow or job models using Venture::useWorkflowModel and Venture::useWorkflowJobModel.

    Caveat: Plugins are installed during the boot phase. Do not perform register phase tasks (like container bindings) inside a plugin's install method.

    <?php
    
    use App\SomeService;
    use App\Models\MyWorkflowModel;
    use Sassnowski\Venture\Plugin\Plugin;
    use Sassnowski\Venture\Plugin\PluginContext;
    
    class MyPlugin implements Plugin
    {
        public function __construct(private SomeService $service) {}
    
        public function install(PluginContext $context): void
        {
            Venture::useWorkflowModel(MyWorkflowModel::class);
            // Other setup...
        }
    }
  10. Use closures as workflow jobs

    Venture allows adding closures as jobs. Because closures lack a class name, you must provide an explicit ID using the id parameter in addJob. Dependencies can be typehinted in the closure and will be resolved from Laravel's container. To access the job instance itself, include a parameter named $job of type WorkflowableJob.
    use Sassnowski\Venture\WorkflowableJob;
    
    $this->define('My Workflow')
        ->addJob(function (TruthGenerator $truths, WorkflowableJob $job) {
            Log::info($truths->getHotTake());
            Log::info($job->workflow());
        }, id: 'log-truth');
  11. Configure dependencies for nested workflows and jobs

    Nested workflows and individual jobs can have dependencies on each other.

    • Workflow depending on a job: When a nested workflow depends on a job, all root nodes (starting jobs) of that nested workflow will depend on the specified job.
    • Job depending on a workflow: A job depends on a nested workflow by waiting for all of the nested workflow's 'final' jobs (jobs with no further dependents) to complete.
    • Workflow depending on another workflow: All starting jobs of the dependent workflow will depend on all final jobs of the prerequisite workflow.
    <?php
    
    namespace App\Workflows;
    
    use Sassnowski\Venture\AbstractWorkflow;
    use Sassnowski\Venture\WorkflowDefinition;
    
    class PodcastWorkflow extends AbstractWorkflow
    {
        public function definition(): WorkflowDefinition
        {
            return $this->define('Podcast Workflow')
                // 1. Job that others depend on
                ->addJob(new ProcessPodcast($this->podcast))
    
                // 2. Nested workflow depending on a job
                ->addWorkflow(new EncodePodcastWorkflow($this->podcast), [
                    ProcessPodcast::class,
                ])
    
                // 3. Nested workflow depending on another nested workflow
                ->addWorkflow(new PublishPodcastWorkflow($this->podcast), [
                    EncodePodcastWorkflow::class,
                ])
    
                // 4. Job depending on a nested workflow
                ->addJob(new NotifySubscribers($this->podcast), [
                    ReleasePodcast::class,
                ]);
        }
    }
  12. Implement custom workflow state transitions

    Venture allows developers to override the default state transitioning behavior of workflows by implementing the WorkflowState interface. This is an 'escape hatch' for advanced extensions where the developer becomes responsible for maintaining consistent states.
    interface WorkflowState
    {
        public function markJobAsFinished(WorkflowableJob $job): void;
        public function markJobAsFailed(WorkflowableJob $job, Throwable $exception): void;
        public function allJobsHaveFinished(): bool;
        public function isFinished(): bool;
        public function markAsFinished(): void;
        public function isCancelled(): bool;
        public function markAsCancelled(): void;
        public function remainingJobs(): int;
        public function hasRan(): bool;
    }