Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"require": {
"php": "^8.1",
"laravel/framework": "^9.0|^10.0|^11.0|^12.0",
"spatie/laravel-model-states": "^2.0",
"react/promise": "^2.9|^3.0"
},
"require-dev": {
Expand Down
3 changes: 2 additions & 1 deletion src/ChildWorkflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Workflow\Exceptions\TransitionNotFound;
use Workflow\Middleware\WithoutOverlappingMiddleware;
use Workflow\Models\StoredWorkflow;

Expand Down Expand Up @@ -62,7 +63,7 @@ public function handle()
} else {
$workflow->next($this->index, $this->now, $this->storedWorkflow->class, $this->return);
}
} catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) {
} catch (TransitionNotFound) {
if ($workflow->running()) {
$this->release();
}
Expand Down
3 changes: 2 additions & 1 deletion src/ChildWorkflowStub.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use React\Promise\Deferred;
use React\Promise\PromiseInterface;
use function React\Promise\resolve;
use Workflow\Exceptions\TransitionNotFound;
use Workflow\Serializers\Serializer;

final class ChildWorkflowStub
Expand Down Expand Up @@ -69,7 +70,7 @@ public static function make($workflow, ...$arguments): PromiseInterface
if ($childWorkflow->running() && ! $childWorkflow->created()) {
try {
$childWorkflow->resume();
} catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) {
} catch (TransitionNotFound) {
// already running
}
} elseif (! $childWorkflow->completed()) {
Expand Down
3 changes: 2 additions & 1 deletion src/Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Workflow\Exceptions\TransitionNotFound;
use Workflow\Middleware\WithoutOverlappingMiddleware;
use Workflow\Models\StoredWorkflow;

Expand Down Expand Up @@ -55,7 +56,7 @@ public function handle()
} else {
$workflow->next($this->index, $this->now, self::class, $this->exception);
}
} catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) {
} catch (TransitionNotFound) {
if ($workflow->running()) {
$this->release();
}
Expand Down
42 changes: 42 additions & 0 deletions src/Exceptions/TransitionNotFound.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Workflow\Exceptions;

use RuntimeException;

final class TransitionNotFound extends RuntimeException
{
private string $from;

private string $to;

private string $modelClass;

public static function make(string $from, string $to, string $modelClass): self
{
$exception = new self("Transition from `{$from}` to `{$to}` on model `{$modelClass}` was not found.");

$exception->from = $from;
$exception->to = $to;
$exception->modelClass = $modelClass;

return $exception;
}

public function getFrom(): string
{
return $this->from;
}

public function getTo(): string
{
return $this->to;
}

public function getModelClass(): string
{
return $this->modelClass;
}
}
3 changes: 2 additions & 1 deletion src/Middleware/ActivityMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Workflow\Events\ActivityCompleted;
use Workflow\Events\ActivityFailed;
use Workflow\Events\ActivityStarted;
use Workflow\Exceptions\TransitionNotFound;

final class ActivityMiddleware
{
Expand Down Expand Up @@ -73,7 +74,7 @@ public function onUnlock(bool $shouldSignal): void
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $throwable) {
$this->job->storedWorkflow->toWorkflow()
->fail($throwable);
} catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) {
} catch (TransitionNotFound) {
if ($this->job->storedWorkflow->toWorkflow()->running()) {
$this->job->release();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Models/StoredWorkflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Arr;
use Spatie\ModelStates\HasStates;
use Workflow\States\HasStates;
use Workflow\States\WorkflowContinuedStatus;
use Workflow\States\WorkflowStatus;
use Workflow\WorkflowMetadata;
Expand Down
3 changes: 2 additions & 1 deletion src/Signal.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Workflow\Exceptions\TransitionNotFound;
use Workflow\Middleware\WithoutOverlappingMiddleware;
use Workflow\Models\StoredWorkflow;

Expand Down Expand Up @@ -56,7 +57,7 @@ public function handle(): void

try {
$workflow->resume();
} catch (\Spatie\ModelStates\Exceptions\TransitionNotFound) {
} catch (TransitionNotFound) {
if ($workflow->running()) {
$this->release();
}
Expand Down
142 changes: 142 additions & 0 deletions src/States/HasStates.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

declare(strict_types=1);

namespace Workflow\States;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

trait HasStates
{
public static function bootHasStates(): void
{
self::creating(static function ($model): void {
$model->setStateDefaults();
});
}

public function initializeHasStates(): void
{
$this->setStateDefaults();
}

public static function getStates(): Collection
{
$model = static::make();

return collect($model->getStateConfigs())
->map(static function (StateConfig $stateConfig) {
return $stateConfig->baseStateClass::getStateMapping()->keys();
});
}

public static function getDefaultStates(): Collection
{
$model = static::make();

return collect($model->getStateConfigs())
->map(static function (StateConfig $stateConfig) {
$defaultStateClass = $stateConfig->defaultStateClass;

if ($defaultStateClass === null) {
return null;
}

return $defaultStateClass::getMorphClass();
});
}

public static function getDefaultStateFor(string $fieldName): ?string
{
return static::getDefaultStates()[$fieldName] ?? null;
}

public static function getStatesFor(string $fieldName): Collection
{
return collect(static::getStates()[$fieldName] ?? []);
}

public function scopeWhereState(Builder $builder, string $column, $states): Builder
{
$states = Arr::wrap($states);
$field = Str::afterLast($column, '.');

return $builder->whereIn($column, $this->getStateNamesForQuery($field, $states));
}

public function scopeWhereNotState(Builder $builder, string $column, $states): Builder
{
$states = Arr::wrap($states);
$field = Str::afterLast($column, '.');

return $builder->whereNotIn($column, $this->getStateNamesForQuery($field, $states));
}

public function scopeOrWhereState(Builder $builder, string $column, $states): Builder
{
$states = Arr::wrap($states);
$field = Str::afterLast($column, '.');

return $builder->orWhereIn($column, $this->getStateNamesForQuery($field, $states));
}

public function scopeOrWhereNotState(Builder $builder, string $column, $states): Builder
{
$states = Arr::wrap($states);
$field = Str::afterLast($column, '.');

return $builder->orWhereNotIn($column, $this->getStateNamesForQuery($field, $states));
}

/**
* @return array<string, StateConfig>
*/
private function getStateConfigs(): array
{
$states = [];

foreach ($this->getCasts() as $field => $state) {
if (! is_subclass_of($state, State::class)) {
continue;
}

$states[$field] = $state::config();
}

return $states;
}

private function getStateNamesForQuery(string $field, array $states): Collection
{
$stateConfig = $this->getStateConfigs()[$field] ?? null;

if ($stateConfig === null) {
return collect([]);
}

return $stateConfig->baseStateClass::getStateMapping()
->filter(static function (string $className, string $morphName) use ($states) {
return in_array($className, $states, true)
|| in_array($morphName, $states, true);
})
->keys();
}

private function setStateDefaults(): void
{
foreach ($this->getStateConfigs() as $field => $stateConfig) {
if ($this->{$field} !== null) {
continue;
}

if ($stateConfig->defaultStateClass === null) {
continue;
}

$this->{$field} = $stateConfig->defaultStateClass;
}
}
}
Loading