8000 [FrameworkBundle][Workflow] Add metadata dumping support for Graphviz… · symfony/symfony@5ff2322 · GitHub
[go: up one dir, main page]

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

+3-3
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 8000 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

+6
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

+99-13
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

+8-5
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)
0