Skip to content

Commit 403d541

Browse files
committed
Add Laravel 12 support
1 parent 7f8bb6e commit 403d541

12 files changed

+324
-16
lines changed

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
}
2222
],
2323
"require": {
24-
"php": "^8.2|^8.3",
25-
"illuminate/contracts": "^10.0||^11.0",
24+
"php": "^8.2|^8.3|^8.4",
25+
"illuminate/contracts": "^10.0||^11.0||^12.0",
2626
"spatie/laravel-package-tools": "^1.16",
2727
"symfony/expression-language": "^7.1",
28-
"zerodahero/laravel-workflow": "^6.0.0"
28+
"zerodahero/laravel-workflow": "^6.1"
2929
},
3030
"require-dev": {
3131
"larastan/larastan": "^2.9",

config/workflow-process.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,22 @@
22

33
// config for Soap/LaravelWorkflowProcess
44
return [
5+
'custom_functions' => [
6+
// 'function_name' => [
7+
// 'compiler' => function () { return 'true'; },
8+
// 'evaluator' => function (array $variables) { return true; },
9+
// ],
10+
// 'function_name' => 'App\CustomGuardFunction',
11+
// 'authenicated' => \Soap\LaravelWorkflowProcess\GuardFunctions\Authenticated::class,
512

13+
'authenticated' => [
14+
'compiler' => function ($guard = 'web') {
15+
return sprintf('authenticated("%s")', $guard);
16+
},
17+
'evaluator' => function (array $variables, $guard = 'web') {
18+
// This allows checking a specific guard (e.g., 'web', 'api', etc.)
19+
return auth()->guard($guard)->check();
20+
},
21+
],
22+
],
623
];

it

Whitespace-only changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Soap\LaravelWorkflowProcess\Contracts;
4+
5+
interface GuardFunctionInterface
6+
{
7+
/**
8+
* Compile the function. Used by the ExpressionLanguage compiler.
9+
* This is the function that will be called when the expression is compiled.
10+
* It is used for caching the compiled expression.
11+
*
12+
* @param mixed ...$args
13+
*/
14+
public function compile(...$args): string;
15+
16+
/**
17+
* Evaluate the function. Executed during the evaluation.
18+
*
19+
* @param mixed ...$args
20+
* @return mixed
21+
*/
22+
public function evaluate(array $variables, ...$args);
23+
}

src/GuardEvaluator.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Soap\LaravelWorkflowProcess;
4+
5+
use Soap\LaravelWorkflowProcess\Contracts\GuardFunctionInterface;
6+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
7+
8+
class GuardEvaluator
9+
{
10+
protected $expressionLanguage;
11+
12+
public function __construct(ExpressionLanguage $expressionLanguage)
13+
{
14+
$this->expressionLanguage = $expressionLanguage;
15+
$this->registerCustomFunctions();
16+
}
17+
18+
protected function registerCustomFunctions()
19+
{
20+
$customFunctions = config('workflow.custom_functions', []);
21+
22+
foreach ($customFunctions as $name => $definition) {
23+
if (is_string($definition) && class_exists($definition)) {
24+
// Instantiate the class via Laravel's container.
25+
$instance = app($definition);
26+
if ($instance instanceof GuardFunctionInterface) {
27+
$compiler = [$instance, 'compile'];
28+
$evaluator = [$instance, 'evaluate'];
29+
} else {
30+
throw new \Exception("Class {$definition} must implement GuardFunctionInterface.");
31+
}
32+
} elseif (is_array($definition)) {
33+
$compiler = $definition['compiler'] ?? function () {
34+
return 'true';
35+
};
36+
$evaluator = $definition['evaluator'] ?? function (array $variables) {
37+
return true;
38+
};
39+
} else {
40+
continue; // Skip invalid definitions.
41+
}
42+
43+
$this->expressionLanguage->register($name, $compiler, $evaluator);
44+
}
45+
}
46+
47+
public function evaluate(string $expression, array $variables = [])
48+
{
49+
return $this->expressionLanguage->evaluate($expression, $variables);
50+
}
51+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Soap\LaravelWorkflowProcess\GuardFunctions;
4+
5+
use Soap\LaravelWorkflowProcess\Contracts\GuardFunctionInterface;
6+
7+
class Authenticated implements GuardFunctionInterface
8+
{
9+
public function compile(...$args): string
10+
{
11+
// Compiler returns a placeholder.
12+
$guard = $args[0] ?? 'web';
13+
14+
return sprintf('authenticated("%s")', $guard);
15+
}
16+
17+
public function evaluate(array $variables, ...$args)
18+
{
19+
// Get the guard name, defaulting to 'web'
20+
$guard = $args[0] ?? 'web';
21+
22+
return auth()->guard($guard)->check();
23+
}
24+
}

src/LaravelWorkflowProcessServiceProvider.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ public function configurePackage(Package $package): void
2424
->hasCommand(LaravelWorkflowProcessCommand::class);
2525
}
2626

27+
public function registeringPackage()
28+
{
29+
$this->app->singleton(GuardEvaluator::class, function ($app) {
30+
return new GuardEvaluator(new \Symfony\Component\ExpressionLanguage\ExpressionLanguage);
31+
});
32+
}
33+
2734
public function packageBooted()
2835
{
2936
Event::subscribe(WorkflowGuardSubscriber::class);

src/Listeners/WorkflowGuardSubscriber.php

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,26 @@
22

33
namespace Soap\LaravelWorkflowProcess\Listeners;
44

5-
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
5+
use Soap\LaravelWorkflowProcess\GuardEvaluator;
66
use ZeroDaHero\LaravelWorkflow\Events\GuardEvent;
77

88
class WorkflowGuardSubscriber
99
{
10+
/**
11+
* The guard evaluator instance.
12+
*
13+
* @var GuardEvaluator
14+
*/
15+
protected $guardEvaluator;
16+
17+
/**
18+
* Create a new subscriber instance.
19+
*/
20+
public function __construct(GuardEvaluator $guardEvaluator)
21+
{
22+
$this->guardEvaluator = $guardEvaluator;
23+
}
24+
1025
/**
1126
* Handle the event.
1227
*/
@@ -15,16 +30,27 @@ public function handleOnGuard(GuardEvent $event): void
1530
// This is a call by using event proxy to Symfony GuardEvent
1631
$subject = $event->getSubject();
1732
$transition = $event->getTransition();
33+
1834
$workflow = $event->getWorkflow();
1935
$metaData = $workflow->getMetadataStore()->getTransitionMetadata($transition);
36+
2037
if (isset($metaData['guard'])) {
21-
$guard = $metaData['guard'];
22-
$expressionLanguage = new ExpressionLanguage;
23-
$result = $expressionLanguage->evaluate($guard, [
24-
'authenticated' => auth()->check(),
25-
'subject' => $subject,
26-
'user' => auth()->user(),
27-
]);
38+
$guardExpression = $metaData['guard'];
39+
// Prepare variables to pass to the evaluator.
40+
// You can pass the workflow subject, user, or any other required objects.
41+
$variables = [
42+
'subject' => $event->getSubject(),
43+
];
44+
45+
// Optionally include the authenticated user.
46+
if (auth()->check()) {
47+
$variables['user'] = auth()->user();
48+
}
49+
50+
// Evaluate the guard expression using the GuardEvaluator.
51+
$result = $this->guardEvaluator->evaluate($guardExpression, $variables);
52+
53+
// If the expression evaluates to false, block the transition.
2854
if (! $result) {
2955
$event->setBlocked(true, 'Guard blocked');
3056
}

tests/ExampleTest.php

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/GuardEvaluatorTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
use Soap\LaravelWorkflowProcess\GuardEvaluator;
4+
use Soap\LaravelWorkflowProcess\Tests\Stubs\CheckFlagGuardFunction;
5+
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
6+
7+
it('evaluates a custom closure guard function correctly', function () {
8+
// Set a test configuration for custom guard functions.
9+
// Here we define a function named "checkFlag" that returns the value of a 'flag' variable.
10+
config()->set('workflow.custom_functions', [
11+
'checkFlag' => [
12+
'compiler' => function () {
13+
// The compiler returns a placeholder expression.
14+
return 'true';
15+
},
16+
'evaluator' => function (array $variables) {
17+
// Evaluate by returning the 'flag' variable or false if not set.
18+
return $variables['flag'] ?? false;
19+
},
20+
],
21+
]);
22+
23+
// Instantiate the ExpressionLanguage and GuardEvaluator.
24+
$expressionLanguage = new ExpressionLanguage;
25+
$guardEvaluator = new GuardEvaluator($expressionLanguage);
26+
27+
// Evaluate the expression with 'flag' set to true.
28+
$resultTrue = $guardEvaluator->evaluate('checkFlag()', ['flag' => true]);
29+
expect($resultTrue)->toBeTrue();
30+
31+
// Evaluate the expression with 'flag' set to false.
32+
$resultFalse = $guardEvaluator->evaluate('checkFlag()', ['flag' => false]);
33+
expect($resultFalse)->toBeFalse();
34+
});
35+
36+
it('evaluates a custom guard function defined as a class correctly', function () {
37+
// Set the configuration so that the custom function points to our evaluator class.
38+
config()->set('workflow.custom_functions', [
39+
'checkFlag' => CheckFlagGuardFunction::class,
40+
]);
41+
42+
// Instantiate the ExpressionLanguage and GuardEvaluator.
43+
$expressionLanguage = new ExpressionLanguage;
44+
$guardEvaluator = new GuardEvaluator($expressionLanguage);
45+
46+
// Evaluate the expression with 'flag' set to true.
47+
$resultTrue = $guardEvaluator->evaluate('checkFlag()', ['flag' => true]);
48+
expect($resultTrue)->toBeTrue();
49+
50+
// Evaluate the expression with 'flag' set to false.
51+
$resultFalse = $guardEvaluator->evaluate('checkFlag()', ['flag' => false]);
52+
expect($resultFalse)->toBeFalse();
53+
});

0 commit comments

Comments
 (0)