Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the new State Machine abstraction #15738

Merged
merged 13 commits into from
Jan 31, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci_e2e-mariadb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
behat-no-js:
needs: get-matrix
runs-on: ubuntu-latest
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}"
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MariaDB ${{ matrix.mariadb }}, State Machine Adapter ${{ matrix.state_machine_adapter }}"
timeout-minutes: 45
strategy:
fail-fast: false
Expand All @@ -53,6 +53,7 @@ jobs:
env:
APP_ENV: test_cached
DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?charset=utf8mb4&serverVersion=mariadb-${{ matrix.mariadb }}"
TEST_SYLIUS_STATE_MACHINE_ADAPTER: "${{ matrix.state_machine_adapter }}"

steps:
- name: Set variables
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
{
"php": "8.0",
"symfony": "^5.4",
"mariadb": "10.4.10"
"mariadb": "10.4.10",
"state_machine_adapter": "winzou_state_machine"
},
{
"php": "8.2",
"symfony": "^6.4",
"mariadb": "10.4.10",
"dbal": "^3.0"
"dbal": "^3.0",
"state_machine_adapter": "symfony_workflow"
}
]
},
Expand Down Expand Up @@ -98,6 +100,7 @@
"php": [ "8.0", "8.1", "8.2" ],
"symfony": [ "^5.4", "^6.4" ],
"mariadb": [ "10.4.10" ],
"state_machine_adapter": [ "winzou_state_machine", "symfony_workflow" ],
"exclude": [
{
"php": "8.0",
Expand Down
30 changes: 24 additions & 6 deletions UPGRADE-1.13.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# UPGRADE FROM `v1.12.X` TO `v1.13.0`

1. Starting with Sylius `1.13` we provided a possibility to use the Symfony Workflow as your State Machine. To allow a smooth transition
we created a new package called `sylius/state-machine-abstraction`,
which provides a configurable abstraction,
allowing you to define which adapter should be used (Winzou State Machine or Symfony Workflow) per graph.
Starting with Sylius `1.13` we configure all existing Sylius' graphs to use the Symfony Workflow.
At the same time, all other graphs are using Winzou State Machine as a default state machine,
and must be configured manually to use the Symfony Workflow.

Configuration:
```yaml
sylius_state_machine_abstraction:
graphs_to_adapters_mapping:
<graph_name>: <adapter_name>
# e.g.
sylius_order_checkout: symfony_workflow # available adapters: symfony_workflow, winzou_state_machine

# we can also can the default adapter
default_adapter: symfony_workflow # winzou_state_machine is a default value here
```

> **Note!** Starting with Sylius `2.0` the Symfony Workflow will be the default state machine for all graphs.

1. A new parameter has been added to specify the validation groups for given gateway factory.
If you have any custom validation groups for your factory, you need to add them to your `config/packages/_sylius.yaml` file.
Also, if you have your own gateway factory and want to add your validation groups you can add another entry to the `validation_groups` configuration node.
Expand All @@ -19,7 +41,7 @@
- 'your_custom_validation_group'
```

1. New parameters have been added to specify the validation groups for given shipping method rules and calculators.
1. There have been a naw parameters added to specify the validation groups for given shipping method rule and calculator.
If you have any custom validation groups for your calculator or rules, you need to add them to your `config/packages/_sylius.yaml` file.
Also, if you have your own shipping method rule or calculator and want to add your validation groups you can add another key to the `validation_groups` parameter.

Expand Down Expand Up @@ -58,7 +80,7 @@
1. Class `Sylius\Bundle\ProductBundle\Form\Type\ProductOptionChoiceType` has been deprecated.
Use `Sylius\Bundle\ProductBundle\Form\Type\ProductOptionAutocompleteType` instead.

1. Using `parentId` query parameter to generate the slug in `Sylius\Bundle\TaxonomyBundle\Controller\TaxonSlugController` has been deprecated.
1. Using `parentId` query parameter to generate slug in `Sylius\Bundle\TaxonomyBundle\Controller\TaxonSlugController` has been deprecated.
Use the `parentCode` query parameter instead.

1. Starting with Sylius 1.13, the `SyliusPriceHistoryPlugin` is included.
Expand All @@ -75,10 +97,6 @@

1. The name of the second argument of `Sylius\Bundle\AdminBundle\Action\ResendOrderConfirmationEmailAction` is deprecated and will be renamed to `$resendOrderConfirmationEmailDispatcher`.

1. Passing `Sylius\Bundle\AdminBundle\EmailManager\ShipmentEmailManagerInterface` to `Sylius\Bundle\AdminBundle\Action\ResendShipmentConfirmationEmailAction` as a second argument is deprecated, use `Sylius\Bundle\CoreBundle\MessageDispatcher\ResendShipmentConfirmationEmailDispatcherInterface` instead.

1. The name of the second argument of `Sylius\Bundle\AdminBundle\Action\ResendShipmentConfirmationEmailAction` is deprecated and will be renamed to `$resendShipmentConfirmationEmailDispatcher`.

1. Not passing `Sylius\Bundle\CoreBundle\CatalogPromotion\Announcer\CatalogPromotionRemovalAnnouncerInterface` to `Sylius\Bundle\CoreBundle\CatalogPromotion\Processor\CatalogPromotionRemovalProcessor`
as a second argument is deprecated.

Expand Down
15 changes: 15 additions & 0 deletions config/packages/test/_sylius.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
parameters:
test_default_state_machine_adapter: 'symfony_workflow'
test_sylius_state_machine_adapter: '%env(string:default:test_default_state_machine_adapter:TEST_SYLIUS_STATE_MACHINE_ADAPTER)%'

sylius_user:
encoder: plaintext

sylius_api:
enabled: true

sylius_state_machine_abstraction:
graphs_to_adapters_mapping:
sylius_catalog_promotion: '%test_sylius_state_machine_adapter%'
sylius_order: '%test_sylius_state_machine_adapter%'
sylius_order_checkout: '%test_sylius_state_machine_adapter%'
sylius_order_payment: '%test_sylius_state_machine_adapter%'
sylius_order_shipping: '%test_sylius_state_machine_adapter%'
sylius_payment: '%test_sylius_state_machine_adapter%'
sylius_product_review: '%test_sylius_state_machine_adapter%'
sylius_shipment: '%test_sylius_state_machine_adapter%'
2 changes: 0 additions & 2 deletions src/Sylius/Abstraction/StateMachine/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,12 @@

<service id="sylius_abstraction.state_machine.adapter.symfony_workflow" class="Sylius\Abstraction\StateMachine\SymfonyWorkflowAdapter">
<argument type="service" id="workflow.registry" />

<tag name="sylius.state_machine" key="symfony_workflow" />
</service>
<service id="Sylius\Abstraction\StateMachine\StateMachineInterface $symfonyWorkflow" alias="sylius_abstraction.state_machine.adapter.symfony_workflow" />

<service id="sylius_abstraction.state_machine.adapter.winzou_state_machine" class="Sylius\Abstraction\StateMachine\WinzouStateMachineAdapter">
<argument type="service" id="sm.factory" />

<tag name="sylius.state_machine" key="winzou_state_machine" />
</service>
<service id="Sylius\Abstraction\StateMachine\StateMachineInterface $winzouStateMachine" alias="sylius_abstraction.state_machine.adapter.winzou_state_machine" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ public function getEnabledTransitions(object $subject, string $graphName): array
return $this->getStateMachineAdapter($graphName)->getEnabledTransitions($subject, $graphName);
}

public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string
{
return $this->getStateMachineAdapter($graphName)->getTransitionFromState($subject, $graphName, $fromState);
}

public function getTransitionToState(object $subject, string $graphName, string $toState): ?string
{
return $this->getStateMachineAdapter($graphName)->getTransitionToState($subject, $graphName, $toState);
}

private function getStateMachineAdapter(string $graphName): StateMachineInterface
{
if (isset($this->graphsToAdaptersMapping[$graphName])) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,17 @@ final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('sylius_abstraction');
$treeBuilder = new TreeBuilder('sylius_state_machine_abstraction');
/** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode();

$rootNode
->addDefaultsIfNotSet()
->children()
->arrayNode('state_machine')
->addDefaultsIfNotSet()
->children()
->scalarNode('default_adapter')->defaultValue('winzou_state_machine')->end()
->arrayNode('graphs_to_adapters_mapping')
->useAttributeAsKey('graph_name')
->scalarPrototype()->end()
->end()
->end()
->scalarNode('default_adapter')->defaultValue('winzou_state_machine')->end()
->arrayNode('graphs_to_adapters_mapping')
->useAttributeAsKey('graph_name')
->scalarPrototype()->end()
->end()
->end()
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function load(array $configs, ContainerBuilder $container): void
$loader = new XmlFileLoader($container, new FileLocator(dirname(__DIR__, 2) . '/config/'));
$loader->load('services.xml');

$container->setParameter('sylius_abstraction.state_machine.default_adapter', $config['state_machine']['default_adapter']);
$container->setParameter('sylius_abstraction.state_machine.graphs_to_adapters_mapping', $config['state_machine']['graphs_to_adapters_mapping']);
$container->setParameter('sylius_abstraction.state_machine.default_adapter', $config['default_adapter']);
$container->setParameter('sylius_abstraction.state_machine.graphs_to_adapters_mapping', $config['graphs_to_adapters_mapping']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@

namespace Sylius\Abstraction\StateMachine\Exception;

class StateMachineExecutionException extends \Exception
class StateMachineExecutionException extends \RuntimeException
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@ public function apply(object $subject, string $graphName, string $transition, ar
* @return array<TransitionInterface>
*/
public function getEnabledTransitions(object $subject, string $graphName): array;

/**
* @throws StateMachineExecutionException
*/
public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string;

/**
* @throws StateMachineExecutionException
*/
public function getTransitionToState(object $subject, string $graphName, string $toState): ?string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,26 @@ function (SymfonyWorkflowTransition $transition): TransitionInterface {
$enabledTransitions,
);
}

public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string
{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getFroms() !== null && in_array($fromState, $transition->getFroms(), true)) {
return $transition->getName();
}
}

return null;
}

public function getTransitionToState(object $subject, string $graphName, string $toState): ?string
{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getTos() !== null && in_array($toState, $transition->getTos(), true)) {
return $transition->getName();
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,79 @@ public function apply(object $subject, string $graphName, string $transition, ar
}

public function getEnabledTransitions(object $subject, string $graphName): array
{
$stateMachine = $this->getStateMachine($subject, $graphName);

return array_filter(
$this->getAllTransitions($stateMachine),
fn (TransitionInterface $transition) => $this->can($subject, $graphName, $transition->getName()),
);
}

/**
* @return array<TransitionInterface>
*/
private function getAllTransitions(\SM\StateMachine\StateMachineInterface $stateMachine): array
{
try {
$transitions = $this->getStateMachine($subject, $graphName)->getPossibleTransitions();
} catch (SMException $exception) {
$transitionsConfig = $this->getConfig($stateMachine)['transitions'];
} catch (\ReflectionException $exception) {
throw new StateMachineExecutionException($exception->getMessage(), $exception->getCode(), $exception);
}

return array_map(
fn (string $transition) => new Transition($transition, null, null),
$transitions,
);
$transitions = [];

foreach ($transitionsConfig as $transitionName => $transitionConfig) {
$froms = $transitionConfig['from'];
$tos = array($transitionConfig['to']);
$transitions[] = new Transition($transitionName, $froms, $tos);
}

return $transitions;
}

/**
* @throws \ReflectionException
*
* @return array{transitions: array<string, array{from: array<string>, to: string}>}
*/
private function getConfig(\SM\StateMachine\StateMachineInterface $stateMachine): array
{
$reflection = new \ReflectionClass($stateMachine);
$configProperty = $reflection->getProperty('config');
$configProperty->setAccessible(true);

return $configProperty->getValue($stateMachine);
}

public function getTransitionFromState(object $subject, string $graphName, string $fromState): ?string
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public before private 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's discussable. I've talked with Janek about it, and we agreed we should all talk about it. I'm in favor of leaving it as-is, and eventually change it later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll discuss it further at a later date, either way, if we'd want to revert it and keep the public => protected => private order that'll be fixed along with adding an ecs rule.

{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getFroms() !== null && in_array($fromState, $transition->getFroms(), true)) {
return $transition->getName();
}
}

return null;
}

public function getTransitionToState(object $subject, string $graphName, string $toState): ?string
{
foreach ($this->getEnabledTransitions($subject, $graphName) as $transition) {
if ($transition->getTos() !== null && in_array($toState, $transition->getTos(), true)) {
return $transition->getName();
}
}

return null;
}

private function getStateMachine(object $subject, string $graphName): \SM\StateMachine\StateMachineInterface
{
return $this->winzouStateMachineFactory->get($subject, $graphName);
try {
return $this->winzouStateMachineFactory->get($subject, $graphName);
} catch (SMException $exception) {
throw new StateMachineExecutionException($exception->getMessage(), $exception->getCode(), $exception);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,13 @@ public function it_allows_to_configure_a_default_state_machine_adapter(): void
$this->assertProcessedConfigurationEquals(
[
[
'state_machine' => [
'default_adapter' => 'symfony_workflow',
],
'default_adapter' => 'symfony_workflow',
],
],
[
'state_machine' => [
'default_adapter' => 'symfony_workflow',
'graphs_to_adapters_mapping' => [],
],
'default_adapter' => 'symfony_workflow',
'graphs_to_adapters_mapping' => [],
],
'state_machine',
);
}

Expand All @@ -49,24 +44,19 @@ public function it_allows_to_configure_the_state_machines_adapters_mapping(): vo
$this->assertProcessedConfigurationEquals(
[
[
'state_machine' => [
'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine',
],
],
],
],
[
'state_machine' => [
'default_adapter' => 'winzou_state_machine',
'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine',
],
],
],
'state_machine',
[
'default_adapter' => 'winzou_state_machine',
'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine',
],
],
);
}

Expand Down