Skip to content

Commit 0d4f2a6

Browse files
committed
feat: Introduce Relationships and RenderOptions classes for managing and filtering class diagram relationships
1 parent 64917de commit 0d4f2a6

File tree

5 files changed

+192
-9
lines changed

5 files changed

+192
-9
lines changed

src/ClassDiagramRenderer/ClassDiagram.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
namespace Tasuku43\MermaidClassDiagram\ClassDiagramRenderer;
55

66
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Node;
7+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Relationship\Dependency;
8+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Relationship\Composition;
9+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Relationship\Inheritance;
10+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Relationship\Realization;
711
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Relationship\Relationship;
12+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Relationship\Relationships;
813

914
class ClassDiagram
1015
{
@@ -13,10 +18,13 @@ class ClassDiagram
1318
*/
1419
private array $nodes;
1520

16-
/**
17-
* @var Relationship[]
18-
*/
19-
private array $relationships = [];
21+
private Relationships $relationships;
22+
23+
public function __construct()
24+
{
25+
$this->nodes = [];
26+
$this->relationships = Relationships::empty();
27+
}
2028

2129
public function addNode(Node $node): self
2230
{
@@ -27,15 +35,17 @@ public function addNode(Node $node): self
2735

2836
public function addRelationships(Relationship ...$relationships): self
2937
{
30-
$this->relationships = [...$this->relationships, ...$relationships];
38+
foreach ($relationships as $relationship) {
39+
$this->relationships->add($relationship);
40+
}
3141

3242
return $this;
3343
}
3444

35-
public function render(): string
45+
public function render(RenderOptions $options = null): string
3646
{
3747
Node::sortNodes($this->nodes);
38-
Relationship::sortRelationships($this->relationships);
48+
$this->relationships->sort();
3949

4050
$output = "classDiagram\n";
4151

@@ -45,7 +55,7 @@ public function render(): string
4555

4656
$output .= "\n";
4757

48-
foreach ($this->relationships as $relationship) {
58+
foreach ($this->relationships->filter($options)->getAll() as $relationship) {
4959
$output .= " " . $relationship->render() . "\n";
5060
}
5161

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\Relationship;
5+
6+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\RenderOptions;
7+
8+
class Relationships
9+
{
10+
/**
11+
* @param Relationship[] $relationships
12+
*/
13+
private function __construct(private array $relationships)
14+
{
15+
}
16+
17+
public static function empty(): self
18+
{
19+
return new self([]);
20+
}
21+
22+
public function add(Relationship $relationship): void
23+
{
24+
$this->relationships[] = $relationship;
25+
}
26+
27+
/**
28+
* @return Relationship[]
29+
*/
30+
public function getAll(): array
31+
{
32+
return $this->relationships;
33+
}
34+
35+
public function sort(): void
36+
{
37+
Relationship::sortRelationships($this->relationships);
38+
}
39+
40+
public function filter(RenderOptions $options): self
41+
{
42+
$filtered = array_filter($this->relationships, function (Relationship $relationship) use ($options) {
43+
if ($relationship instanceof Dependency && !$options->includeDependencies) {
44+
return false;
45+
}
46+
if ($relationship instanceof Composition && !$options->includeCompositions) {
47+
return false;
48+
}
49+
if ($relationship instanceof Inheritance && !$options->includeInheritances) {
50+
return false;
51+
}
52+
if ($relationship instanceof Realization && !$options->includeRealizations) {
53+
return false;
54+
}
55+
56+
return true;
57+
});
58+
59+
return new self(array_values($filtered));
60+
}
61+
}
62+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Tasuku43\MermaidClassDiagram\ClassDiagramRenderer;
5+
6+
class RenderOptions
7+
{
8+
public function __construct(
9+
public bool $includeDependencies,
10+
public bool $includeCompositions,
11+
public bool $includeInheritances,
12+
public bool $includeRealizations,
13+
) {
14+
}
15+
16+
public static function default(): self
17+
{
18+
return new self(true, true, true, true);
19+
}
20+
}

src/Console/Command/GenerateCommand.php

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Symfony\Component\Console\Output\OutputInterface;
1313
use Symfony\Component\Console\Style\SymfonyStyle;
1414
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\ClassDiagramBuilder;
15+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\RenderOptions;
1516
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\NodeParser;
1617

1718
class GenerateCommand extends Command
@@ -25,6 +26,12 @@ protected function configure()
2526
null,
2627
InputOption::VALUE_REQUIRED,
2728
);
29+
$this->addOption(
30+
'exclude-relationships',
31+
null,
32+
InputOption::VALUE_REQUIRED,
33+
'Comma-separated relationship types to exclude: dependency,composition,inheritance,realization'
34+
);
2835
}
2936

3037
protected function execute(InputInterface $input, OutputInterface $output): int
@@ -38,8 +45,52 @@ protected function execute(InputInterface $input, OutputInterface $output): int
3845
new NodeFinder()
3946
));
4047

41-
$symfonyStyle->write($builder->build($path)->render());
48+
$options = $this->buildRenderOptions($input);
49+
50+
$symfonyStyle->write($builder->build($path)->render($options));
4251

4352
return self::SUCCESS;
4453
}
54+
55+
private function buildRenderOptions(InputInterface $input): RenderOptions
56+
{
57+
$options = RenderOptions::default();
58+
59+
$exclude = (string)($input->getOption('exclude-relationships') ?? '');
60+
if ($exclude === '') {
61+
return $options;
62+
}
63+
64+
$tokens = array_filter(array_map('trim', explode(',', $exclude)));
65+
foreach ($tokens as $token) {
66+
switch (strtolower($token)) {
67+
case 'dependency':
68+
case 'dependencies':
69+
case 'dep':
70+
case 'deps':
71+
$options->includeDependencies = false;
72+
break;
73+
case 'composition':
74+
case 'compositions':
75+
case 'comp':
76+
$options->includeCompositions = false;
77+
break;
78+
case 'inheritance':
79+
case 'inheritances':
80+
case 'extends':
81+
$options->includeInheritances = false;
82+
break;
83+
case 'realization':
84+
case 'realizations':
85+
case 'implements':
86+
$options->includeRealizations = false;
87+
break;
88+
default:
89+
// Ignore unknown tokens silently for now
90+
break;
91+
}
92+
}
93+
94+
return $options;
95+
}
4596
}

tests/ClassDiagramRenderer/ClassDiagramBuilderTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpParser\PhpVersion;
1010
use PHPUnit\Framework\TestCase;
1111
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\ClassDiagramBuilder;
12+
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\RenderOptions;
1213
use Tasuku43\MermaidClassDiagram\ClassDiagramRenderer\Node\NodeParser;
1314

1415
class ClassDiagramBuilderTest extends TestCase
@@ -85,6 +86,45 @@ class UserController {
8586
AbstractController <|-- UserController: inheritance
8687
UserController *-- UserService: composition
8788

89+
EOT;
90+
91+
$this->assertSame($expectedDiagram, $classDiagram);
92+
}
93+
94+
public function testBuildFromSampleProjectOnlyPropertiesDeps(): void
95+
{
96+
$path = __DIR__ . '/../data/Project';
97+
98+
$classDiagram = $this->classDigagramBuilder
99+
->build($path)
100+
->render(new RenderOptions(false, true, true, true));
101+
102+
$expectedDiagram = <<<'EOT'
103+
classDiagram
104+
class AbstractController {
105+
<<abstract>>
106+
}
107+
class User {
108+
}
109+
class UserController {
110+
}
111+
class UserRepository {
112+
}
113+
class UserRepositoryInterface {
114+
<<interface>>
115+
}
116+
class UserService {
117+
}
118+
class UserStatus {
119+
<<enum>>
120+
}
121+
122+
User *-- UserStatus: composition
123+
AbstractController <|-- UserController: inheritance
124+
UserController *-- UserService: composition
125+
UserRepositoryInterface <|.. UserRepository: realization
126+
UserService *-- UserRepositoryInterface: composition
127+
88128
EOT;
89129

90130
$this->assertSame($expectedDiagram, $classDiagram);

0 commit comments

Comments
 (0)