SchemaLoader::validateRelations()   C
last analyzed

Complexity

Conditions 7
Paths 34

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 7

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 39
ccs 24
cts 24
cp 1
rs 6.7272
cc 7
eloc 21
nc 34
nop 1
crap 7
1
<?php
2
3
/*
4
 * This file is part of the "RocketORM" package.
5
 *
6
 * https://github.com/RocketORM/ORM
7
 *
8
 * For the full license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Rocket\ORM\Generator\Schema\Loader;
13
14
use Rocket\ORM\Generator\Schema\Configuration\SchemaConfiguration;
15
use Rocket\ORM\Generator\Schema\Loader\Exception\InvalidConfigurationException;
16
use Rocket\ORM\Generator\Schema\Loader\Exception\SchemaNotFoundException;
17
use Rocket\ORM\Generator\Schema\Schema;
18
use Rocket\ORM\Generator\Schema\Transformer\SchemaRelationTransformerInterface;
19
use Rocket\ORM\Generator\Schema\Transformer\SchemaTransformerInterface;
20
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException as ConfigurationException;
21
use Symfony\Component\Config\Definition\Processor;
22
use Symfony\Component\Config\Util\XmlUtils;
23
use Symfony\Component\Finder\Finder;
24
use Symfony\Component\Finder\SplFileInfo;
25
use Symfony\Component\Yaml\Yaml;
26
27
/**
28
 * @author Sylvain Lorinet <[email protected]>
29
 */
30
class SchemaLoader
31
{
32
    /**
33
     * @var string|array
34
     */
35
    protected $path;
36
37
    /**
38
     * @var string|array
39
     */
40
    protected $exclude;
41
42
    /**
43
     * @var array
44
     */
45
    protected $options;
46
47
48
    /**
49
     * @param string|array $path
50
     * @param array        $options
51
     */
52 6
    public function __construct($path, array $options = [])
53
    {
54 6
        $this->path    = $path;
55 6
        $this->options = array_replace_recursive($this->getDefaultOptions(), $options);
56 6
    }
57
58
    /**
59
     * @return array|Schema[]
60
     *
61
     * @throws InvalidConfigurationException
62
     * @throws SchemaNotFoundException
63
     */
64 2
    public function load()
65
    {
66 2
        $schemaLocations = $this->getSchemasLocation();
67 1
        $schemas = [];
68
69 1
        foreach ($schemaLocations['xml'] as $path) {
70 1
            $schemas[$path] = $this->parseXml($path);
71 1
        }
72
73 1
        foreach ($schemaLocations['yml'] as $path) {
74 1
            $schemas[$path] = $this->parseYaml($path);
75 1
        }
76
77 1
        return $this->validate($schemas);
78
    }
79
80
    /**
81
     * @param array $schemas
82
     *
83
     * @return array
84
     *
85
     * @throws InvalidConfigurationException
86
     */
87 5
    protected function validate(array $schemas)
88
    {
89 5
        $processor = new Processor();
90 5
        $configuration = new SchemaConfiguration();
91
92 5
        $defaultOptions = $this->getDefaultOptions();
93 5
        $transformerClass = $this->options['transformer']['schema']['class'];
94
95 5
        if (is_object($transformerClass)) {
96 1
            $schemaTransformer = $transformerClass;
97 1
        } else {
98 4
            $schemaTransformer = new $transformerClass($this->options['model'], $defaultOptions['model']);
99
        }
100
101 5
        if (!$schemaTransformer instanceof SchemaTransformerInterface) {
102 1
            throw new \InvalidArgumentException(
103 1
                'The schema transformer class "' . $transformerClass . '" should implements '
104 1
                . '\Rocket\ORM\Generator\Schema\Transformer\SchemaTransformerInterface'
105 1
            );
106
        }
107
108 4
        $normalizedSchemas = [];
109 4
        foreach ($schemas as $path => $schema) {
110
            try {
111 4
                $normalizedSchemas[$path] = $schemaTransformer->transform(
112 4
                    $processor->processConfiguration($configuration, [$schema]),
113
                    $path
114 4
                );
115 4
            } catch (ConfigurationException $e) {
116 1
                throw new InvalidConfigurationException($path, $e);
117
            }
118 3
        }
119
120 3
        return $this->validateRelations($normalizedSchemas);
121
    }
122
123
    /**
124
     * @param array|Schema[] $normalizedSchemas
125
     *
126
     * @return array|Schema[]
127
     *
128
     * @throws InvalidConfigurationException
129
     */
130 3
    protected function validateRelations(array $normalizedSchemas)
131
    {
132 3
        $transformerClass = $this->options['transformer']['relation']['class'];
133 3
        if (is_object($transformerClass)) {
134 1
            $schemaRelationTransformer = $transformerClass;
135 1
        } else {
136 2
            $schemaRelationTransformer = new $transformerClass();
137
        }
138
139 3
        if (!$schemaRelationTransformer instanceof SchemaRelationTransformerInterface) {
140 1
            throw new \InvalidArgumentException(
141 1
                'The schema relation transformer class "' . $transformerClass . '" should implements '
142 1
                . '\Rocket\ORM\Generator\Schema\Transformer\SchemaRelationTransformerInterface'
143 1
            );
144
        }
145
146 2
        $validSchemas = [];
147
148
        // All schemas are loaded, now we can validate relations
149
        /** @var Schema $schema */
150 2
        foreach ($normalizedSchemas as $path => $schema) {
151
            try {
152 2
                foreach ($schema->getTables() as $table) {
153 2
                    $schemaRelationTransformer->transform($table, $normalizedSchemas);
154 1
                }
155
156
                // Must wait for the relation transformations above ($relation->with)
157 1
                foreach ($schema->getTables() as $table) {
158 1
                    $schemaRelationTransformer->transformRelatedRelations($table, $normalizedSchemas);
0 ignored issues
show
Unused Code introduced by
The call to SchemaRelationTransforme...sformRelatedRelations() has too many arguments starting with $normalizedSchemas.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
159 1
                }
160 2
            } catch (ConfigurationException $e) {
161 1
                throw new InvalidConfigurationException($path, $e);
162
            }
163
164 1
            $validSchemas[] = $schema;
165 1
        }
166
167 1
        return $validSchemas;
168
    }
169
170
    /**
171
     * @return array
172
     *
173
     * @throws SchemaNotFoundException
174
     */
175 2
    protected function getSchemasLocation()
176
    {
177 2
        $finder = new Finder();
178
        $finder
179 2
            ->files()
180 2
            ->in($this->path)
181 2
            ->exclude($this->options['exclude'])
182 2
            ->name('/(.*)?schema.(yml|xml)/')
183
        ;
184
185 2
        if (0 === $finder->count()) {
186 1
            throw new SchemaNotFoundException('Schema not found in path "' . $this->path . '"');
187
        }
188
189
        $schemas = [
190 1
            'xml' => [],
191 1
            'yml' => []
192 1
        ];
193
194
        /** @var SplFileInfo $file */
195 1
        foreach ($finder as $file) {
196 1
            if ('xml' == strtolower($file->getExtension())) {
197 1
                $schemas['xml'][] = $file->getRealPath();
198 1
            } elseif ('yml' == strtolower($file->getExtension())) {
199 1
                $schemas['yml'][] = $file->getRealPath();
200 1
            }
201
202
            // else, extension not implemented
203 1
        }
204
205 1
        return $schemas;
206
    }
207
208
    /**
209
     * @param string $path
210
     *
211
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double|string|boolean|array|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
212
     */
213 1
    protected function parseXml($path)
214
    {
215 1
        return XmlUtils::convertDomElementToArray(XmlUtils::loadFile($path)->documentElement);
216
    }
217
218
    /**
219
     * @param string $path
220
     *
221
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be string|array|\stdClass?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
222
     */
223 1
    protected function parseYaml($path)
224
    {
225 1
        return Yaml::parse(file_get_contents($path));
226
    }
227
228
    /**
229
     * Return the default available configurable options
230
     *
231
     * @return array
232
     */
233 6
    protected function getDefaultOptions()
234
    {
235
        return [
236 6
            'exclude'     => null,
237
            'model'       => [
238 6
                'schema'   => ['class' => '\Rocket\ORM\Generator\Schema\Schema'],
239 6
                'table'    => ['class' => '\Rocket\ORM\Generator\Schema\Table'],
240 6
                'column'   => ['class' => '\Rocket\ORM\Generator\Schema\Column'],
241 6
                'relation' => ['class' => '\Rocket\ORM\Generator\Schema\Relation']
242 6
            ],
243
            'transformer' => [
244 6
                'schema'   => ['class' => '\Rocket\ORM\Generator\Schema\Transformer\SchemaTransformer'],
245 6
                'relation' => ['class' => '\Rocket\ORM\Generator\Schema\Transformer\SchemaRelationTransformer']
246 6
            ]
247 6
        ];
248
    }
249
}
250