Passed
Branch master (b2f909)
by Andreas
04:01
created

driver::loadMetadataForClass()   C

Complexity

Conditions 13
Paths 58

Size

Total Lines 94
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 47
CRAP Score 13.0015

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 55
c 1
b 0
f 0
nc 58
nop 2
dl 0
loc 94
ccs 47
cts 48
cp 0.9792
crap 13.0015
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 Doctrine\Persistence\Mapping\Driver\MappingDriver as driver_interface;
14
use Doctrine\Persistence\Mapping\ClassMetadata;
15
use Doctrine\ORM\Mapping\MappingException;
16
use Doctrine\ORM\Mapping\ClassMetadata as CM;
17
use Doctrine\DBAL\Types\Types;
18
19
class driver implements driver_interface
20
{
21
    private $dbtypemap = [
22
        'unsigned integer' => ['type' => Types::INTEGER, 'default' => 0], // <== UNSIGNED in Doctrine\DBAL\Schema\Column
23
        'integer' => ['type' => Types::INTEGER, 'default' => 0],
24
        'boolean' => ['type' => Types::BOOLEAN, 'default' => false],
25
        'bool' => ['type' => Types::BOOLEAN, 'default' => false],
26
        'guid' => ['type' => Types::STRING, 'length' => 80, 'default' => ''],
27
        'varchar(80)' => ['type' => Types::STRING, 'length' => 80, 'default' => ''],
28
        'string' => ['type' => Types::STRING, 'length' => 255, 'default' => ''],
29
        'datetime' => ['type' => Types::DATETIME_MUTABLE, 'default' => '0001-01-01 00:00:00'],
30
        'text' => ['type' => Types::TEXT],
31
        'longtext' => ['type' => Types::TEXT],
32
        'float' => ['type' => Types::FLOAT, 'default' => 0.0],
33
        'double' => ['type' => Types::FLOAT, 'default' => 0.0]
34
    ];
35
36
    private $vardir;
37
38
    private $types;
39
40
    private $namespace;
41
42
    private $manager;
43
44
    /**
45
     * keep track of the namespaces already in use and
46
     * remember the used manager instance for resolving types
47
     *
48
     * @var array
49
     */
50
    private static $processed_namespaces = [];
51
52
    /**
53
     * indicates whether the current namespace has been used before
54
     *
55
     * @var boolean
56
     */
57
    private $is_fresh_namespace;
58
59 13
    public function __construct(array $schemadirs, string $vardir, string $namespace = 'midgard\\portable\\entities')
60
    {
61 13
        $this->vardir = $vardir . '/';
62 13
        $this->namespace = $namespace;
63
64 13
        $this->is_fresh_namespace = !isset(self::$processed_namespaces[$this->namespace]);
65 13
        if ($this->is_fresh_namespace) {
66 13
            $this->manager = new manager($schemadirs, $this->namespace);
67 13
            self::$processed_namespaces[$this->namespace] = ["manager" => $this->manager];
68
        } else {
69
            // reuse manager instance
70
            $this->manager = self::$processed_namespaces[$this->namespace]["manager"];
71
        }
72
    }
73
74 9
    public function is_fresh_namespace() : bool
75
    {
76 9
        return $this->is_fresh_namespace;
77
    }
78
79 9
    public function get_namespace() : string
80
    {
81 9
        return $this->namespace;
82
    }
83
84 10
    public function get_manager() : manager
85
    {
86 10
        return $this->manager;
87
    }
88
89 9
    public function get_vardir() : string
90
    {
91 9
        return rtrim($this->vardir, '/');
92
    }
93
94 17
    private function initialize()
95
    {
96 17
        if ($this->types === null) {
97 13
            $this->types = $this->manager->get_types();
98
        }
99
    }
100
101
    /**
102
     * {@inheritDoc}
103
     */
104 2
    public function getAllClassNames() : array
105
    {
106 2
        $this->initialize();
107 2
        return array_keys($this->types);
108
    }
109
110
    /**
111
     * {@inheritDoc}
112
     */
113 13
    public function isTransient($classname) : bool
114
    {
115 13
        $this->initialize();
116 13
        return !isset($this->types[$classname]);
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     */
122 16
    public function loadMetadataForClass($classname, ClassMetadata $metadata)
123
    {
124 16
        $this->initialize();
125 16
        if (!isset($this->types[$classname])) {
126 4
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($classname);
127
        }
128
129 15
        $type = $this->types[$classname];
130
131
        // TODO: extends
132
133
        $table = [
134 15
            'name' => '`' . $type->table . '`',
135
            'options' => [
136
                //Doctrine's default on MySQL is InnoDB, and the foreign keys don't play well with Midgard logic
137
                //TODO: Maybe at some point we could try to figure out how to explicitly disable foreign key constraint creation instead
138
                'engine' => 'MyISAM'
139
            ]
140
        ];
141
142 15
        $metadata->setPrimaryTable($table);
143
144 15
        $properties = $type->get_properties();
145 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...
146 15
        $metadata->midgard['parentfield'] = $type->parentfield;
147 15
        $metadata->midgard['upfield'] = $type->upfield;
148 15
        $metadata->midgard['childtypes'] = $this->manager->get_child_classes($type->name);
149 15
        $metadata->midgard['field_aliases'] = $type->field_aliases;
150 15
        $metadata->midgard['field_order'] = array_keys($properties);
151
152 15
        foreach ($properties as $name => $property) {
153
            // doctrine can handle id links only
154 15
            if (   $property->link
155 15
                && $target_class = $this->manager->resolve_targetclass($property)) {
156
                $link_mapping = [
157 10
                    'fieldName' => $property->name,
158
                    'targetEntity' => $target_class,
159
                    'joinColumns' => [
160
                        [
161 10
                            'name' => $property->field,
162 10
                            'referencedColumnName' => $property->link['field']
163
                        ]
164
                    ],
165 10
                    'midgard:link_target' => $property->link['field'],
166 10
                    'midgard:link_name' => $property->link['target'],
167
                ];
168
169 10
                if ($link_mapping['fieldName'] == 'id') {
170
                    $link_mapping['id'] = true;
171
                }
172
173 10
                $metadata->mapManyToOne($link_mapping);
174 10
                continue;
175
            }
176
177 15
            $mapping = $this->dbtypemap[$property->dbtype] ?? $this->parse_dbtype($property);
178
179 15
            if ($property->unique) {
180 15
                if ($property->name == 'guid') {
181 15
                    $mapping['unique'] = true;
182
                } else {
183
                    //we can't set this as a real DB constraint because of softdelete and tree hierarchies
184 6
                    $metadata->midgard['unique_fields'][] = $property->name;
185
                }
186
            }
187
188 15
            $mapping['columnName'] = $property->field;
189 15
            $mapping['midgard:midgard_type'] = translator::to_constant($property->type);
190 15
            $mapping['midgard:description'] = $property->description;
191
192
            // its some other link (like guid link)
193 15
            if ($property->noidlink) {
194 5
                $mapping['noidlink'] = $property->noidlink;
195
            }
196
197 15
            if ($property->name == $type->primaryfield) {
198 15
                $mapping['id'] = true;
199 15
                unset($mapping['default']);
200 15
                if ($mapping['type'] == Types::INTEGER) {
201 15
                    $metadata->setIdGeneratorType(CM::GENERATOR_TYPE_AUTO);
202
                } else {
203 3
                    $metadata->setIdGeneratorType(CM::GENERATOR_TYPE_NONE);
204
                }
205
            }
206
207 15
            $mapping['fieldName'] = $name;
208
209 15
            $metadata->mapField($mapping);
210
211 15
            if ($property->index) {
212 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...
213 14
                    $metadata->table['indexes'] = [];
214
                }
215 14
                $metadata->table['indexes'][$type->name . '_' . $property->name . '_idx'] = ['columns' => [$property->field]];
216
            }
217
        }
218
    }
219
220 5
    private function parse_dbtype(property $property) : array
221
    {
222 5
        if (strpos($property->dbtype, 'varchar') === 0) {
223
            $mapping = [
224
                'type' => Types::STRING,
225
            ];
226
227 5
            if (substr($property->dbtype, -1) == ')') {
228 4
                $mapping['length'] = (int) substr($property->dbtype, 8, -1);
229 4
                return $mapping;
230
            }
231
232 2
            if (substr($property->dbtype, -8) == ') binary') {
233
                // see http://www.doctrine-project.org/jira/browse/DDC-1817
234 2
                $mapping['length'] = (int) substr($property->dbtype, 8, -1);
235 2
                $mapping['comment'] = 'BINARY';
236 2
                return $mapping;
237
            }
238 2
        } elseif (strpos($property->dbtype, 'set') === 0) {
239
            // see http://docs.doctrine-project.org/en/latest/cookbook/mysql-enums.html
240 2
            if (!empty($this->dbtypemap[$property->type])) {
241 2
                $mapping = $this->dbtypemap[$property->type];
242 2
                $mapping['comment'] = $property->dbtype;
243 2
                return $mapping;
244
            }
245
        } elseif (strpos(strtolower($property->dbtype), 'decimal') === 0) {
246
            $matches = [];
247
            preg_match('/DECIMAL\((\d+),(\d+)\)/i', $property->dbtype, $matches);
248
            $mapping = [
249
                'type' => Types::DECIMAL,
250
                'precision' => $matches[1],
251
                'scale' => $matches[2]
252
            ];
253
            return $mapping;
254
        }
255
256
        throw new \Exception($property->get_parent()->name . ': ' . $property->name . ' ' . $property->dbtype . ' not implemented yet');
257
    }
258
}
259