Host Integration Checklist
Alpha notice:
dbflowlabs/coreis a headless runtime. It does not ship Filament pages, business guards, or assignee business rules. This page lists what your Laravel application must wire aftercomposer 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
WorkflowDefinitionProviderdefinition - 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)
composer requireFilament + Core alpha packages; migrate.- Publish
dbflow-filament-config; setpermission_checker_class(notAllowAllPermissionChecker). - Call
DBFlowFilamentPanel::register($panel)when bothdbflow.enabledand your host feature flag are on. - Implement
WorkflowRouteResolvableon workflowable models for links back to business resources. - Bind
PermissionAssigneeOptionsResolverso definition editors show labels for your Core assignee resolver keys. - 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()whilehasRunningWorkflow()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
- First Workflow — hands-on refund example
- Code-defined Workflows — JSON schema and assignees
- Approve and Reject — task API and audit logs
- Testing Workflows — PHPUnit patterns