-
With #46000 the workflow registry has been marked as With the registry I was able to just ask the registry to give me a workflow that supports a given object. Something like $workflow = $this->workflowRegistry->get($job); What is the intended approach for such a use case without using the registry? I know that I can inject the workflows as either iterators or locators into any service, but how do I find the workflow supporting a given object? I'd need the accompanying support strategies for the workflows. I mean even the documentation (on https://symfony.com/doc/current/components/workflow.html) describes this exact scenario - using the registry. $blogPostWorkflow = ...;
$newsletterWorkflow = ...;
$registry = new Registry();
$registry->addWorkflow($blogPostWorkflow, new InstanceOfSupportStrategy(BlogPost::class));
$registry->addWorkflow($newsletterWorkflow, new InstanceOfSupportStrategy(Newsletter::class));
$blogPost = new BlogPost();
$workflow = $registry->get($blogPost); |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 23 replies
-
Hello. Actually the registry was a bad idea. It's useful only in twig. What is you use case for using a registry and not the right workflow directly? Note: I submitted a PR to remove this part of the documentation: symfony/symfony-docs#17878 |
Beta Was this translation helpful? Give feedback.
-
Hi! My team and I have similar concern as @sgehrig regarding Registry being marked We are using I'll provide a simplified explanation of our use case, so you could better understand why we would need that :) Lets imagine a service that aggregates some financial products (insurance, bank loans, etc..) provided by some financial organizations. This service implements an integration with each partner (financial organizations). Each partner has it's own API and it's own process how exactly (step by step) a customer (end user) can get a bank loan from that partner. So, lets say in that service we have an entity called #[ORM\Entity(repositoryClass: DealRepository::class)]
class Deal
{
#[ORM\Id]
#[ORM\Column(type: 'ulid', unique: true)]
private Ulid $id;
#[ORM\ManyToOne(fetch: 'EAGER')]
#[ORM\JoinColumn(nullable: false)]
private Integration $integration;
#[ORM\Column(type: 'json')]
private array $states = [];
public function getId(): Ulid
{
return $this->id;
}
public function getIntegration(): Integration
{
return $this->integration;
}
public function setIntegration(Integration $integration): self
{
$this->integration = $integration;
return $this;
}
public function getStates(): array
{
return $this->states;
}
public function setStates(array $states): self
{
$this->states = $states;
return $this;
}
} Methods
The integration process (workflow) with all partners is different in general, but there are some common operations that appear in all integrations (create a Deal entity within our service, send initial user data to partner, upload user documents, etc.) There are API endpoints for all these common operations that define all possible interactions with our customer (end user). In these API endpoints (besides other business logic which is the same for all integrations) we, eventually, resolve Deal workflow in runtime and apply a common transition (which exists in each integration's workflow). Deal workflow depends on what product from what partner the customer has selected. // somewhere within API endpoint's logic (exceptions handling omitted for simplicity)
$dealWorkflow = $this->workflowRegistry->get($deal);
$dealWorkflow->apply($deal, 'InitDeal'); There are few key points to take into account:
final class IntegrationCodeSupportStrategy implements WorkflowSupportStrategyInterface
{
public function supports(WorkflowInterface $workflow, object $subject): bool
{
return $subject instanceof Deal
&& $workflow->getMetadataStore()->getMetadata('integration_code') === $subject->getIntegration()->getCode();
}
}
So, basically, we have multiple workflows that look similar to this: framework:
workflows:
some_partner_deal:
type: 'workflow'
support_strategy: 'app.workflow.integration_code_supports_strategy' # custom supports strategy
metadata:
integration_code: 'some_partner_integration_code' # this code is unique for all integrations
initial_marking: 'new'
places:
'new': ~
'foo': ~
'bar': ~
# other places...
transitions:
'InitDeal': # this transition gets applied within an API endpoint reused in all integrations with all partners
from: 'new'
to: 'foo'
'SomePartnerSpecificTransition': # this transition gets applied somewhere in partner-specific integration code
from: 'foo'
to: 'bar'
# other transitions... The real usage is more complex but I hope this should be enough to explain why we use I'm sure we are not the only team who finds it useful, at least since what @sgehrig described in this discussion sounds like he has something similar. And there are many devs still using Symfony 5.4 and have no clue what changes are coming in 6.2+ - they might also share some interesting use cases... @lyrixx You proposed using service locators instead of I thought it over, so, yes, it's possible with some restrictions, and I guess it would look similar to this: class WorkflowResolver
{
private ContainerInterface $workflowLocator;
public function __construct(#[TaggedLocator('workflow')] ContainerInterface $workflowLocator) {
$this->workflowLocator = $workflowLocator;
}
public function resolve(Deal $deal): WorkflowInterface
{
// to get a workflow instance from service locator we do need the workflow name, no other options here..
// so I imagine we would have to use integration code as a part of workflow name
$workflowName = sprintf('%s_deal', $deal->getIntegration()->getCode());
// and then we just get the required workflow by its name
// looks quite simple, right?
return $this->workflowLocator->get($workflowName);
}
} But if you take a closer look, you may note this approach ( We could do the same by implementing a final class IntegrationCodeSupportStrategy implements WorkflowSupportStrategyInterface
{
public function supports(WorkflowInterface $workflow, object $subject): bool
{
return $subject instanceof Deal
&& $workflow->getName() === sprintf('%s_deal', $subject->getIntegration()->getCode());
}
} ... and still use Basically So, I see 2 disadvantages comparing to existing functionality:
The main question is simple: Why? What is the reason to mark As @sgehrig already mentioned, having In my experience I didn't have any issues with Some extra thoughts: if the Core Team wants to get rid of The strategy could be optional, by default workflow would support all objects (in fact as it is now, devs are free to pass any object to a workflow instance, which is a good place for potential human mistakes.. so maybe it would be even better to have some built-in object check within Workflow, I guess a default check like Not sure if it makes any sense for anyone, but thanks for taking time to read all this! :) |
Beta Was this translation helpful? Give feedback.
-
Hi @lyrixx I just upgraded my project and this change broke it. a) I have a compiler pass, that looks for the b) Then I have a factory that creates 2 new endpoints for these entities at runtime.
(I would have no idea about the workflow names here!) c) Then I have listeners who listen to these operations and perform the transitions. (I would have no idea about the workflow names here too) For the step c I could use a service locator as you have described above, but how do you suggest I go about steps (a) and (b) |
Beta Was this translation helpful? Give feedback.
-
It would for the compiler pass.
I guess then i could build my own registry in that compiler pass, which i
can use to get workflows for an entity to generate routes ( step b as
described above)
β¦On Thu, Jun 1, 2023, 13:45 GrΓ©goire Pineau ***@***.***> wrote:
Oh you are right! And if we expose the configuration in the tag attribute,
it would solves your issue, right?
β
Reply to this email directly, view it on GitHub
<#49199 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAALOOJV4E2IH5SNWEOPYM3XJBD2LANCNFSM6AAAAAAUO65AU4>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
hi all, I have decided to remove the deprecation about the registry! I don't like it, but it cause too much pain I guess! |
Beta Was this translation helpful? Give feedback.
Hello. Actually the registry was a bad idea. It's useful only in twig.
It's easier to inject the workflow directly where you need it.
What is you use case for using a registry and not the right workflow directly?
Note: I submitted a PR to remove this part of the documentation: symfony/symfony-docs#17878