Completed
Push — master ( cb12e8...6c74e8 )
by Andreas
04:28
created

driver::loadMetadataForClass()   C

Complexity

Conditions 13
Paths 58

Size

Total Lines 94
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 48
CRAP Score 13.0014

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 13
eloc 55
c 3
b 0
f 0
nc 58
nop 2
dl 0
loc 94
ccs 48
cts 49
cp 0.9796
crap 13.0014
rs 6.6166

How to fix   Long Method    Complexity   

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
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
4
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
6
 */
7
8
namespace midgard\portable;
9
10
use midgard\portable\mgdschema\manager;
11
use midgard\portable\mgdschema\translator;
12
use midgard\portable\mgdschema\property;
13
use midgard\portable\storage\type\datetime;
14
use Doctrine\Persistence\Mapping\Driver\MappingDriver as driver_interface;
15
use Doctrine\Persistence\Mapping\ClassMetadata;
16
use Doctrine\ORM\Mapping\MappingException;
17
use Doctrine\ORM\Mapping\ClassMetadata as CM;
18
use Doctrine\DBAL\Types\Type;
19
20
class driver implements driver_interface
21
{
22
    private $dbtypemap = [
23
        'unsigned integer' => ['type' => Type::INTEGER, 'default' => 0], // <== UNSIGNED in Doctrine\DBAL\Schema\Column
24
        'integer' => ['type' => Type::INTEGER, 'default' => 0],
25
        'boolean' => ['type' => Type::BOOLEAN, 'default' => false],
26
        'bool' => ['type' => Type::BOOLEAN, 'default' => false],
27
        'guid' => ['type' => Type::STRING, 'length' => 80, 'default' => ''],
28
        'varchar(80)' => ['type' => Type::STRING, 'length' => 80, 'default' => ''],
29
        'string' => ['type' => Type::STRING, 'length' => 255, 'default' => ''],
30
        'datetime' => ['type' => datetime::TYPE, 'default' => '0001-01-01 00:00:00'],
31
        'text' => ['type' => Type::TEXT],
32
        'longtext' => ['type' => Type::TEXT],
33
        'float' => ['type' => Type::FLOAT, 'default' => 0.0],
34
        'double' => ['type' => Type::FLOAT, 'default' => 0.0]
35
    ];
36
37
    private $vardir;
38
39
    private $types;
40
41
    private $namespace;
42
43
    private $manager;
44
45
    /**
46
     * keep track of the namespaces already in use and
47
     * remember the used manager instance for resolving types
48
     *
49
     * @var array
50
     */
51
    private static $processed_namespaces = [];
52
53
    /**
54
     * indicates whether the current namespace has been used before
55
     *
56
     * @var boolean
57
     */
58
    private $is_fresh_namespace;
59
60 14
    public function __construct(array $schemadirs, string $vardir, string $namespace = 'midgard\\portable\\entities')
61
    {
62 14
        $this->vardir = $vardir . '/';
63 14
        $this->namespace = $namespace;
64
65 14
        $this->is_fresh_namespace = !isset(self::$processed_namespaces[$this->namespace]);
66 14
        if ($this->is_fresh_namespace) {
67 14
            $this->manager = new manager($schemadirs, $this->namespace);
68 14
            self::$processed_namespaces[$this->namespace] = ["manager" => $this->manager];
69
        } else {
70
            // reuse manager instance
71
            $this->manager = self::$processed_namespaces[$this->namespace]["manager"];
72
        }
73 14
    }
74
75 10
    public function is_fresh_namespace() : bool
76
    {
77 10
        return $this->is_fresh_namespace;
78
    }
79
80 10
    public function get_namespace() : string
81
    {
82 10
        return $this->namespace;
83
    }
84
85 11
    public function get_manager() : manager
86
    {
87 11
        return $this->manager;
88
    }
89
90 10
    public function get_vardir() : string
91
    {
92 10
        return rtrim($this->vardir, '/');
93
    }
94
95 17
    private function initialize()
96
    {
97 17
        if ($this->types === null) {
98 13
            $this->types = $this->manager->get_types();
99
        }
100 17
    }
101
102
    /**
103
     * {@inheritDoc}
104
     */
105 2
    public function getAllClassNames() : array
106
    {
107 2
        $this->initialize();
108 2
        return array_keys($this->types);
109
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114 13
    public function isTransient($classname) : bool
115
    {
116 13
        $this->initialize();
117 13
        return !isset($this->types[$classname]);
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     */
123 16
    public function loadMetadataForClass($classname, ClassMetadata $metadata)
124
    {
125 16
        $this->initialize();
126 16
        if (!isset($this->types[$classname])) {
127 4
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($classname);
128
        }
129
130 15
        $type = $this->types[$classname];
131
132
        // TODO: extends
133
134
        $table = [
135 15
            'name' => '`' . $type->table . '`',
136
            'options' => [
137
                //Doctrine's default on MySQL is InnoDB, and the foreign keys don't play well with Midgard logic
138
                //TODO: Maybe at some point we could try to figure out how to explicitly disable foreign key constraint creation instead
139
                'engine' => 'MyISAM'
140
            ]
141
        ];
142
143 15
        $metadata->setPrimaryTable($table);
144
145 15
        $properties = $type->get_properties();
146 15
        $metadata->midgard['parent'] = $type->parent;
0 ignored issues
show
Bug introduced by
Accessing midgard on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
147 15
        $metadata->midgard['parentfield'] = $type->parentfield;
148 15
        $metadata->midgard['upfield'] = $type->upfield;
149 15
        $metadata->midgard['childtypes'] = $this->manager->get_child_classes($type->name);
150 15
        $metadata->midgard['field_aliases'] = $type->field_aliases;
151 15
        $metadata->midgard['field_order'] = array_keys($properties);
152
153 15
        foreach ($properties as $name => $property) {
154
            // doctrine can handle id links only
155 15
            if (   $property->link
156 15
                && $target_class = $this->manager->resolve_targetclass($property)) {
157
                $link_mapping = [
158 10
                    'fieldName' => $property->name,
159 10
                    'targetEntity' => $target_class,
160
                    'joinColumns' => [
161
                        [
162 10
                            'name' => $property->field,
163 10
                            'referencedColumnName' => $property->link['field']
164
                        ]
165
                    ],
166 10
                    'midgard:link_target' => $property->link['field'],
167 10
                    'midgard:link_name' => $property->link['target'],
168
                ];
169
170 10
                if ($link_mapping['fieldName'] == 'id') {
171
                    $link_mapping['id'] = true;
172
                }
173
174 10
                $metadata->mapManyToOne($link_mapping);
175 10
                continue;
176
            }
177
178 15
            $mapping = $this->dbtypemap[$property->dbtype] ?? $this->parse_dbtype($property);
179
180 15
            if ($property->unique) {
181 15
                if ($property->name == 'guid') {
182 15
                    $mapping['unique'] = true;
183
                } else {
184
                    //we can't set this as a real DB constraint because of softdelete and tree hierarchies
185 6
                    $metadata->midgard['unique_fields'][] = $property->name;
186
                }
187
            }
188
189 15
            $mapping['columnName'] = $property->field;
190 15
            $mapping['midgard:midgard_type'] = translator::to_constant($property->type);
191 15
            $mapping['midgard:description'] = $property->description;
192
193
            // its some other link (like guid link)
194 15
            if ($property->noidlink) {
195 5
                $mapping['noidlink'] = $property->noidlink;
196
            }
197
198 15
            if ($property->name == $type->primaryfield) {
199 15
                $mapping['id'] = true;
200 15
                unset($mapping['default']);
201 15
                if ($mapping['type'] == Type::INTEGER) {
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Types\Type::INTEGER has been deprecated: Use {@see Types::INTEGER} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

201
                if ($mapping['type'] == /** @scrutinizer ignore-deprecated */ Type::INTEGER) {

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
202 15
                    $metadata->setIdGeneratorType(CM::GENERATOR_TYPE_AUTO);
203
                } else {
204 3
                    $metadata->setIdGeneratorType(CM::GENERATOR_TYPE_NONE);
205
                }
206
            }
207
208 15
            $mapping['fieldName'] = $name;
209
210 15
            $metadata->mapField($mapping);
211
212 15
            if ($property->index) {
213 14
                if (empty($metadata->table['indexes'])) {
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
214 14
                    $metadata->table['indexes'] = [];
215
                }
216 14
                $metadata->table['indexes'][$type->name . '_' . $property->name . '_idx'] = ['columns' => [$property->field]];
217
            }
218
        }
219 15
    }
220
221 5
    private function parse_dbtype(property $property) : array
222
    {
223 5
        if (strpos($property->dbtype, 'varchar') === 0) {
224
            $mapping = [
225 5
                'type' => Type::STRING,
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Types\Type::STRING has been deprecated: Use {@see Types::STRING} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

225
                'type' => /** @scrutinizer ignore-deprecated */ Type::STRING,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
226
            ];
227
228 5
            if (substr($property->dbtype, -1) == ')') {
229 4
                $mapping['length'] = (int) substr($property->dbtype, 8, -1);
230 4
                return $mapping;
231
            }
232
233 2
            if (substr($property->dbtype, -8) == ') binary') {
234
                // see http://www.doctrine-project.org/jira/browse/DDC-1817
235 2
                $mapping['length'] = (int) substr($property->dbtype, 8, -1);
236 2
                $mapping['comment'] = 'BINARY';
237 2
                return $mapping;
238
            }
239 2
        } elseif (strpos($property->dbtype, 'set') === 0) {
240
            // see http://docs.doctrine-project.org/en/latest/cookbook/mysql-enums.html
241 2
            if (!empty($this->dbtypemap[$property->type])) {
242 2
                $mapping = $this->dbtypemap[$property->type];
243 2
                $mapping['comment'] = $property->dbtype;
244 2
                return $mapping;
245
            }
246
        } elseif (strpos(strtolower($property->dbtype), 'decimal') === 0) {
247
            $matches = [];
248
            preg_match('/DECIMAL\((\d+),(\d+)\)/i', $property->dbtype, $matches);
249
            $mapping = [
250
                'type' => Type::DECIMAL,
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Types\Type::DECIMAL has been deprecated: Use {@see Types::DECIMAL} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

250
                'type' => /** @scrutinizer ignore-deprecated */ Type::DECIMAL,

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
251
                'precision' => $matches[1],
252
                'scale' => $matches[2]
253
            ];
254
            return $mapping;
255
        }
256
257
        throw new \Exception($property->get_parent()->name . ': ' . $property->name . ' ' . $property->dbtype . ' not implemented yet');
258
    }
259
}
260