Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Workflow][RFC] Simplify workflow configuration #52378

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

Closed
lyrixx opened this issue Oct 31, 2023 · 10 comments · Fixed by #53866
Closed

[Workflow][RFC] Simplify workflow configuration #52378

lyrixx opened this issue Oct 31, 2023 · 10 comments · Fixed by #53866
Labels
RFC RFC = Request For Comments (proposals about features that you want to be discussed) Workflow

Comments

@lyrixx
Copy link
Member

lyrixx commented Oct 31, 2023

I have been thinking about this for a while, so it's time to open a RFC!

The workflow definition is redundant. You have to define the places twice:

  • in the places
  • in the transitions

Example:

framework:
    workflows:
        article:
            places:
                - first
                - last
            transitions:
                go:
                    from: first
                    to: last

I think we could simplify it to:

framework:
    workflows:
        article:
            transitions:
                go:
                    from: first
                    to: last

Indeed, all places could be guessed from the transitions froms et tos. If a
place is not reached by a transition, it become useless anyway.

Pros

  • Simpler configuration

Cons

  • If one made a typo between the place and the transitions transition, it's
    not caught anymore

WDYT ?

@lyrixx lyrixx added RFC RFC = Request For Comments (proposals about features that you want to be discussed) Workflow labels Oct 31, 2023
@alexandre-daubois
Copy link
Member

Big yes to me!

Would it make sense to still keep places ? I like the fact you can define places thanks to an array of constants for example, then define transitions specifically. Just wondering if this would be a removal of places or just if it wouldn't be required anymore 🙂

About the con, I'm not sure it would be a big deal. Of course it's always cool to have some double check, but I don't think this is necessary, other than being nice for the DX.

@lyrixx
Copy link
Member Author

lyrixx commented Oct 31, 2023

Would it make sense to still keep places ?

Not sure it's required anymore. But removing it required breaking the BC. (with BC layer etc, but still)

@hhamon
Copy link
Contributor

hhamon commented Oct 31, 2023

It would be great!

@javleds
Copy link
Contributor

javleds commented Nov 1, 2023

This sound great, if we require support I can create a PR to achieve this.

I have 2 possible approaches in mind:


  1. Infer the places based on the transitions in the Wofkflow/Definition.php always places is empty.
# Definition.php

public function __construct(array $places, array $transitions, $initialPlaces = null, MetadataStoreInterface $metadataStore = null)
{
    if (empty($places)) {
        $places = $this->inferPlacesFromTransitions($transitions);
    }
    
    foreach ($places as $place) {
        $this->addPlace($place);
    }
    //...
}
Pros:
  • The ones mentioned by @lyrixx
  • Retrocompatibility
  • We still have the oportunity to define the places in the case we require validations.
Cons:
  • This is kind of "magic" approach.

  1. We may add the infer_places: [bool] entry in the yaml configuration file in order to allow the developers to decide if they want to use the places for the additional validations. For example:
framework:
    workflows:
        article:
            infer_places: true
            transitions:
                go:
                    from: first
                    to: last
Pros:
  • Same as first option.
  • Explicit behavior defined by the developer.
Cons:
  • Extra verbosity in configuration files.

@GromNaN
Copy link
Member

GromNaN commented Nov 1, 2023

If one made a typo between the place and the transitions transition, it's
not caught anymore

Good practice is to use constants, isn't it?

I would deprecate the places config and ignore it.

@lyrixx
Copy link
Member Author

lyrixx commented Nov 2, 2023

This sound great, if we require support I can create a PR to achieve this.

Thanks for you support, but I'll take care of this one.

@vinceAmstoutz
Copy link

Could be a nice enhancement to the DX, great idea 🙂

@michalananapps
Copy link

I think that you can't just drop places from config. You can define metadata for states/places there.

https://symfony.com/blog/new-in-symfony-4-1-workflow-improvements#allow-to-store-metadata

@lyrixx
Copy link
Member Author

lyrixx commented Nov 16, 2023

Hmm, there is a big drawback: no more metadata on place! so we must keep it

@tacman
Copy link
Contributor

tacman commented Jan 30, 2024

I've been working on a bundle that allows workflows to be defined as attributes. For example.

    final const PLACE_NEW = 'new';
    final const PLACE_COMPOSER_LOADED = 'loaded';
    final const PLACE_OUTDATED = 'outdated_symfony';
    final const PLACE_OUTDATED_PHP = 'php_too_old';
    final const PLACE_ABANDONED = 'abandoned';
    final const PLACE_VALID_REQUIREMENTS = 'valid';

    #[Transition([self::PLACE_NEW], self::PLACE_COMPOSER_LOADED)]
    final const TRANSITION_LOAD = 'load';

    #[Transition([self::PLACE_COMPOSER_LOADED], self::PLACE_OUTDATED)]
    final const TRANSITION_OUTDATED = 'outdated';

    #[Transition([self::PLACE_COMPOSER_LOADED], self::PLACE_VALID_REQUIREMENTS)]
    final const TRANSITION_VALID = 'valid';

It also supports metadata.

#[Attribute(Attribute::TARGET_CLASS_CONSTANT)]
class Transition
{
    public function __construct(
        public array|string $from,
        public array|string $to,
        public ?string $guard=null,
        public ?array $metadata=[]
    ) {

    }

Then I autowire the configuration during the compilerPass:

class ConfigureFromAttributesService
{
    static public function configureFramework(string $workflowClass, FrameworkConfig $framework, array|string $supports)
    {
        $reflectionClass = new \ReflectionClass($workflowClass);
        // look in attribute first
        $workflow = $framework->workflows()->workflows($reflectionClass->getShortName())
            ->supports($supports);

        $constants = $reflectionClass->getConstants();
        $seen = [];
        foreach ($reflectionClass->getConstants() as $name => $constantValue) {
            $reflectionConstant = new \ReflectionClassConstant($workflowClass, $name);
            foreach ($reflectionConstant->getAttributes() as $attribute) {
                $instance = $attribute->newInstance();
                assert($reflectionConstant->getValue() == $constantValue);
                switch ($instance::class) {
                    case Place::class:
                        // check for initial
                        if ($instance->initial) {
                            $initial = $constantValue;
                        }
                        $seen[] = $name;
                        $workflow->place()->name($constantValue) // the name of the place is the value of the constant
                        ->metadata($instance->metadata);
                        break;
                    case Transition::class:
                        $workflow->transition()
                            ->name($constantValue)
                            ->from($instance->from)
                            ->to($instance->to)
                            ->metadata($instance->metadata);
                        break;
                }
            }
        }

        // shortcut to add all places of a certain pattern, if not already seen
        foreach ($constants as $name => $constantValue) {
            if (preg_match('/PLACE_/', $name)) {

                if (!in_array($name, $seen)) {
                    $workflow->place()->name($constantValue);
                    // @todo: look at attributes
                    if (empty($initial)) {
                        $initial = $constantValue;
                    }
                }
            }
        }
        $workflow->initialMarking($initial);
    }
}

While I use the bundle for all my projects, it's not yet ready for prime time. Nonetheless, if you're rethinking workflows, I've found this approach to be helpful for me.

Now that I'm posting this, I really should add some documentation to https://github.com/survos/SurvosWorkflowHelperBundle

@fabpot fabpot closed this as completed Feb 11, 2024
fabpot added a commit that referenced this issue Feb 11, 2024
This PR was merged into the 7.1 branch.

Discussion
----------

[Workflow] determines places from transitions

| Q             | A
| ------------- | ---
| Branch?       | 7.1
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Issues        | Fix #52378
| License       | MIT

Commits
-------

ed30d81 [workflow] determines places form transitions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RFC RFC = Request For Comments (proposals about features that you want to be discussed) Workflow
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants