{"id":47968555,"url":"https://github.com/dereuromark/cakephp-workflow","last_synced_at":"2026-05-23T13:04:02.618Z","repository":{"id":347642529,"uuid":"1194728353","full_name":"dereuromark/cakephp-workflow","owner":"dereuromark","description":"Batteries-included state machine plugin for CakePHP with PHP 8 Attributes, YAML config, audit trails, and visual admin dashboard","archived":false,"fork":false,"pushed_at":"2026-05-21T17:26:33.000Z","size":341,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-05-21T22:40:42.353Z","etag":null,"topics":["cakephp","cakephp-plugin","finite-state-machine","graph","php","statemachine","transitions","workflow"],"latest_commit_sha":null,"homepage":"https://dereuromark.github.io/cakephp-workflow/","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dereuromark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-28T18:27:51.000Z","updated_at":"2026-05-21T17:26:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dereuromark/cakephp-workflow","commit_stats":null,"previous_names":["dereuromark/cakephp-workflow"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/dereuromark/cakephp-workflow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dereuromark%2Fcakephp-workflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dereuromark%2Fcakephp-workflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dereuromark%2Fcakephp-workflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dereuromark%2Fcakephp-workflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dereuromark","download_url":"https://codeload.github.com/dereuromark/cakephp-workflow/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dereuromark%2Fcakephp-workflow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33396576,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cakephp","cakephp-plugin","finite-state-machine","graph","php","statemachine","transitions","workflow"],"created_at":"2026-04-04T10:40:46.148Z","updated_at":"2026-05-23T13:04:02.613Z","avatar_url":"https://github.com/dereuromark.png","language":"PHP","funding_links":[],"categories":["Plugins"],"sub_categories":["Miscellaneous"],"readme":"# CakePHP Workflow Plugin\n\n[![CI](https://github.com/dereuromark/cakephp-workflow/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/dereuromark/cakephp-workflow/actions/workflows/ci.yml?query=branch%3Amaster)\n[![codecov](https://img.shields.io/codecov/c/github/dereuromark/cakephp-workflow/master.svg)](https://codecov.io/gh/dereuromark/cakephp-workflow)\n[![Latest Stable Version](https://poser.pugx.org/dereuromark/cakephp-workflow/v/stable.svg)](https://packagist.org/packages/dereuromark/cakephp-workflow)\n[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%208.2-8892BF.svg)](https://php.net/)\n[![License](https://poser.pugx.org/dereuromark/cakephp-workflow/license.svg)](LICENSE)\n[![Coding Standards](https://img.shields.io/badge/cs-PhpCollective-purple.svg?style=flat-square)](https://github.com/php-collective/code-sniffer)\n\nThis branch is for **CakePHP 5.2+**. See [version map](https://github.com/dereuromark/cakephp-workflow/wiki#cakephp-version-map) for details.\n\nState machine and workflow engine for CakePHP with PHP 8 Attributes, YAML/NEON config support, and admin UI.\n\n\u003e [!TIP]\n\u003e Try the live demo: \u003chttps://sandbox.dereuromark.de/workflow-sandbox\u003e\n\n## Requirements\n\n- CakePHP 5.2+\n\n## Installation\n\n```bash\ncomposer require dereuromark/cakephp-workflow\n```\n\nLoad the plugin:\n\n```bash\nbin/cake plugin load Workflow\n```\n\nRun migrations:\n\n```bash\nbin/cake migrations migrate --plugin Workflow\n```\n\nThe generic `entity_id` column is polymorphic and defaults to `integer`; set the shared\n`Polymorphic.type` key to `biginteger` for large-id apps, or `uuid` / `binaryuuid` for\nnon-integer keys. **UUID / char primary keys are fully supported** — no code changes needed. See\n[Installation: Entity id type](https://dereuromark.github.io/cakephp-workflow/guide/installation#using-uuid-char-primary-keys).\n\n## Configuration\n\nConfigure the plugin in your `config/app.php`:\n\n```php\n'Workflow' =\u003e [\n    'adminAccess' =\u003e function (\\Cake\\Http\\ServerRequest $request): bool {\n        $identity = $request-\u003egetAttribute('identity');\n\n        return $identity !== null \u0026\u0026 in_array('admin', (array)$identity-\u003eroles, true);\n    },\n    'loader' =\u003e [\n        'namespaces' =\u003e [\n            'App\\\\Workflow',\n        ],\n        'configPath' =\u003e CONFIG . 'workflows' . DS,\n    ],\n    'logging' =\u003e true,\n    'locking' =\u003e true,\n    'timeouts' =\u003e true,\n    'lockDuration' =\u003e 30,\n    'adminBackUrl' =\u003e ['plugin' =\u003e false, 'controller' =\u003e 'Dashboard', 'action' =\u003e 'index'],\n    'adminActorResolver' =\u003e function (string $userId, ?\\Workflow\\Model\\Entity\\WorkflowTransition $transition = null): string {\n        return 'Admin #' . $userId;\n    },\n],\n```\n\nThe admin UI is fail-closed by default: you must provide `Workflow.adminAccess`\nto expose `/admin/workflow/...`. Manual admin actions also record actor and\ncontext metadata; when you still rely on legacy session auth, the admin logger\nfalls back to `Auth.User.id` automatically.\n\n## Defining Workflows\n\n### Using PHP 8 Attributes (Recommended)\n\nCreate state classes in your namespace:\n\n```php\n\u003c?php\nnamespace App\\Workflow\\Order;\n\nuse Workflow\\Attribute\\StateMachine;\nuse Workflow\\State\\AbstractState;\n\n#[StateMachine(name: 'order', table: 'Orders', field: 'state')]\nabstract class OrderState extends AbstractState\n{\n}\n```\n\n```php\n\u003c?php\nnamespace App\\Workflow\\Order;\n\nuse Workflow\\Attribute\\Command;\nuse Workflow\\Attribute\\FinalState;\nuse Workflow\\Attribute\\Guard;\nuse Workflow\\Attribute\\InitialState;\nuse Workflow\\Attribute\\Transition;\n\n#[InitialState]\n#[Transition(to: PaidState::class, name: 'pay', happy: true)]\nclass PendingState extends OrderState\n{\n    #[Guard('pay')]\n    public function ensurePayable(): bool|string\n    {\n        return (float)$this-\u003egetEntity()?-\u003eget('total') \u003e 0\n            ? true\n            : 'Order total must be positive';\n    }\n\n    #[Command('pay')]\n    public function markPaymentCaptured(): void\n    {\n        $this-\u003egetEntity()?-\u003eset('payment_captured', true);\n    }\n}\n```\n\n```php\n\u003c?php\nnamespace App\\Workflow\\Order;\n\nuse Workflow\\Attribute\\FinalState;\nuse Workflow\\Attribute\\OnEnter;\n\n#[FinalState]\nclass PaidState extends OrderState\n{\n    #[OnEnter]\n    public function sendReceipt(): void\n    {\n        // Runs after the entity enters the paid state.\n    }\n}\n```\n\n### Using NEON or YAML\n\nInstall the optional parser you want:\n\n- NEON: `composer require nette/neon`\n- YAML: `composer require symfony/yaml`\n\nCreate workflow files in `config/workflows/`:\n\n```neon [config/workflows/order.neon]\norder:\n  table: Orders\n  field: state\n  states:\n    pending:\n      initial: true\n    paid:\n      color: '#00AA00'\n    completed:\n      final: true\n  transitions:\n    pay:\n      from: [pending]\n      to: paid\n      happy: true\n    complete:\n      from: [paid]\n      to: completed\n```\n\n## Using the Workflow\n\nAdd the behavior to your table:\n\n```php\npublic function initialize(array $config): void\n{\n    $this-\u003eaddBehavior('Workflow.Workflow', [\n        'workflow' =\u003e 'order',\n    ]);\n}\n```\n\nApply transitions:\n\n```php\n$behavior = $this-\u003eOrders-\u003egetBehavior('Workflow');\n\nif ($behavior-\u003ecanTransition($order, 'pay')) {\n    // Atomic: applies transition, saves entity, logs - all in one transaction\n    $result = $behavior-\u003etransition($order, 'pay', ['user_id' =\u003e $userId]);\n}\n```\n\nSee the [documentation](https://dereuromark.github.io/cakephp-workflow/) for the full API.\n\n## Drift Safety\n\nChanging a workflow while records exist can leave records in a state that no\nlonger exists. This is handled out of the box, with no configuration:\n\n- **Graceful degradation:** orphaned records never crash reads, display, or the\n  admin UI — they render as a neutral \"unknown\" state, and transitioning one\n  returns a clear blocked result.\n- **Detection:** the admin Orphans view (`/admin/workflow/orphans`) and\n  `workflow validate --check-data` list records whose state is no longer defined.\n- **Remediation:** move them forward interactively, or headlessly:\n\n```bash\nbin/cake workflow validate order --check-data          # report orphaned records\nbin/cake workflow migrate order --map legacy:pending   # move orphaned records forward\n```\n\nSee [Drift Safety](https://dereuromark.github.io/cakephp-workflow/guide/versioning) for details.\n\n## CLI Commands\n\n```bash\nbin/cake workflow init order Orders    # Scaffold new workflow\nbin/cake workflow list                 # List all workflows\nbin/cake workflow show order           # Show workflow details\nbin/cake workflow validate             # Validate definitions\nbin/cake workflow migrate order        # Move orphaned records to valid states\n```\n\n## Features\n\n- PHP 8 Attributes or NEON/YAML definitions\n- Guards, commands, and lifecycle callbacks\n- Audit logging with user tracking\n- Pessimistic locking for concurrent transitions\n- Automatic timeouts\n- Drift safety: orphaned records never crash, with detection and forward migration\n- Admin UI with Mermaid.js diagrams\n- CLI tools for management and validation\n\n## Documentation\n\nFull documentation: https://dereuromark.github.io/cakephp-workflow/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdereuromark%2Fcakephp-workflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdereuromark%2Fcakephp-workflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdereuromark%2Fcakephp-workflow/lists"}