Skip to content

Commit 5ff2322

Browse files
Louis-Proffitnicolas-grekas
authored andcommitted
[FrameworkBundle][Workflow] Add metadata dumping support for GraphvizDumper
1 parent d3c26bb commit 5ff2322

File tree

5 files changed

+259
-30
lines changed

5 files changed

+259
-30
lines changed

src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ protected function configure(): void
7373
new InputArgument('name', InputArgument::REQUIRED, 'A workflow name'),
7474
new InputArgument('marking', InputArgument::IS_ARRAY, 'A marking (a list of places)'),
7575
new InputOption('label', 'l', InputOption::VALUE_REQUIRED, 'Label a graph'),
76+
new InputOption('with-metadata', null, InputOption::VALUE_NONE, 'Include the workflow\'s metadata in the dumped graph', null),
7677
new InputOption('dump-format', null, InputOption::VALUE_REQUIRED, 'The dump format ['.implode('|', self::DUMP_FORMAT_OPTIONS).']', 'dot'),
7778
])
7879
->setHelp(<<<'EOF'
@@ -134,10 +135,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
134135

135136
$options = [
136137
'name' => $workflowName,
138+
'with-metadata' => $input->getOption('with-metadata'),
137139
'nofooter' => true,
138-
'graph' => [
139-
'label' => $input->getOption('label'),
140-
],
140+
'label' => $input->getOption('label'),
141141
];
142142
$output->writeln($dumper->dump($definition, $marking, $options));
143143

src/Symfony/Component/Workflow/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
CHANGELOG
22
=========
33

4+
6.4
5+
---
6+
7+
* Add `with-metadata` option to the command `workflow:dump` to include places,
8+
transitions and workflow's metadata into dumped graph
9+
410
6.2
511
---
612

src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,27 @@ class GraphvizDumper implements DumperInterface
4444
*/
4545
public function dump(Definition $definition, Marking $marking = null, array $options = []): string
4646
{
47-
$places = $this->findPlaces($definition, $marking);
48-
$transitions = $this->findTransitions($definition);
47+
$withMetadata = $options['with-metadata'] ?? false;
48+
49+
$places = $this->findPlaces($definition, $withMetadata, $marking);
50+
$transitions = $this->findTransitions($definition, $withMetadata);
4951
$edges = $this->findEdges($definition);
5052

5153
$options = array_replace_recursive(self::$defaultOptions, $options);
5254

53-
return $this->startDot($options)
54-
.$this->addPlaces($places)
55-
.$this->addTransitions($transitions)
55+
$label = $this->formatLabel($definition, $withMetadata, $options);
56+
57+
return $this->startDot($options, $label)
58+
.$this->addPlaces($places, $withMetadata)
59+
.$this->addTransitions($transitions, $withMetadata)
5660
.$this->addEdges($edges)
5761
.$this->endDot();
5862
}
5963

6064
/**
6165
* @internal
6266
*/
63-
protected function findPlaces(Definition $definition, Marking $marking = null): array
67+
protected function findPlaces(Definition $definition, bool $withMetadata, Marking $marking = null): array
6468
{
6569
$workflowMetadata = $definition->getMetadataStore();
6670

@@ -80,9 +84,16 @@ protected function findPlaces(Definition $definition, Marking $marking = null):
8084
$attributes['style'] = 'filled';
8185
$attributes['fillcolor'] = $backgroundColor;
8286
}
87+
if ($withMetadata) {
88+
$attributes['metadata'] = $workflowMetadata->getPlaceMetadata($place);
89+
}
8390
$label = $workflowMetadata->getMetadata('label', $place);
8491
if (null !== $label) {
8592
$attributes['name'] = $label;
93+
if ($withMetadata) {
94+
// Don't include label in metadata if already used as name
95+
unset($attributes['metadata']['label']);
96+
}
8697
}
8798
$places[$place] = [
8899
'attributes' => $attributes,
@@ -95,7 +106,7 @@ protected function findPlaces(Definition $definition, Marking $marking = null):
95106
/**
96107
* @internal
97108
*/
98-
protected function findTransitions(Definition $definition): array
109+
protected function findTransitions(Definition $definition, bool $withMetadata): array
99110
{
100111
$workflowMetadata = $definition->getMetadataStore();
101112

@@ -111,9 +122,17 @@ protected function findTransitions(Definition $definition): array
111122
}
112123
$name = $workflowMetadata->getMetadata('label', $transition) ?? $transition->getName();
113124

125+
if ($withMetadata) {
126+
$metadata = $workflowMetadata->getTransitionMetadata($transition);
127+
unset($metadata['label']);
128+
} else {
129+
$metadata = [];
130+
}
131+
114132
$transitions[] = [
115133
'attributes' => $attributes,
116134
'name' => $name,
135+
'metadata' => $metadata,
117136
];
118137
}
119138

@@ -123,7 +142,7 @@ protected function findTransitions(Definition $definition): array
123142
/**
124143
* @internal
125144
*/
126-
protected function addPlaces(array $places): string
145+
protected function addPlaces(array $places, float $withMetadata): string
127146
{
128147
$code = '';
129148

@@ -135,7 +154,15 @@ protected function addPlaces(array $places): string
135154
$placeName = $id;
136155
}
137156

138-
$code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $this->escape($placeName), $this->addAttributes($place['attributes']));
157+
if ($withMetadata) {
158+
$escapedLabel = sprintf('<<B>%s</B>%s>', $this->escape($placeName), $this->addMetadata($place['attributes']['metadata']));
159+
// Don't include metadata in default attributes used to format the place
160+
unset($place['attributes']['metadata']);
161+
} else {
162+
$escapedLabel = sprintf('"%s"', $this->escape($placeName));
163+
}
164+
165+
$code .= sprintf(" place_%s [label=%s, shape=circle%s];\n", $this->dotize($id), $escapedLabel, $this->addAttributes($place['attributes']));
139166
}
140167

141168
return $code;
@@ -144,12 +171,18 @@ protected function addPlaces(array $places): string
144171
/**
145172
* @internal
146173
*/
147-
protected function addTransitions(array $transitions): string
174+
protected function addTransitions(array $transitions, bool $withMetadata): string
148175
{
149176
$code = '';
150177

151178
foreach ($transitions as $i => $place) {
152-
$code .= sprintf(" transition_%s [label=\"%s\",%s];\n", $this->dotize($i), $this->escape($place['name']), $this->addAttributes($place['attributes']));
179+
if ($withMetadata) {
180+
$escapedLabel = sprintf('<<B>%s</B>%s>', $this->escape($place['name']), $this->addMetadata($place['metadata']));
181+
} else {
182+
$escapedLabel = '"'.$this->escape($place['name']).'"';
183+
}
184+
185+
$code .= sprintf(" transition_%s [label=%s,%s];\n", $this->dotize($i), $escapedLabel, $this->addAttributes($place['attributes']));
153186
}
154187

155188
return $code;
@@ -215,10 +248,11 @@ protected function addEdges(array $edges): string
215248
/**
216249
* @internal
217250
*/
218-
protected function startDot(array $options): string
251+
protected function startDot(array $options, string $label): string
219252
{
220-
return sprintf("digraph workflow {\n %s\n node [%s];\n edge [%s];\n\n",
253+
return sprintf("digraph workflow {\n %s%s\n node [%s];\n edge [%s];\n\n",
221254
$this->addOptions($options['graph']),
255+
'""' !== $label && '<>' !== $label ? sprintf(' label=%s', $label) : '',
222256
$this->addOptions($options['node']),
223257
$this->addOptions($options['edge'])
224258
);
@@ -248,6 +282,9 @@ protected function escape(string|bool $value): string
248282
return \is_bool($value) ? ($value ? '1' : '0') : addslashes($value);
249283
}
250284

285+
/**
286+
* @internal
287+
*/
251288
protected function addAttributes(array $attributes): string
252289
{
253290
$code = [];
@@ -259,6 +296,34 @@ protected function addAttributes(array $attributes): string
259296
return $code ? ' '.implode(' ', $code) : '';
260297
}
261298

299+
300+
/**
301+
* Handles the label of the graph depending on whether a label was set in CLI,
302+
* if metadata should be included and if there are any.
303+
*
304+
* The produced label must be escaped.
305+
*
306+
* @internal
307+
*/
308+
protected function formatLabel(Definition $definition, string $withMetadata, array $options): string
309+
{
310+
$currentLabel = $options['label'] ?? '';
311+
312+
if (!$withMetadata) {
313+
// Only currentLabel to handle. If null, will be translated to empty string
314+
return sprintf('"%s"', $this->escape($currentLabel));
315+
}
316+
$workflowMetadata = $definition->getMetadataStore()->getWorkflowMetadata();
317+
318+
if ('' === $currentLabel) {
319+
// Only metadata to handle
320+
return sprintf('<%s>', $this->addMetadata($workflowMetadata, false));
321+
}
322+
323+
// currentLabel and metadata to handle
324+
return sprintf('<<B>%s</B>%s>', $this->escape($currentLabel), $this->addMetadata($workflowMetadata));
325+
}
326+
262327
private function addOptions(array $options): string
263328
{
264329
$code = [];
@@ -269,4 +334,25 @@ private function addOptions(array $options): string
269334

270335
return implode(' ', $code);
271336
}
337+
338+
/**
339+
* @param bool $lineBreakFirstIfNotEmpty Whether to add a separator in the first place when metadata is not empty
340+
*/
341+
private function addMetadata(array $metadata, bool $lineBreakFirstIfNotEmpty = true): string
342+
{
343+
$code = [];
344+
345+
$skipSeparator = !$lineBreakFirstIfNotEmpty;
346+
347+
foreach ($metadata as $key => $value) {
348+
if ($skipSeparator) {
349+
$code[] = sprintf('%s: %s', $this->escape($key), $this->escape($value));
350+
$skipSeparator = false;
351+
} else {
352+
$code[] = sprintf('%s%s: %s', '<BR/>', $this->escape($key), $this->escape($value));
353+
}
354+
}
355+
356+
return $code ? implode('', $code) : '';
357+
}
272358
}

src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,19 @@ class StateMachineGraphvizDumper extends GraphvizDumper
2727
*/
2828
public function dump(Definition $definition, Marking $marking = null, array $options = []): string
2929
{
30-
$places = $this->findPlaces($definition, $marking);
30+
$withMetadata = $options['with-metadata'] ?? false;
31+
32+
$places = $this->findPlaces($definition, $withMetadata, $marking);
3133
$edges = $this->findEdges($definition);
3234

3335
$options = array_replace_recursive(self::$defaultOptions, $options);
3436

35-
return $this->startDot($options)
36-
.$this->addPlaces($places)
37+
$label = $this->formatLabel($definition, $withMetadata, $options);
38+
39+
return $this->startDot($options, $label)
40+
.$this->addPlaces($places, $withMetadata)
3741
.$this->addEdges($edges)
38-
.$this->endDot()
39-
;
42+
.$this->endDot();
4043
}
4144

4245
/**

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy