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

Workflow Manager Laravel Package

xentixar/workflow-manager

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require xentixar/workflow-manager
    php artisan vendor:publish --tag=workflow-manager-config
    php artisan vendor:publish --tag=workflow-manager-migrations
    php artisan migrate
    

    Configure config/workflow-manager.php with your roles (e.g., 'admin' => 'Admin').

  2. Define an Enum: Create a PHP enum for your workflow states (e.g., Status::class):

    enum Status: string {
        case DRAFT = 'draft';
        case REVIEW = 'review';
        case PUBLISHED = 'published';
        case ARCHIVED = 'archived';
    }
    
  3. Add to a Filament Resource: Use StateSelect in your Filament resource:

    use Xentixar\WorkflowManager\Components\StateSelect;
    
    StateSelect::make('status')
        ->setRole('admin') // Matches config key
        ->enum(Status::class)
        ->required();
    
  4. First Transition: In your resource's edit() or create() method, attach the workflow:

    public static function form(Form $form): Form {
        return $form
            ->schema([
                // ... other fields
                StateSelect::make('status')
                    ->setRole('admin')
                    ->enum(Status::class)
                    ->required(),
            ]);
    }
    

First Use Case: Basic Workflow

Create a workflow for a Post model with transitions:

php artisan make:workflow PostStatus --role=admin --enum=Status

This generates a migration and workflow definition. Edit the migration to define transitions:

Schema::create('post_status_workflows', function (Blueprint $table) {
    $table->id();
    $table->string('role')->index();
    $table->string('enum')->index();
    $table->json('transitions')->comment('Allowed transitions: {from: "draft", to: "review"}');
    $table->timestamps();
});

Populate the transitions field with allowed state changes (e.g., ['from' => 'draft', 'to' => 'review']).


Implementation Patterns

Workflow Integration in Filament Resources

  1. State Field in Forms/Tables: Reuse StateSelect in forms and tables for consistency:

    // Form
    StateSelect::make('status')
        ->setRole('admin')
        ->enum(Status::class)
        ->required()
        ->live(onChange: fn ($value) => $this->set('last_updated_at', now()));
    
    // Table
    Tables\Columns\SelectColumn::make('status')
        ->options(Status::class)
        ->color(fn ($state) => match ($state) {
            Status::DRAFT => 'gray',
            Status::PUBLISHED => 'green',
            default => 'blue',
        });
    
  2. Transition Buttons: Dynamically render transition buttons based on current state:

    use Xentixar\WorkflowManager\Facades\WorkflowManager;
    
    $currentState = $record->status;
    $allowedTransitions = WorkflowManager::getAllowedTransitions(
        role: 'admin',
        enum: Status::class,
        from: $currentState
    );
    
    foreach ($allowedTransitions as $transition) {
        Button::make('Transition to ' . $transition['to'])
            ->action(fn () => $this->updateStatus($transition['to']))
            ->visible(fn () => $currentState === $transition['from']);
    }
    
  3. Workflow-Aware Logic: Use middleware or policies to enforce workflow rules:

    // Policy
    public function update(User $user, Post $post): bool {
        return WorkflowManager::canTransition(
            role: 'admin',
            enum: Status::class,
            from: $post->status,
            to: $newStatus
        );
    }
    

Advanced Patterns

  1. Conditional Transitions: Attach conditions to transitions via the conditions field in the workflow table:

    // Migration
    $table->json('conditions')->nullable()->comment('e.g., {to: "review", callback: "App\\Policies\\PostPolicy@canReview"}');
    
    // Usage
    WorkflowManager::canTransition(
        role: 'admin',
        enum: Status::class,
        from: Status::DRAFT,
        to: Status::REVIEW,
        conditions: ['callback' => 'App\Policies\PostPolicy@canReview']
    );
    
  2. Bulk Transitions: Add a bulk action to Filament tables:

    Tables\Actions\Action::make('bulkTransition')
        ->action(fn (Collection $records, string $toState) => {
            foreach ($records as $record) {
                $record->update(['status' => $toState]);
            }
        })
        ->modalHeading('Transition Selected Items')
        ->modalDescription('Select the new status:')
        ->modalSubmitAction(fn (Collection $records) => $this->bulkTransition($records, $this->modalState))
        ->modalContent(fn (Collection $records) => StateSelect::make('status')
            ->enum(Status::class)
            ->required()
            ->live(onChange: fn ($value) => $this->modalState = $value));
    
  3. Workflow Diagrams: Generate interactive diagrams for admin panels:

    use Xentixar\WorkflowManager\Components\WorkflowDiagram;
    
    WorkflowDiagram::make()
        ->role('admin')
        ->enum(Status::class)
        ->width('full');
    

Testing Workflows

  1. Unit Tests: Test transition logic in isolation:

    public function test_can_transition_from_draft_to_review() {
        $this->assertTrue(
            WorkflowManager::canTransition(
                role: 'admin',
                enum: Status::class,
                from: Status::DRAFT,
                to: Status::REVIEW
            )
        );
    }
    
  2. Feature Tests: Test Filament interactions:

    public function test_status_transition_in_filament() {
        $this->actingAs($adminUser)
            ->get('/admin/posts/1/edit')
            ->assertSee('Draft')
            ->press('Transition to Review')
            ->assertSee('Review');
    }
    

Gotchas and Tips

Common Pitfalls

  1. Role Mismatch:

    • Issue: Transitions fail silently if the role in StateSelect doesn’t match the workflow’s role.
    • Fix: Ensure setRole() matches the workflow’s role field in the database.
    • Debug: Run WorkflowManager::getWorkflows('admin', Status::class) to verify loaded workflows.
  2. Enum Mismatch:

    • Issue: Using a different enum class than defined in the workflow table.
    • Fix: Double-check the enum field in the workflow table matches your enum class name.
  3. Missing Migrations:

    • Issue: Forgetting to run migrations after publishing.
    • Fix: Always run php artisan migrate post-installation.
  4. Circular References:

    • Issue: Defining transitions that create circular dependencies (e.g., A → B and B → A without conditions).
    • Fix: Use conditions to gate reverse transitions or document workflow rules clearly.
  5. Livewire Stale State:

    • Issue: StateSelect not updating immediately after a transition.
    • Fix: Use live() with onChange to refresh dependent fields:
      StateSelect::make('status')
          ->live(onChange: fn ($value) => $this->emit('refreshForm'));
      

Debugging Tips

  1. Log Workflow Data: Add temporary logging to inspect workflows:

    \Log::info('Workflows for admin:', [
        'workflows' => WorkflowManager::getWorkflows('admin', Status::class),
    ]);
    
  2. Check Database: Verify workflow definitions in the workflow_transitions table:

    SELECT * FROM workflow_transitions WHERE role = 'admin' AND enum = 'App\\Enums\\Status';
    
  3. Enable Query Logging: Temporarily enable Laravel’s query log to debug migration issues:

    DB::enableQueryLog();
    // Run migration code...
    dd(DB::getQueryLog());
    

Configuration Quirks

  1. Default Role:

    • If no role is set in StateSelect, the package defaults to the first role in config/workflow-manager.php.
    • Tip: Explicitly set the role to avoid ambiguity.
  2. Enum Serialization:

    • The enum field in the database stores the fully qualified class name (e.g., App\Enums\Status).
    • Tip: Use get_class($enum) to ensure consistency.
  3. Transition Conditions:

    • Conditions are stored as JSON in the conditions field. Use arrays for complex logic:
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor