Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.00% covered (success)
95.00%
57 / 60
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
CreateDomainObject
95.00% covered (success)
95.00%
57 / 60
66.67% covered (warning)
66.67%
4 / 6
19
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getBoundedContextConfig
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 getIdentifierPath
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDomainObjectPath
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 generateDomainIdentifierCode
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
5
 generateDomainObjectCode
93.10% covered (success)
93.10%
27 / 29
0.00% covered (danger)
0.00%
0 / 1
9.03
1<?php
2namespace Apie\Maker\CodeGenerators;
3
4use Apie\Common\ValueObjects\EntityNamespace;
5use Apie\Core\Entities\EntityInterface;
6use Apie\Core\Identifiers\IdentifierInterface;
7use Apie\Maker\Dtos\DomainObjectDto;
8use Apie\Maker\Enums\IdType;
9use Apie\Maker\Utils;
10use LogicException;
11use Nette\PhpGenerator\PhpFile;
12use Nette\PhpGenerator\PromotedParameter;
13use Nette\PhpGenerator\PsrPrinter;
14use ReflectionClass;
15
16class CreateDomainObject
17{
18    /**
19     * @param array<string, mixed> $boundedContexts
20     */
21    public function __construct(
22        private readonly array $boundedContextConfig
23    ) {
24    }
25
26    private function getBoundedContextConfig(DomainObjectDto $domainObjectDto): array
27    {
28        $boundedContextId = $domainObjectDto->boundedContextId->toNative();
29        if (!isset($this->boundedContextConfig[$boundedContextId])) {
30            throw new LogicException('Bounded context "' . $boundedContextId . '" not found!');
31        }
32        return $this->boundedContextConfig[$boundedContextId];
33    }
34
35    public function getIdentifierPath(DomainObjectDto $domainObjectDto): string
36    {
37        $boundedContextConfig = $this->getBoundedContextConfig($domainObjectDto);
38        return $boundedContextConfig['entities_folder'] . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'Identifiers' . DIRECTORY_SEPARATOR . $domainObjectDto->name . 'Identifier.php';
39    }
40
41    public function getDomainObjectPath(DomainObjectDto $domainObjectDto): string
42    {
43        $boundedContextConfig = $this->getBoundedContextConfig($domainObjectDto);
44        return $boundedContextConfig['entities_folder'] . DIRECTORY_SEPARATOR . $domainObjectDto->name . '.php';
45    }
46
47    public function generateDomainIdentifierCode(DomainObjectDto $domainObjectDto): string
48    {
49        $boundedContextConfig = $this->getBoundedContextConfig($domainObjectDto);
50        $resourceNamespace = new EntityNamespace($boundedContextConfig['entities_namespace']);
51        $idNamespace = $resourceNamespace->getParentNamespace()->getChildNamespace('Identifiers');
52        
53        $file = new PhpFile();
54        if ($domainObjectDto->reuseExistingCode) {
55            $targetPath = $this->getIdentifierPath($domainObjectDto);
56            if (file_exists($targetPath)) {
57                $file = PhpFile::fromCode(file_get_contents($targetPath));
58            }
59        }
60
61        $code = Utils::addOrGetNamespace($file, $idNamespace);
62        Utils::addUseStatements($code, IdentifierInterface::class, $resourceNamespace . $domainObjectDto->name, $domainObjectDto->idType->value, ReflectionClass::class);
63        $classType = $code->getClasses()[$domainObjectDto->name . 'Identifier'] ?? $code->addClass($domainObjectDto->name . 'Identifier');
64        $classType->setExtends($domainObjectDto->idType->value);
65        if (!in_array(IdentifierInterface::class, $classType->getImplements())) {
66            $classType->addImplement(IdentifierInterface::class);
67        }
68        if (!$classType->hasMethod('getReferenceFor')) {
69            $classType->addMethod('getReferenceFor');
70        }
71        $classType->getMethod('getReferenceFor')
72            ->setStatic(true)
73            ->setBody('return new ReflectionClass(' . $domainObjectDto->name. '::class);')
74            ->setReturnType(ReflectionClass::class);
75        $printer = new PsrPrinter();
76        return $printer->printFile($file);
77    }
78
79    public function generateDomainObjectCode(DomainObjectDto $domainObjectDto): string
80    {
81        $boundedContextConfig = $this->getBoundedContextConfig($domainObjectDto);
82        $resourceNamespace = new EntityNamespace($boundedContextConfig['entities_namespace']);
83        $idNamespace = $resourceNamespace->getParentNamespace()->getChildNamespace('Identifiers');
84        $file = new PhpFile();
85        if ($domainObjectDto->reuseExistingCode) {
86            $targetPath = $this->getDomainObjectPath($domainObjectDto);
87            if (file_exists($targetPath)) {
88                $file = PhpFile::fromCode(file_get_contents($targetPath));
89            }
90        }
91        $code = Utils::addOrGetNamespace($file, $resourceNamespace);
92        $classType = $code->getClasses()[(string) $domainObjectDto->name] ?? $code->addClass((string) $domainObjectDto->name);
93        if (!in_array(EntityInterface::class, $classType->getImplements())) {
94            $classType->addImplement(EntityInterface::class);
95        }
96        $shouldInitConstructor = !$classType->hasMethod('__construct') && $domainObjectDto->idType === IdType::Integer;
97        $idProperty = Utils::searchOrAddProperty(
98            $classType,
99            'id',
100            $domainObjectDto->idType === IdType::Integer || $domainObjectDto->idType === IdType::Uuid,
101            $domainObjectDto->idType === IdType::Uuid
102        );
103        $idClass = $idNamespace . $domainObjectDto->name . 'Identifier';
104        Utils::addUseStatements($code, $idClass);
105        $idProperty->setType($idClass);
106        if ($shouldInitConstructor && !$idProperty instanceof PromotedParameter) {
107            // constructor is created in Utils::searchOrAddProperty....
108            $classType->getMethod('__construct')
109                ->addBody('$this->id = $id ?? new ' . $domainObjectDto->name . 'Identifier(null);');
110        }
111        if (!$classType->hasMethod('getId')) {
112            $classType->addMethod('getId')->setBody('return $this->id;')->setReturnType($idClass);
113        }
114        $printer = new PsrPrinter();
115        return $printer->printFile($file);
116    }
117}