Completed
Push — master ( 4d3c9c...4a4bf4 )
by Gerrit
02:32
created

MappingXmlDriver::loadRDMMetadataForClass()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 55
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 55
c 0
b 0
f 0
ccs 23
cts 23
cp 1
rs 8.7752
cc 6
eloc 23
nc 6
nop 1
crap 6

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Copyright (C) 2018 Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 * @license GPL-3.0
8
 * @author Gerrit Addiks <[email protected]>
9
 */
10
11
namespace Addiks\RDMBundle\Mapping\Drivers;
12
13
use DOMDocument;
14
use DOMXPath;
15
use DOMNode;
16
use Addiks\RDMBundle\Mapping\Drivers\MappingDriverInterface;
17
use Doctrine\Common\Persistence\Mapping\Driver\FileLocator;
18
use Addiks\RDMBundle\Mapping\EntityMappingInterface;
19
use Addiks\RDMBundle\Mapping\EntityMapping;
20
use Addiks\RDMBundle\Mapping\ServiceMapping;
21
use Addiks\RDMBundle\Mapping\MappingInterface;
22
use Addiks\RDMBundle\Mapping\ChoiceMapping;
23
use DOMAttr;
24
use Doctrine\DBAL\Schema\Column;
25
use Doctrine\DBAL\Types\Type;
26
27
final class MappingXmlDriver implements MappingDriverInterface
28
{
29
30
    const RDM_SCHEMA_URI = "http://github.com/addiks/symfony_rdm/tree/master/Resources/mapping-schema.v1.xsd";
31
    const DOCTRINE_SCHEMA_URI = "http://doctrine-project.org/schemas/orm/doctrine-mapping";
32
33
    /**
34
     * @var FileLocator
35
     */
36
    private $fileLocator;
37
38
    /**
39
     * @var string
40
     */
41
    private $schemaFilePath;
42
43 2
    public function __construct(
44
        FileLocator $fileLocator,
45
        string $schemaFilePath
46
    ) {
47 2
        $this->fileLocator = $fileLocator;
48 2
        $this->schemaFilePath = $schemaFilePath;
49 2
    }
50
51 1
    public function loadRDMMetadataForClass(string $className): ?EntityMappingInterface
52
    {
53
        /** @var ?EntityMappingInterface $mapping */
0 ignored issues
show
Documentation introduced by
The doc-type ?EntityMappingInterface could not be parsed: Unknown type name "?EntityMappingInterface" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
54 1
        $mapping = null;
55
56
        /** @var array<MappingInterface> $fieldMappings */
57 1
        $fieldMappings = array();
58
59 1
        if ($this->fileLocator->fileExists($className)) {
60
            /** @var string $mappingFile */
61 1
            $mappingFile = $this->fileLocator->findMappingFile($className);
62
63
            /** @var boolean $previousUseLibxmlInternalErrors */
64 1
            $previousUseLibxmlInternalErrors = libxml_use_internal_errors(true);
65
66 1
            $dom = new DOMDocument();
67 1
            $dom->loadXML(file_get_contents($mappingFile));
68
69
            /** @var string $rdmPrefix */
70 1
            $rdmPrefix = $dom->lookupPrefix(self::RDM_SCHEMA_URI);
71
72 1
            if (!empty($rdmPrefix)) {
73 1
                $xpath = new DOMXPath($dom);
74
75 1
                $xpath->registerNamespace($rdmPrefix, self::RDM_SCHEMA_URI);
76 1
                $xpath->registerNamespace("d", self::DOCTRINE_SCHEMA_URI);
77
78 1
                foreach ($xpath->query("//d:entity/{$rdmPrefix}:service", $dom) as $serviceNode) {
79
                    /** @var DOMNode $serviceNode */
80
81
                    /** @var string $fieldName */
82 1
                    $fieldName = (string)$serviceNode->attributes->getNamedItem("field")->nodeValue;
83
84 1
                    $fieldMappings[$fieldName] = $this->readService($serviceNode, $mappingFile);
85
                }
86
87 1
                foreach ($xpath->query("//d:entity/{$rdmPrefix}:choice", $dom) as $choiceNode) {
88
                    /** @var DOMNode $choiceNode */
89
90
                    /** @var string $fieldName */
91 1
                    $fieldName = (string)$choiceNode->attributes->getNamedItem("field")->nodeValue;
92
93 1
                    $fieldMappings[$fieldName] = $this->readChoice($choiceNode, $mappingFile, $fieldName);
94
                }
95
            }
96
97 1
            libxml_use_internal_errors($previousUseLibxmlInternalErrors);
98
        }
99
100 1
        if (!empty($fieldMappings)) {
101 1
            $mapping = new EntityMapping($className, $fieldMappings);
102
        }
103
104 1
        return $mapping;
105
    }
106
107 1
    private function readChoice(DOMNode $choiceNode, string $mappingFile, string $defaultColumnName): ChoiceMapping
108
    {
109
        /** @var string|Colum $columnName */
110 1
        $column = $defaultColumnName;
111
112 1
        if (!is_null($choiceNode->attributes->getNamedItem("column"))) {
113 1
            $column = (string)$choiceNode->attributes->getNamedItem("column")->nodeValue;
114
        }
115
116
        /** @var array<MappingInterface> $choiceMappings */
117 1
        $choiceMappings = array();
118
119 1
        foreach ($choiceNode->childNodes as $optionNode) {
120
            /** @var DOMNode $optionNode */
121
122 1
            while ($optionNode instanceof DOMNode && in_array($optionNode->nodeType, [
123 1
                XML_TEXT_NODE,
124 1
                XML_COMMENT_NODE
125
            ])) {
126 1
                $optionNode = $optionNode->nextSibling;
127
            }
128
129 1
            if ($optionNode instanceof DOMNode) {
130
                /** @var mixed $nodeName */
131 1
                $nodeName = $optionNode->namespaceURI . ":" . $optionNode->localName;
132
133 1
                if ($nodeName === self::RDM_SCHEMA_URI . ":option") {
134
                    /** @var string $determinator */
135 1
                    $determinator = (string)$optionNode->attributes->getNamedItem("name")->nodeValue;
136
137
                    /** @var DOMNode $optionMappingNode */
138 1
                    $optionMappingNode = $optionNode->firstChild;
139
140 1
                    while (in_array($optionMappingNode->nodeType, [XML_TEXT_NODE, XML_COMMENT_NODE])) {
141 1
                        $optionMappingNode = $optionMappingNode->nextSibling;
142
                    }
143
144 1
                    if ($optionMappingNode->nodeName === $optionMappingNode->prefix . ":service") {
145 1
                        $choiceMappings[$determinator] = $this->readService($optionMappingNode, $mappingFile);
146
147
                    } elseif ($optionMappingNode->nodeName === $optionMappingNode->prefix . ":choice") {
148
                        $choiceMappings[$determinator] = $this->readChoice($optionMappingNode, $mappingFile, sprintf(
149
                            "%s_%s",
150
                            $defaultColumnName,
151 1
                            $determinator
152
                        ));
153
                    }
154
155 1
                } elseif ($nodeName === self::DOCTRINE_SCHEMA_URI . ":field") {
156 1
                    $column = $this->readDoctrineField($optionNode);
157
                }
158
            }
159
        }
160
161 1
        return new ChoiceMapping($column, $choiceMappings, sprintf(
162 1
            "in file '%s'",
163 1
            $mappingFile
164
        ));
165
    }
166
167 1
    private function readService(DOMNode $serviceNode, string $mappingFile): ServiceMapping
168
    {
169
        /** @var bool $lax */
170 1
        $lax = false;
171
172 1
        if ($serviceNode->attributes->getNamedItem("lax") instanceof DOMNode) {
173 1
            $lax = strtolower($serviceNode->attributes->getNamedItem("lax")->nodeValue) === 'true';
174
        }
175
176
        /** @var string $serviceId */
177 1
        $serviceId = (string)$serviceNode->attributes->getNamedItem("id")->nodeValue;
178
179 1
        return new ServiceMapping($serviceId, $lax, sprintf(
180 1
            "in file '%s'",
181 1
            $mappingFile
182
        ));
183
    }
184
185 1
    private function readDoctrineField(DOMNode $fieldNode): Column
186
    {
187
        /** @var array<string> $attributes */
188 1
        $attributes = array();
189
190
        /** @var array<string> $keyMap */
191
        $keyMap = array(
192 1
            'name'              => 'name',
193
            'type'              => 'type',
194
            'nullable'          => 'notnull',
195
            'length'            => 'length',
196
            'precision'         => 'precision',
197
            'scale'             => 'scale',
198
            'column-definition' => 'columnDefinition',
199
        );
200
201
        /** @var string $columnName */
202 1
        $columnName = null;
203
204
        /** @var Type $type */
205 1
        $type = null;
206
207 1
        foreach ($fieldNode->attributes as $key => $attribute) {
208
            /** @var DOMAttr $attribute */
209
210 1
            $attributeValue = (string)$attribute->nodeValue;
211
212 1
            if ($key === 'name') {
213 1
                $columnName = $attributeValue;
214
215 1
            } elseif ($key === 'type') {
216 1
                $type = Type::getType($attributeValue);
217
218 1
            } elseif (isset($keyMap[$key])) {
219 1
                if ($key === 'nullable') {
220
                    # target is 'notnull', so falue is reversed
221 1
                    $attributeValue = ($attributeValue === 'false');
222
                }
223
224 1
                $attributes[$keyMap[$key]] = $attributeValue;
225
            }
226
        }
227
228 1
        $column = new Column(
229 1
            $columnName,
230 1
            $type,
231 1
            $attributes
232
        );
233
234 1
        return $column;
235
    }
236
237
}
238