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

Skip to content

[Workflow] Declare Workflow with PHP Class and attributes #58503

Open
@thomassebert

Description

@thomassebert

Description

Hello,

I really like the Workflow component, but not his yaml configuration. Constants in yaml are too verbose, as is passing config to php format. So I tried to create three attributes: [AsWorkflow], to be set on a class (which should extend AbstractWorkflow), [Place], to be set on a constant, and [Transition], also to be set on a constant. With these three attributes, and a few configurations that I've put in a dedicated bundle for now, it's possible to add to Symfony's Workflow component the ability to simply declare a Workflow via a PHP class, autowire it directly from that class (you can use can(), apply(), .. functions directly from the class) and, if need be, mix it with the classic yaml configuration without any hassle or BC Break.

In the bundle, metadata management, testing and documentation still need to be completed, but the code works well, it's being used in real projects and everything's running smoothly. Before going any further, I'd like to hear your opinion on whether this could be integrated directly into the Workflow component? Is this the right approach? I could do a PR if I get positive feedback.

The code is available here => https://packagist.org/packages/letots/workflow-extension-bundle

To test, simply run the compose require letots/workflow-extension-bundle command in a recent Symfony project (attribute support is required) and create a new class (see example below). You can then debug the workflow, use it in any other class with autowire (you can still use the WorkflowInterface with Target attribute but you can also inject directly your class), ...

Is it really useful? Apart from the syntax, which I personally find clearer in PHP, we have the advantage of not having to configure anything for this to work, of having a separate PHP file for each Workflow and therefore of having autowiring with autocompletion of the class name, the advantages of using constants for autocompletion and validation to define the names of Places and Transitions (no need to go back 10 times in the workflow.yaml file to find out how a particular status was named), ... Last argument, even if this only applies to a few very specific cases, it is possible to create a dynamic workflow by rewriting the getPlaces() and getTransitions() functions of the AbstractWorkflow file, in order to add Places dynamically, for example, depending on the properties of the target object, all grouped together in the dedicated workflow class.

Example

<?php

// src/Workflow/InterventionWorkflow.php (but can be placed anywhere, just need AsWorkflow attribute and extend AbstractWorkflow

namespace App\Workflow;

use App\Entity\Intervention;
use LeTots\WorkflowExtension\AbstractWorkflow;
use LeTots\WorkflowExtension\Attribute\AsWorkflow;
use LeTots\WorkflowExtension\Attribute\Place;
use LeTots\WorkflowExtension\Attribute\Transition;

#[AsWorkflow(name: 'intervention_status', type: AsWorkflow::TYPE_STATE_MACHINE, supportStrategy: Intervention::class)]
class InterventionWorkflow extends AbstractWorkflow
{
	// Places
	#[Place(initial: true)]
	public const string STATE_QUALIFY = 'state_qualify';

	#[Place]
	public const string STATE_EXCLUSIONS = 'state_exclusions';

	#[Place]
	public const string STATE_CONSTANTS = 'state_constants';

	#[Place]
	public const string STATE_TEST = 'state_test';

	#[Place]
	public const string STATE_RESULT = 'state_result';

	#[Place]
	public const string STATE_FINISHED = 'state_finished';
	
	// Transitions
	#[Transition(from: self::STATE_QUALIFY, to: self::STATE_EXCLUSIONS)]
	public const string TRANSITION_TO_EXCLUSIONS = 'transition_to_exclusions';

	#[Transition(from: self::STATE_EXCLUSIONS, to: self::STATE_CONSTANTS)]
	public const string TRANSITION_TO_CONSTANTS = 'transition_to_constants';

	#[Transition(from: self::STATE_CONSTANTS, to: self::STATE_TEST)]
	public const string TRANSITION_TO_TEST = 'transition_to_test';

	#[Transition(from: self::STATE_TEST, to: self::STATE_RESULT)]
	public const string TRANSITION_TO_RESULT = 'transition_to_result';
	
	#[Transition(from: self::STATE_RESULT, to: self::STATE_FINISHED)]
	public const string TRANSITION_RESULT_TO_FINISHED = 'transition_result_to_finished';
}  

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions