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

Skip to content

[FrameworkBundle][Workflow] Attach the workflow's configuration to the workflow tag #51227

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

Merged
merged 1 commit into from
Mar 11, 2024

Conversation

lyrixx
Copy link
Member

@lyrixx lyrixx commented Aug 2, 2023

Q A
Branch? 7.1
Bug fix? no
New feature? yes
Deprecations? no
Tickets
License MIT
Doc PR

Since the registry is deprecated, users have to build their own registry when a service locator is not enough.
However, some information can be missing, like the supports configuration option.

In this PR, I add the whole configuration to the tag, so everyone can build exactly what they need.

The config is added only to the workflow tag, not sub tags. To get it:

class Kernel extends BaseKernel implements CompilerPassInterface
{
    use MicroKernelTrait;

    public function process(ContainerBuilder $container)
    {
        foreach ($container->findTaggedServiceIds('workflow.workflow') as $id => $attributes) {
            $config = $container->getDefinition($id)->getTag('workflow')[0];
            dd($config);
        }
    }
}

@nicolas-grekas
Copy link
Member

That's a bit strange to me, I'm not sure we ever did that. Usually tag attributes are used in a compiler pass. Shouldn't we do the same here? What's the more extended use case for this?

@lyrixx
Copy link
Member Author

lyrixx commented Aug 2, 2023

That's a bit strange to me, I'm not sure we ever did that. Usually tag attributes are used in a compiler pass. Shouldn't we do the same here? What's the more extended use case for this?

It looks like you missed the PR description :D The example show a usage of tag attribute in a compiler pass

And the use case is described in the PR description too. But to go a bit further

class Kernel extends BaseKernel implements CompilerPassInterface
{
    use MicroKernelTrait;

    public function process(ContainerBuilder $container)
    {
        $classesToWorkflow = [];
        foreach ($container->findTaggedServiceIds('workflow.workflow') as $id => $attributes) {
            $config = $container->getDefinition($id)->getTag('workflow')[0];
            foreach ($config['supports'] ?? [] as $class) {
                $classesToWorkflow[$class] = new Reference($id);
            }
        }

        $container->register(MyRegistry::class, MyRegistry::class)
            ->setArguments([$classesToWorkflow])
        ;
    }
}

Note

  1. I did not tested this very example
  2. Instead of an array of service, it would be better to use a service locator, but that's not the point here

@nicolas-grekas
Copy link
Member

Doesn't your registry duplicate the code we have in the Registry class? Shouldn't we revert making it internal instead?
I'm challenging this because it feel strange to me to use tags for what you do here (I'm not sure we have any similar example).

@lyrixx
Copy link
Member Author

lyrixx commented Aug 3, 2023

I have a big issue with the registry: People use it to get one workflow. In the documentation we used to tell to do :

$workflowRegistry->get($subject)->apply($subject, 'foobar');

And this is wrong: people must inject the right Workflow directly (law of demeter, performance, etc)

That's why I decided to make it internal initially.

The documentation has been updated since (I just checked, and there is still one occurrence, cc @alexandre-daubois)

But there is still one issue. If you have generic controller / service where you cannot know in advance which workflow to use, you have to use a ServiceLocator. There are all tagged, by name, so it should be easy...

BUT, sometime, (cf this discussion) the name is not enough.

With this PR, people will have full power to build their own registry. It'll be faster, cleaner, and build exactly for each use case.

Even with the current Registry and its WorkflowSupportStrategyInterface you can't build something totally dynamic, since you miss some information about the configuration

javiereguiluz added a commit to javiereguiluz/symfony-docs that referenced this pull request Aug 3, 2023
…urrences (alexandre-daubois)

This PR was merged into the 5.4 branch.

Discussion
----------

[Workflow] Remove registry workflow retrieval occurrences

Follow-up of symfony/symfony#51227 (comment), `@lyrixx` proposed to remove "registry retrieval" occurrences in the documentation. Actually, the `Alternatively, use the registry...` part isn't present already in the 6.3 documentation.

Commits
-------

aea773c [Workflow] Remove registry workflow retrieval occurrences
Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

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

OK, thanks for the explanations. I guess I would prefer wiring the registry (ours) in a compiler pass instead of doing it in FWB, but that can be done separately.

@fabpot
Copy link
Member

fabpot commented Aug 4, 2023

Mouais, I'm not convinced.
The docs tell people the best way.
The Registry is the entry point for more powerful features.
If people want to shoot themselves in the foot, that's life.
So, I don't see why we need to have yet another way. I understand that one is userland and the other is internal, but still. If we're talking about edge cases, just using the current Registry should be enough, right?

@lyrixx
Copy link
Member Author

lyrixx commented Aug 4, 2023

I disagree. Registry needs instantiated workflow to work. It means no lazy loading.

Then you can't access supports configuration option.

That's two pain points that show the current way to work doesn't work well.

@lyrixx lyrixx force-pushed the workflow-dic-tag branch from 4788d19 to 75c8463 Compare August 8, 2023 14:38
@lyrixx
Copy link
Member Author

lyrixx commented Aug 8, 2023

Rebased, and conflict fixed

@lyrixx
Copy link
Member Author

lyrixx commented Oct 19, 2023

Hello, This PR is ready and I have rebased it.

@fabpot I understand your concern. But I have a real use case where I need to fetch a workflow dynamically based on some entity properties. We'll have a LOT of workflows - basically one by client (B to B to C), and every Client lays on the same entity. I could use the registry, but there is no lazy loading, so it will degrade performance.

Finally, the diff is very minimal, and does not really impact the framework. Everything is thrown after the compilation. I don't really how it could hurt 🤓

@stof
Copy link
Member

stof commented Oct 19, 2023

The issue is that if we start exposing this config on the attribute with the intent of allowing projects to read it, we must provide BC on the structure of this config (with no way at all to ever deprecate anything in it as it is not accessed through a dedicated API in which we can trigger deprecations)

@lyrixx
Copy link
Member Author

lyrixx commented Oct 19, 2023

Hmm, Got it 👍🏼 We could document that the shape is not BC?

@stof
Copy link
Member

stof commented Oct 19, 2023

@lyrixx but then, how would you use it ?

@lyrixx
Copy link
Member Author

lyrixx commented Oct 19, 2023

Basically with something like this:

# config/packages/workflow.yaml
framework:
    workflows:
        my_workflow_name:
            metadata:
                features: [repair, wash, dry]
// src/Kernel.php
class Kernel extends BaseKernel implements CompilerPassInterface
{
    use MicroKernelTrait;

    public function process(ContainerBuilder $container)
    {
        $configurations = [];
        foreach ($container->findTaggedServiceIds('workflow') as $id => $config) {
            $configurations[$id] = $config[0]['config'];
        }

        $container
            ->getDefinition(Selector::class)
            ->setArgument('$configurations', $configurations)
        ;
    }
}
<?php
// src/Workflow/Selector.php
namespace App\Workflow;

use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;

class Selector
{
    public function __construct(
        #[TaggedLocator('workflow')]
        private readonly ContainerInterface $container,
        private readonly array $configurations,
    ) {
    }

    public function getWorkflow(/** Article $article */)
    {
        foreach ($this->configurations as $id => $config) {
            // $features = $article->getWorkflowFeatures();
            $featuresRequired = ['repair', 'wash', 'dry'];
            sort($featuresRequired);
            $featuresAvailable = $config['metadata']['features'] ?? [];
            sort($featuresAvailable);
            if ($featuresRequired === $featuresAvailable) {
                return $this->container->get($id);
            }
        }

        throw new \RuntimeException('No workflow found');
    }
}

and finally

    public function index(Selector $selector, Article $article): Response
    {
        $w = $selector->getWorkflow($article);
        dd($w);

@lyrixx lyrixx force-pushed the workflow-dic-tag branch 2 times, most recently from 682b35f to 91c1f10 Compare October 20, 2023 09:09
@nicolas-grekas nicolas-grekas removed this from the 6.4 milestone Nov 15, 2023
@nicolas-grekas nicolas-grekas added this to the 7.1 milestone Nov 15, 2023
@lyrixx
Copy link
Member Author

lyrixx commented Nov 21, 2023

Would you accept this PR if I inject in the tag only the workflow metadata? It'll be enough.

@lyrixx
Copy link
Member Author

lyrixx commented Jan 29, 2024

Would you accept this PR if I inject in the tag only the workflow metadata? It'll be enough.

cc @fabpot

@lyrixx
Copy link
Member Author

lyrixx commented Feb 8, 2024

PR rebased, and updated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants