Skip to content

Commit 56a0749

Browse files
Herberto Gracahgraca
authored andcommitted
Create a IsMapped expression
This will allow us to ensure that certain classes are always in a map. This is useful, for example, to ensure that DTOs like commands and events are always set in a map, so that a serializer knows how to serialize/deserialize them.
1 parent 976c200 commit 56a0749

File tree

3 files changed

+107
-0
lines changed

3 files changed

+107
-0
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,23 @@ For example: `phparkitect debug:expression ResideInOneOfTheseNamespaces App`
152152

153153
Currently, you can check if a class:
154154

155+
### Is referenced in a given map
156+
157+
This is useful, for example, to ensure that DTOs like commands and
158+
events are always set in a map, so that we are sure a serializer knows how
159+
to serialize/deserialize them.
160+
161+
```php
162+
$map = [
163+
'a' => 'App\Core\Component\MyComponent\Command\MyCommand',
164+
'b' => 'App\Core\Component\MyComponent\Event\MyEvent',
165+
];
166+
$rules = Rule::allClasses()
167+
->that(new ResideInOneOfTheseNamespaces('App\Core\Component\**\Command', 'App\Core\Component\**\Event'))
168+
->should(new IsMapped($map))
169+
->because('we want to ensure our serializer can serialize/deserialize all commands and events');
170+
```
171+
155172
### Depends on a namespace
156173

157174
```php
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Arkitect\Expression\ForClasses;
6+
7+
use Arkitect\Analyzer\ClassDescription;
8+
use Arkitect\Expression\Description;
9+
use Arkitect\Expression\Expression;
10+
use Arkitect\Rules\Violation;
11+
use Arkitect\Rules\ViolationMessage;
12+
use Arkitect\Rules\Violations;
13+
14+
final class IsMapped implements Expression
15+
{
16+
public const POSITIVE_DESCRIPTION = 'should exist in the list';
17+
18+
/** @var array */
19+
private $list;
20+
21+
public function __construct(array $list)
22+
{
23+
$this->list = array_flip($list);
24+
}
25+
26+
public function describe(ClassDescription $theClass, string $because): Description
27+
{
28+
return new Description(self::POSITIVE_DESCRIPTION, $because);
29+
}
30+
31+
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
32+
{
33+
if (isset($this->list[$theClass->getFQCN()])) {
34+
return;
35+
}
36+
37+
$violation = Violation::create(
38+
$theClass->getFQCN(),
39+
ViolationMessage::selfExplanatory($this->describe($theClass, $because))
40+
);
41+
42+
$violations->add($violation);
43+
}
44+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Arkitect\Tests\Unit\Expressions\ForClasses;
6+
7+
use Arkitect\Analyzer\ClassDescriptionBuilder;
8+
use Arkitect\Expression\ForClasses\IsMapped;
9+
use Arkitect\Rules\Violations;
10+
use PHPUnit\Framework\TestCase;
11+
12+
final class IsMappedTest extends TestCase
13+
{
14+
public function test_it_should_not_add_violation_if_fqcn_is_in_list(): void
15+
{
16+
$listedFqcn = 'App\SharedKernel\Component\MyClass';
17+
$list = [
18+
'a' => $listedFqcn,
19+
'b' => 'App\SharedKernel\Component\MyOtherClass',
20+
];
21+
$expression = new IsMapped($list);
22+
$classDescription = (new ClassDescriptionBuilder())->setClassName($listedFqcn)->build();
23+
24+
$violations = new Violations();
25+
$expression->evaluate($classDescription, $violations, '');
26+
27+
self::assertEquals(0, $violations->count());
28+
}
29+
30+
public function test_it_should_add_violation_if_fqcn_is_not_in_list(): void
31+
{
32+
$nonListedFqcn = 'App\SharedKernel\Component\MyClass';
33+
$list = [
34+
'a' => 'App\SharedKernel\Component\SomeClass',
35+
'b' => 'App\SharedKernel\Component\MyOtherClass',
36+
];
37+
$expression = new IsMapped($list);
38+
$classDescription = (new ClassDescriptionBuilder())->setClassName($nonListedFqcn)->build();
39+
40+
$violations = new Violations();
41+
$expression->evaluate($classDescription, $violations, '');
42+
43+
self::assertEquals(1, $violations->count());
44+
self::assertEquals(IsMapped::POSITIVE_DESCRIPTION, $expression->describe($classDescription, '')->toString());
45+
}
46+
}

0 commit comments

Comments
 (0)