Host Integration Checklist

Alpha notice: dbflowlabs/core is a headless runtime. It does not ship Filament pages, business guards, or assignee business rules. This page lists what your Laravel application must wire after composer require dbflowlabs/core.

Use this checklist before calling DBFlow::start() in production. Skipping a step is the most common cause of WorkflowNotFoundException, empty assignee lists, or approvals that never appear in UI.

Integration flow

Install Core → publish config/migrations → migrate
    → attach HasWorkflow to models
    → register WorkflowDefinitionProvider (boot)
    → register AssigneeResolver keys (when using callback / permission assignees)
    → SyncWorkflowDefinitions (deploy / artisan)
    → expose start + inbox + business guards in your app
    → optional WorkflowHooks for host status columns

1. Install and configure

composer require dbflowlabs/core
php artisan vendor:publish --tag=dbflow-config
php artisan vendor:publish --tag=dbflow-migrations
php artisan migrate

Pin an exact alpha tag in production (for example 0.1.0-alpha.1). See Installation for Packagist and path-repository options.

config/dbflow.php

Key Default Purpose
enabled true Feature flag. During alpha, setting false does not fully unregister the service provider yet.
binding_mode code code = explicit start / startWorkflow(). ui = auto-start on model created when dbflow_workflows.model_type matches.
auth.model from env Host user model FQCN (DBFLOW_AUTH_MODEL).
auth.guard from env Guard for DbflowAuth::currentUser() (DBFLOW_AUTH_GUARD).
auth.resolver ConfigUserResolver Override when you need custom actor lookup (UUID/ULID stacks).
visual_builder_enabled false Pro / visual builder flag.

Example .env:

DBFLOW_ENABLED=true
DBFLOW_BINDING_MODE=code
DBFLOW_AUTH_MODEL=App\Models\User
DBFLOW_AUTH_GUARD=web

2. Register providers at boot

Create a host service provider (recommended) instead of inline registration in routes or controllers:

<?php

namespace App\Providers;

use App\Support\Dbflow\AssigneeResolvers\FinanceTeamAssigneeResolver;
use App\Support\Dbflow\Definitions\RefundDisputeWorkflowProvider;
use App\Support\Dbflow\RefundDisputeWorkflowHooks;
use DbflowLabs\Core\DBFlow;
use DbflowLabs\Core\Services\AssigneeResolverRegistry;
use DbflowLabs\Core\Services\WorkflowDefinitionRegistry;
use DbflowLabs\Core\Services\WorkflowHooksRegistry;
use Illuminate\Support\ServiceProvider;

final class AppDbflowServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if (! config('dbflow.enabled', true)) {
            return;
        }

        DBFlow::registerDefinitionProvider(
            $this->app->make(WorkflowDefinitionRegistry::class),
            $this->app->make(RefundDisputeWorkflowProvider::class),
        );

        DBFlow::registerAssigneeResolver(
            $this->app->make(AssigneeResolverRegistry::class),
            'finance_team',
            $this->app->make(FinanceTeamAssigneeResolver::class),
        );

        DBFlow::registerWorkflowHooks(
            $this->app->make(WorkflowHooksRegistry::class),
            'refund_dispute_approval',
            RefundDisputeWorkflowHooks::class,
        );
    }
}

Register the provider in bootstrap/providers.php.

Core ships no php artisan dbflow:sync command. Wrap SyncWorkflowDefinitions in your own deploy step or Artisan command.

3. Sync definitions into the database

DBFlow::start() resolves the published version from dbflow_workflows / dbflow_workflow_versions. Registering a PHP provider alone is not enough.

use DbflowLabs\Core\Actions\SyncWorkflowDefinitions;

/** @var array{created: list<string>, updated: list<string>, unchanged: list<string>} $summary */
$summary = app(SyncWorkflowDefinitions::class)->handle();

// created / updated / unchanged workflow keys

Run sync:

  • After first deploy
  • After changing a WorkflowDefinitionProvider definition
  • In CI before integration tests that call DBFlow::start()

Provider definitions may include 'enabled' => true (maps to dbflow_workflows.is_enabled). Disabled workflows throw WorkflowNotAvailableException on start.

Filament definition editor after code sync

SyncWorkflowDefinitions publishes versions but does not write draft_definition. The Standard WorkflowResource node editor is hidden unless Workflow::hasDraft() is true — so code-synced workflows may show metadata on the edit page but no approval graph form.

If operators need the Standard editor for code-owned workflows, seed a draft after sync with SaveWorkflowDraft and the workflow's currentDefinition(). Skip workflows with source = ui or existing drafts. See Filament UI.

Alternatively, disable enable_workflow_definition_resource when definitions are code-only.

4. Assignee resolution (callback / permission)

Approval nodes declare assignees under config.assignees. The alpha runtime supports:

type Runtime behaviour
user Single numeric user ID in value.
callback callback (or value) must match a key passed to DBFlow::registerAssigneeResolver().
permission value is also a resolver registry key — not Spatie Permission, not Laravel Gate.
role Listed in schema for editors; not supported by the open-core runtime (throws InvalidWorkflowDefinitionException).

Example callback assignee in definition JSON:

'assignees' => [
    'type' => 'callback',
    'callback' => 'finance_team',
],

Resolver implementation:

use DbflowLabs\Core\Contracts\AssigneeResolver;
use DbflowLabs\Core\Models\WorkflowInstance;

final class FinanceTeamAssigneeResolver implements AssigneeResolver
{
    public function resolve(WorkflowInstance $instance, array $nodeDefinition): array
    {
        // Return list<int> user IDs
        return [/* ... */];
    }
}

See Code-defined Workflows for full examples.

5. Start workflows with metadata (optional)

DBFlow::start(
    'refund_dispute_approval',
    $dispute,
    $submitter,
    ['submit_comment' => 'Please review'],
);

Metadata is stored on dbflow_workflow_instances.metadata. It does not replace WorkflowContextInterface::getWorkflowVariables() for transition conditions.

6. Act on tasks — always pass an actor

DBFlow::approve($task, $user, 'Approved.');
DBFlow::reject($task, $user, 'Missing receipt.', RejectStrategy::End);

When $actor is null, Core skips pending-assignment checks on approve. Production code should always pass the authenticated user.

7. Cancel — host authorization required

DBFlow::cancel($instance, $user, 'Withdrawn by submitter.');

Core does not enforce “only the submitter may cancel”. Gate cancel() in your Filament actions or services (for example compare $instance->started_by_user_id).

Cancelled instances set status = cancelled and clear active_key. Whether the business record may proceed after cancel is host policy (hooks or guards).

8. Query helpers on the model

HasWorkflow exposes:

$model->runningWorkflowInstance('workflow_key');
$model->completedWorkflowInstance('workflow_key'); // approved | rejected | cancelled
$model->hasRunningWorkflow('workflow_key');
$model->workflowLogs('workflow_key');

Use completedWorkflowInstance() when business actions (confirm order, post payment) must check the latest terminal workflow outcome.

9. UI options

Approach When to use
dbflowlabs/filament Recommended Standard UI: My Tasks, instances, timelines, optional definition resource. Register via DBFlowFilamentPanel::register($panel) and replace permission_checker_class.
Host-built Filament inbox Custom task list when you need a deeply embedded ERP UX; still call DBFlow::approve() / reject() with an authenticated actor.
API only Mobile or external approvers.

Core does not require Filament. Filament UI is a separate Composer package.

Filament Standard checklist (when using dbflowlabs/filament)

  1. composer require Filament + Core alpha packages; migrate.
  2. Publish dbflow-filament-config; set permission_checker_class (not AllowAllPermissionChecker).
  3. Call DBFlowFilamentPanel::register($panel) when both dbflow.enabled and your host feature flag are on.
  4. Implement WorkflowRouteResolvable on workflowable models for links back to business resources.
  5. Bind PermissionAssigneeOptionsResolver so definition editors show labels for your Core assignee resolver keys.
  6. After sync, seed drafts if operators need the Standard definition editor for code workflows.

See Host Reference Patterns for a full module layout, boot order, and ERP pilot case study.

10. Business guards (host responsibility)

Core does not know your domain rules. Examples the host must implement:

  • Block confirm() while hasRunningWorkflow() is true
  • Hide legacy approval UI when DBFlow engine is active
  • Map hook outcomes to document status columns

Runtime exceptions (quick reference)

Exception Typical cause
WorkflowNotFoundException Key missing — sync not run or typo
WorkflowNotPublishedException No current_version_id / active version
WorkflowNotAvailableException is_enabled = false or workflow archived
WorkflowAlreadyRunningException Duplicate active_key for same workflowable
InvalidWorkflowDefinitionException Invalid graph or empty assignee resolution
UserCannotApproveTaskException Actor has no pending assignment
TaskNotPendingException Stale task action / double submit

UI binding mode (binding_mode = ui)

When DBFLOW_BINDING_MODE=ui, HasWorkflow auto-starts matching workflows on model created. You must also set dbflow_workflows.model_type to the workflowable morph class for each workflow row. SyncWorkflowDefinitions does not set model_type automatically — configure it in seeders or admin tooling.

What's next

Something wrong? Open an issue on GitHub