• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wol-soft / php-json-schema-model-generator / 17768946986

16 Sep 2025 02:21PM UTC coverage: 98.287% (-0.3%) from 98.564%
17768946986

Pull #92

github

Enno Woortmann
remove unnecessary function
Pull Request #92: BuilderClassPostProcessor

123 of 135 new or added lines in 9 files covered. (91.11%)

4 existing lines in 1 file now uncovered.

3385 of 3444 relevant lines covered (98.29%)

564.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.66
/src/SchemaProcessor/PostProcessor/BuilderClassPostProcessor.php
1
<?php
2

3
declare(strict_types = 1);
4

5
namespace PHPModelGenerator\SchemaProcessor\PostProcessor;
6

7
use PHPMicroTemplate\Render;
8
use PHPModelGenerator\Exception\FileSystemException;
9
use PHPModelGenerator\Exception\ValidationException;
10
use PHPModelGenerator\Interfaces\BuilderInterface;
11
use PHPModelGenerator\Interfaces\JSONModelInterface;
12
use PHPModelGenerator\Model\GeneratorConfiguration;
13
use PHPModelGenerator\Model\Property\PropertyInterface;
14
use PHPModelGenerator\Model\Property\PropertyType;
15
use PHPModelGenerator\Model\Schema;
16
use PHPModelGenerator\Model\Validator;
17
use PHPModelGenerator\Model\Validator\FilterValidator;
18
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintDecorator;
19
use PHPModelGenerator\PropertyProcessor\Decorator\TypeHint\TypeHintTransferDecorator;
20
use PHPModelGenerator\Utils\RenderHelper;
21

22
class BuilderClassPostProcessor extends PostProcessor
23
{
24
    /** @var Schema[] */
25
    private array $schemas = [];
26
    private GeneratorConfiguration $generatorConfiguration;
27

28
    public function process(Schema $schema, GeneratorConfiguration $generatorConfiguration): void
1✔
29
    {
30
        // collect the schemas and generate builder classes in postProcess hook to make sure the related model class
31
        // already has been created
32
        $this->schemas[] = $schema;
1✔
33
        $this->generatorConfiguration = $generatorConfiguration;
1✔
34
    }
35

36
    public function postProcess(): void
1✔
37
    {
38
        parent::postProcess();
1✔
39

40
        foreach ($this->schemas as $schema) {
1✔
41
            $properties = [];
1✔
42
            foreach ($schema->getProperties() as $property) {
1✔
43
                if (!$property->isInternal()) {
1✔
44
                    $properties[] = (clone $property)
1✔
45
                        ->setReadOnly(false)
1✔
46
                        // ensure the getter methods for required properties can return null (they have not been set yet)
1✔
47
                        ->setType($property->getType(), new PropertyType($property->getType(true)->getName(), true))
1✔
48
                        ->addTypeHintDecorator(new TypeHintTransferDecorator($property))
1✔
49
                        // keep filters to ensure values set on the builder match the return type of the getter
1✔
50
                        ->filterValidators(static fn(Validator $validator): bool
1✔
51
                            => is_a($validator->getValidator(), FilterValidator::class)
1✔
52
                        );
1✔
53
                }
54
            }
55

56
            $namespace = trim(
1✔
57
                join('\\', [$this->generatorConfiguration->getNamespacePrefix(), $schema->getClassPath()]),
1✔
58
                '\\',
1✔
59
            );
1✔
60

61
            $result = file_put_contents(
1✔
62
                str_replace('.php', 'Builder.php', $schema->getTargetFileName()),
1✔
63
                (new Render(__DIR__ . DIRECTORY_SEPARATOR . 'Templates' . DIRECTORY_SEPARATOR))->renderTemplate(
1✔
64
                    'BuilderClass.phptpl',
1✔
65
                    [
1✔
66
                        'namespace'              => $namespace,
1✔
67
                        'class'                  => $schema->getClassName(),
1✔
68
                        'schema'                 => $schema,
1✔
69
                        'properties'             => $properties,
1✔
70
                        'use'                    => $this->getBuilderClassImports($properties, $schema->getUsedClasses(), $namespace),
1✔
71
                        'generatorConfiguration' => $this->generatorConfiguration,
1✔
72
                        'viewHelper'             => new RenderHelper($this->generatorConfiguration),
1✔
73
                    ],
1✔
74
                )
1✔
75
            );
1✔
76

77
            $fqcn = "{$schema->getClassPath()}\\{$schema->getClassName()}Builder";
1✔
78

79
            if ($result === false) {
1✔
80
                // @codeCoverageIgnoreStart
81
                throw new FileSystemException("Can't write builder class $fqcn.",);
82
                // @codeCoverageIgnoreEnd
83
            }
84

85
            if ($this->generatorConfiguration->isOutputEnabled()) {
1✔
86
                // @codeCoverageIgnoreStart
87
                echo "Rendered builder class $fqcn\n";
88
                // @codeCoverageIgnoreEnd
89
            }
90
        }
91
    }
92

93
    /**
94
     * @param PropertyInterface[] $properties
95
     *
96
     * @return string[]
97
     */
98
    private function getBuilderClassImports(array $properties, array $originalClassImports, string $namespace): array
1✔
99
    {
100
        $imports = [BuilderInterface::class];
1✔
101
        $imports[] = $this->generatorConfiguration->collectErrors()
1✔
102
            ? $this->generatorConfiguration->getErrorRegistryClass()
1✔
NEW
103
            : ValidationException::class;
×
104

105
        foreach ($properties as $property) {
1✔
106
            // use typehint instead of type to cover multi-types
107
            foreach (array_unique(
1✔
108
                [...explode('|', $property->getTypeHint()), ...explode('|', $property->getTypeHint(true))]
1✔
109
            ) as $type) {
1✔
110
                // as the typehint only knows the class name but not the fqcn, lookup in the original imports
111
                foreach ($originalClassImports as $originalClassImport) {
1✔
112
                    if (str_ends_with($originalClassImport, "\\$type")) {
1✔
NEW
113
                        $type = $originalClassImport;
×
114
                    }
115
                }
116

117
                if (class_exists($type)) {
1✔
NEW
118
                    $imports[] = $type;
×
119

120
                    // for nested objects, allow additionally to pass an instance of the nested model also just plain
121
                    // arrays which will result in an object instantiation and validation during the build process
NEW
122
                    if (in_array(JSONModelInterface::class, class_implements($type))) {
×
NEW
123
                        $property->addTypeHintDecorator(new TypeHintDecorator(['array', basename($type) . 'Builder']));
×
NEW
124
                        $property->setType();
×
125
                    }
126
                }
127
            }
128
        }
129

130
        return RenderHelper::filterClassImports(array_unique($imports), $namespace);
1✔
131
    }
132
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc