Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Workflow Laravel Package

zerodahero/laravel-workflow

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require zerodahero/laravel-workflow
    

    For Laravel 10/11/12/13, use ^6.x (PHP 8.1+). For older versions, check the version matrix.

  2. Publish Config

    php artisan vendor:publish --provider="ZeroDaHero\LaravelWorkflow\WorkflowServiceProvider"
    

    This generates config/workflow.php.

  3. Define a Workflow Add a workflow to config/workflow.php:

    'blog_post' => [
        'supports' => [App\Models\BlogPost::class],
        'places' => ['draft', 'review', 'published'],
        'transitions' => [
            'to_review' => ['from' => 'draft', 'to' => 'review'],
            'publish' => ['from' => 'review', 'to' => 'published'],
        ],
    ]
    
  4. Attach to Model Use the WorkflowTrait in your model:

    use ZeroDaHero\LaravelWorkflow\Traits\WorkflowTrait;
    
    class BlogPost extends Model
    {
        use WorkflowTrait;
    }
    
  5. First Transition

    $post = BlogPost::find(1);
    $post->workflow_apply('to_review'); // Apply transition
    $post->save(); // Persist state
    

Implementation Patterns

Common Workflows

  1. State Machine (Single State) Use type: 'state_machine' for models with one active state (e.g., order status).

    'order_status' => [
        'type' => 'state_machine',
        'supports' => [App\Models\Order::class],
        'places' => ['pending', 'shipped', 'delivered'],
        'transitions' => [...],
    ]
    
  2. Workflow (Multiple States) Default type. Use for complex workflows (e.g., approval chains).

    'approval_workflow' => [
        'type' => 'workflow',
        'supports' => [App\Models\Document::class],
        'places' => ['submitted', 'reviewed', 'approved', 'rejected'],
        'transitions' => [...],
    ]
    
  3. Dynamic Workflows Load workflows dynamically via a service or API:

    $workflowConfig = $this->fetchWorkflowConfigFromApi();
    Workflow::register($workflowConfig);
    

Integration Tips

  • Validation Check transitions before applying:

    if ($post->workflow_can('publish')) {
        $post->workflow_apply('publish');
    }
    
  • Events Listen to transitions for side effects (e.g., notifications):

    // In EventServiceProvider
    $this->listen(
        'workflow.blog_post.transition.publish',
        \App\Listeners\SendPublishNotification::class
    );
    
  • Metadata Attach metadata to places/transitions for business logic:

    'places' => [
        'draft' => ['metadata' => ['max_words' => 1000]],
    ],
    
  • Custom Guards Block transitions via events:

    public function onGuard(GuardEvent $event) {
        if ($event->getSubject()->is_confidential) {
            $event->getOriginalEvent()->setBlocked(true);
        }
    }
    
  • Testing Mock workflows in tests:

    $workflow = Mockery::mock(WorkflowInterface::class);
    $workflow->shouldReceive('can')->andReturn(true);
    $this->app->instance(WorkflowInterface::class, $workflow);
    

Gotchas and Tips

Pitfalls

  1. Forgetting to Save Transitions update the model’s marking property but won’t auto-save. Always call $model->save() after applying transitions.

  2. Multiple Workflows If a model supports multiple workflows, specify the name:

    $workflow = Workflow::get($post, 'blog_post'); // Explicit workflow
    
  3. State Machine vs. Workflow

    • State Machine: Only one place (state) is active at a time.
    • Workflow: Multiple places can be active simultaneously (e.g., "submitted" + "reviewed"). Misconfiguring this can lead to unexpected behavior.
  4. Event Listener Duplication Avoid listening to raw event classes (e.g., GuardEvent). Use Symfony’s dot syntax (e.g., workflow.blog_post.guard.publish) to prevent duplicates.

  5. Metadata Access Metadata is stored but not automatically cast. Access it via:

    $metadata = $workflow->getMetadataStore()->getPlaceMetadata('draft');
    

Debugging Tips

  • Check Current State

    $places = $workflow->getMarking($post)->getPlaces();
    dd($places); // ['draft', 'review']
    
  • Enabled Transitions

    $enabled = $workflow->getEnabledTransitions($post);
    dd($enabled->getNames()); // ['publish']
    
  • Event Debugging Use Tinker to test events:

    php artisan tinker
    >>> event(new \ZeroDaHero\LaravelWorkflow\Events\GuardEvent(...));
    

Extension Points

  1. Custom Marking Stores Override the default EloquentMethodMarkingStore for non-Eloquent models:

    'marking_store' => [
        'property' => 'status',
        'class' => \App\Services\CustomMarkingStore::class,
    ]
    
  2. Dynamic Workflow Registration Register workflows at runtime:

    Workflow::register([
        'dynamic_workflow' => [...],
    ]);
    
  3. Custom Transition Logic Extend the workflow class:

    class CustomWorkflow extends Workflow
    {
        public function preApply(Transition $transition, $subject) {
            // Custom logic before transition
        }
    }
    

    Bind it in AppServiceProvider:

    $this->app->bind(WorkflowInterface::class, CustomWorkflow::class);
    
  4. Laravel Policies Integrate with Laravel’s authorization:

    public function authorizePublish(User $user, BlogPost $post) {
        return $post->workflow_can('publish');
    }
    

Performance

  • Caching Workflows Register workflows once (e.g., in boot()) to avoid repeated parsing:

    public function boot() {
        Workflow::register(config('workflow'));
    }
    
  • Avoid Over-Fetching Use with() to eager-load related models if workflows depend on them:

    $post = BlogPost::with('author')->find(1);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata