Skip to content

Instantly share code, notes, and snippets.

@Nemo64
Last active May 25, 2022 01:45
Show Gist options
  • Save Nemo64/d3c45bbc46badb9a2ee0e9bc284d8b7c to your computer and use it in GitHub Desktop.
Save Nemo64/d3c45bbc46badb9a2ee0e9bc284d8b7c to your computer and use it in GitHub Desktop.
<?php
namespace App\Command;
use App\Entity\Order;
use App\Service\TickService;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console;
use Symfony\Component\Workflow\WorkflowInterface;
class OrderTickCommand extends Console\Command\Command
{
public function __construct(
private WorkflowInterface $orderStateMachine,
private EntityManagerInterface $entityManager,
private TickService $tickService,
) {
parent::__construct();
}
public function configure()
{
$this->setName('order:tick');
$this->addArgument('ids', Console\Input\InputArgument::OPTIONAL | Console\Input\InputArgument::IS_ARRAY, 'limit to some ids');
}
protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output)
{
// fetch id list to process
$ids = $input->getArgument('ids');
if (empty($ids)) {
$statesToCheck = $this->getStatesToCheck();
$output->writeln(sprintf(
'Checking orders in states: <info>%s</info>',
implode(', ', $statesToCheck)
));
$qb = $this->entityManager->createQueryBuilder()
->select('order.id AS id')
->from(Order::class, 'order')
->where('order.status IN (:states)')
->setParameter('states', $statesToCheck);
$ids = array_column($qb->getQuery()->getArrayResult(), 'id');
}
// go through all id's and execute the tick
$progress = new Console\Helper\ProgressBar($output, count($ids));
$progress->setFormat('debug');
foreach ($ids as $index => $id) {
$this->entityManager->clear(); // prevent memory leaks
$progress->setProgress($index);
$transition = $this->entityManager->wrapInTransaction(function () use ($id) {
$order = $this->entityManager->find(Order::class, $id, LockMode::PESSIMISTIC_WRITE);
return $this->tickService->tick($order);
});
if ($transition) {
$output->writeln(sprintf(
'executed <info>%s</info> on order <info>%s</info> to <info>%s</info>',
$transition->getName(),
$id,
implode(', ', $transition->getTos())
));
}
}
$progress->finish();
return 0;
}
/**
* Builds a list of states that can have transitions with "on_tick" enabled.
*/
private function getStatesToCheck(): array
{
$result = [];
foreach ($this->orderStateMachine->getDefinition()->getTransitions() as $transition) {
$tickEnabled = $this->orderStateMachine->getMetadataStore()->getTransitionMetadata($transition)['on_tick'] ?? false;
if (!$tickEnabled) {
continue;
}
foreach ($transition->getFroms() as $from) {
$result[$from] = $from;
}
}
return array_values($result);
}
}
<?php
namespace App\Service;
use Symfony\Component\Workflow;
class TickService
{
public function __construct(
private Workflow\Registry $registry
) {
}
public function tick(object $subject, string $workflowName = null): ?Workflow\Transition
{
$workflow = $this->registry->get($subject, $workflowName);
foreach ($workflow->getEnabledTransitions($subject) as $transition) {
$tickEnabled = $workflow->getMetadataStore()->getTransitionMetadata($transition)['on_tick'] ?? false;
if (!$tickEnabled) {
continue;
}
$workflow->apply($subject, $transition);
return $transition;
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment