PHPackages                             helvetitec/flowengine - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. helvetitec/flowengine

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

helvetitec/flowengine
=====================

Statemachine bots for conversations in Laravel

v1.0.1(1mo ago)00PHP

Since Mar 23Pushed 1mo agoCompare

[ Source](https://github.com/Helvetitec/flowengine)[ Packagist](https://packagist.org/packages/helvetitec/flowengine)[ RSS](/packages/helvetitec-flowengine/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (2)Versions (3)Used By (0)

FlowEngine
==========

[](#flowengine)

A simple, flexible state machine engine for Laravel to handle dynamic flows like chats, onboarding, workflows, and more.

🚀 Concept
---------

[](#-concept)

FlowEngine allows you to define **state-driven flows** where each subject (e.g. a chat, user, or process) moves through states based on input.

Typical flow:

1. Receive input
2. Process current state
3. Transition to next state
4. Persist state + context
5. Stop execution
6. Resume later (via new input or cooldown)

🧱 Core Components
-----------------

[](#-core-components)

### FlowEngine

[](#flowengine-1)

The base class that handles execution:

```
abstract class FlowEngine
{
    final public function run(FlowSubject $subject, mixed $input): void;

    abstract protected function doRun(mixed $input): void;

    final protected function stop(bool $persist = true): never;
}
```

### FlowSubject

[](#flowsubject)

Any model that participates in a flow must implement:

```
interface FlowSubject
{
    public function getStateKey(): string;
    public function setStateKey(string $state): void;

    public function getContext(): array;
    public function setContext(array $context): void;

    public function getCooldown(): Carbon;
    public function setCooldown(Carbon $until): void;

    public function persist(): void;
}
```

⚡ Example Implementation
------------------------

[](#-example-implementation)

### 1.1. Subject (e.g. Chat)

[](#11-subject-eg-chat)

If you only want to use one flow at a time.

```
class Chat extends Model implements FlowSubject
{
    use HasFlow;

    protected $casts = [
        'context' => 'array',
        'cooldown_until' => 'datetime',
    ];

    public function getActive(): bool
    {
        return $this->active;
    }

    public function getStateKey(): string
    {
        return $this->state_key;
    }

    public function setStateKey(string $state): void
    {
        $this->state_key = $state;
    }

    public function getContext(): array
    {
        return $this->context ?? [];
    }

    public function setContext(array $context): void
    {
        $this->context = $context;
    }

    public function getCooldown(): ?Carbon
    {
        return $this->cooldown_until;
    }

    public function setCooldown(Carbon $until): void
    {
        $this->cooldown_until = $until;
    }

    public function persist(): void
    {
        $this->save();
    }
}
```

### 1.2. FlowRun (Recomended)

[](#12-flowrun-recomended)

If you want you can use the FlowRuns which would allow multiple FlowEngines running at the same time.

**Important:** If you use this, please run php artisan vendor:publish --tag="helvetitec.flowengine.migrations"

```
//Add to your Subject (e.g Chat)
use HasFlowRuns;
```

### 2. Flow

[](#2-flow)

```
class ChatFlow extends FlowEngine
{
    protected function doRun(mixed $input): void
    {
        $state = $this->subject->getStateKey();

        match ($state) {
            'start' => $this->start(),
            'waiting' => $this->handleAnswer($input),
            default => $this->start(),
        };
    }

    protected function start(): void
    {
        ChatService::send($this->subject, "Choose 1 or 2");

        $this->transition('waiting')
             ->set('options', [1,2]) //Sets the context for the flow
             ->stop();
    }

    protected function handleAnswer($input): void
    {
        $options = $this->get('options');
        if(!in_array($input, $options)){
            ChatService::send($this->subject, "Invalid input");
            $this->stop();
            return;
        }
        ChatService::send($this->subject, "You chose: {$input}");

        $this->transition('done')
             ->cooldown(now()->addMinutes(5))
             ->stop();
    }
}
```

### 3. Triggering the Flow

[](#3-triggering-the-flow)

```
app(ChatFlow::class)->run($chat, $message);
```

or

```
//With use HasFlow;
$chat->runFlow($message);
```

or

```
//With use HasFlowRuns;
$chat->runFlow(ChatFlow::class, $message, $force);
```

You typically call this from:

- Controllers
- Jobs
- Event listeners
- Webhooks

🔁 Flow Lifecycle
----------------

[](#-flow-lifecycle)

```
Input → run() → doRun()
        ↓
   state logic
        ↓
 transition()
 set()
 cooldown()
        ↓
     stop()
        ↓
   persist()

```

🧩 Helper Methods
----------------

[](#-helper-methods)

### Transition state

[](#transition-state)

```
$this->transition('next_state');
```

### Store data in context

[](#store-data-in-context)

```
$this->set('key', 'value');

$value = $this->get('key');
```

### Cooldown

[](#cooldown)

```
$this->cooldown(now()->addMinutes(10));
```

### Stop execution

[](#stop-execution)

```
$this->stop(); //persists
```

or

```
$this->stop(persist: false); //does not persist
```

⚠️ Important Rules
------------------

[](#️-important-rules)

### 1. Always use `run()`

[](#1-always-use-run)

```
// ✅ Correct
app(MyFlow::class)->run($subject, $input);

// ❌ Wrong
$flow->doRun($input);
```

### 2. Always stop after sending output

[](#2-always-stop-after-sending-output)

```
$this->transition('next')
     ->stop();
```

### 3. Do not persist manually

[](#3-do-not-persist-manually)

Persistence is handled automatically by the engine.

### 4. Use state keys as strings

[](#4-use-state-keys-as-strings)

```
'start'
'waiting_for_input'
'completed'
```

📄 AI Usage
----------

[](#-ai-usage)

AI was used to create this readme file and for smaller parts of the code to make it cleaner.

###  Health Score

35

—

LowBetter than 79% of packages

Maintenance97

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2f366058ff9e06fad5c432234f0ac35a154bcc4153dbff096987489b16e87eb7?d=identicon)[daredloco](/maintainers/daredloco)

---

Top Contributors

[![daredloco](https://avatars.githubusercontent.com/u/60240491?v=4)](https://github.com/daredloco "daredloco (17 commits)")

###  Code Quality

Static AnalysisPHPStan

### Embed Badge

![Health badge](/badges/helvetitec-flowengine/health.svg)

```
[![Health](https://phpackages.com/badges/helvetitec-flowengine/health.svg)](https://phpackages.com/packages/helvetitec-flowengine)
```

###  Alternatives

[naxeem/thaana-transliterator

Thaana transliterator

202.5k](/packages/naxeem-thaana-transliterator)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
