Passed
Pull Request — 2.1 (#66)
by Vincent
22:57 queued 15:10
created

CriteriaGenerator::parseMetadata()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 16
ccs 10
cts 10
cp 1
rs 9.9666
cc 4
nc 3
nop 1
crap 4
1
<?php
2
3
namespace Bdf\Prime\Entity;
4
5
use Bdf\Prime\Mapper\Mapper;
6
use Bdf\Prime\Mapper\Metadata;
7
use LogicException;
8
use Nette\PhpGenerator\ClassType;
9
use Nette\PhpGenerator\PhpFile;
10
use Nette\PhpGenerator\PsrPrinter;
11
use ReflectionFunction;
12
use ReflectionMethod;
13
use ReflectionNamedType;
14
15
use function file_get_contents;
16
17
/**
18
 * Generate a criteria class for a given entity
19
 */
20
final class CriteriaGenerator
21
{
22
    private string $className;
23
    private ?PhpFile $file = null;
24
    private ?ClassType $class = null;
25
26
    /**
27
     * @param string $className The generated criteria class name. Can be a FQCN (i.e. with namespace)
28
     */
29 11
    public function __construct(string $className)
30
    {
31 11
        $this->className = $className;
32
    }
33
34
    /**
35
     * Loads a criteria class from a file
36
     * Calling this method permit to update the class (i.e. keep the existing methods) instead of regenerating it.
37
     *
38
     * @param string $filename The file path to load
39
     * @return void
40
     */
41 4
    public function loadFromFile(string $filename): void
42
    {
43 4
        $this->file = PhpFile::fromCode(file_get_contents($filename));
44 4
        $this->class = $this->file->getClasses()[$this->className] ?? null;
45
    }
46
47
    /**
48
     * Declare a setter method for each attribute of the entity
49
     *
50
     * @param Metadata $metadata
51
     * @return void
52
     */
53 8
    public function parseMetadata(Metadata $metadata): void
54
    {
55 8
        $class = $this->class();
56
57 8
        foreach ($metadata->attributes() as $field) {
58 8
            if (isset($field['embedded']) || $class->hasMethod($field['attribute'])) {
59 8
                continue;
60
            }
61
62 8
            $method = $class->addMethod($field['attribute']);
63
64 8
            $method->addParameter('value');
65 8
            $method->setReturnType('self');
66
67 8
            $method->addBody('$this->add(?, $value);', [$field['attribute']]);
68 8
            $method->addBody('return $this;');
69
        }
70
    }
71
72
    /**
73
     * Declare a setter for each custom filter
74
     *
75
     * @param array<string, callable> $customFilters
76
     * @return void
77
     *
78
     * @see Mapper::filters()
79
     */
80 8
    public function parseCustomFilters(array $customFilters): void
81
    {
82 8
        $class = $this->class();
83
84 8
        foreach ($customFilters as $name => $filter) {
85 2
            if ($class->hasMethod($name)) {
86 1
                continue;
87
            }
88
89 2
            $method = $class->addMethod($name);
90 2
            $method->setReturnType('self');
91
92 2
            $reflection = is_array($filter) ? new ReflectionMethod($filter[0], $filter[1]) : new ReflectionFunction($filter);
93
94 2
            if (!$parameterReflection = $reflection->getParameters()[1] ?? null) {
95
                continue;
96
            }
97
98 2
            $method->addParameter($parameterReflection->getName())
99 2
                ->setType($parameterReflection->getType() instanceof ReflectionNamedType ? $parameterReflection->getType()->getName() : null)
100 2
            ;
101
102 2
            $method->addBody('$this->add(?, $?);', [$name, $parameterReflection->getName()]);
103 2
            $method->addBody('return $this;');
104
        }
105
    }
106
107
    /**
108
     * Dump the generated class
109
     * Note: the code starts with an open `<?php` tag
110
     *
111
     * @return string
112
     */
113 10
    public function dump(): string
114
    {
115 10
        if (!$this->file) {
116
            throw new LogicException('No criteria class generated. Call parseMetadata() or parseCustomFilters() first');
117
        }
118
119 10
        return (new PsrPrinter())->printFile($this->file);
120
    }
121
122 9
    private function class(): ClassType
123
    {
124 9
        if ($this->class) {
125 7
            return $this->class;
126
        }
127
128 6
        if (!$this->file) {
129 6
            $this->file = new PhpFile();
130
        }
131
132 6
        $this->class = $this->file->addClass($this->className);
133 6
        $this->class->setExtends(Criteria::class);
134
135 6
        foreach ($this->file->getNamespaces() as $namespace) {
136 6
            $namespace->addUse(Criteria::class);
137
        }
138
139 6
        return $this->class;
140
    }
141
}
142