Passed
Branch master (056094)
by Andreas
04:47
created

driver::loadMetadataForClass()   D

Complexity

Conditions 15
Paths 224

Size

Total Lines 98
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 62
CRAP Score 15.0068

Importance

Changes 0
Metric Value
cc 15
eloc 57
c 0
b 0
f 0
nc 224
nop 2
dl 0
loc 98
ccs 62
cts 64
cp 0.9688
crap 15.0068
rs 4.1814

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\Common\Persistence\Mapping\Driver\MappingDriver as driver_interface;
15
use Doctrine\Common\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, $vardir, $namespace = 'midgard\\portable\\entities')
61
    {
62 14
        $this->vardir = $vardir . '/';
63 14
        $this->namespace = $namespace;
64
65 14
        $this->is_fresh_namespace = !array_key_exists($this->namespace, self::$processed_namespaces);
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 14
        } 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()
76
    {
77 10
        return $this->is_fresh_namespace;
78
    }
79
80 10
    public function get_namespace()
81
    {
82 10
        return $this->namespace;
83
    }
84
85 11
    public function get_manager()
86
    {
87 11
        return $this->manager;
88
    }
89
90 10
    public function get_vardir()
91
    {
92 10
        return rtrim($this->vardir, '/');
93
    }
94
95 12
    private function initialize()
96
    {
97 12
        $this->types = $this->manager->get_types();
98 12
    }
99
100
    /**
101
     * {@inheritDoc}
102
     */
103 2
    public function getAllClassNames()
104
    {
105 2
        if ($this->types === null) {
106 1
            $this->initialize();
107 1
        }
108
109 2
        return array_keys($this->types);
110
    }
111
112
    /**
113
     * {@inheritDoc}
114
     */
115 11
    public function isTransient($classname)
116
    {
117 11
        if ($this->types === null) {
118 8
            $this->initialize();
119 8
        }
120 11
        return !array_key_exists($classname, $this->types);
121
    }
122
123
    /**
124
     * {@inheritDoc}
125
     */
126 14
    public function loadMetadataForClass($classname, ClassMetadata $metadata)
127
    {
128 14
        if ($this->types === null) {
129 3
            $this->initialize();
130 3
        }
131 14
        if (!array_key_exists($classname, $this->types)) {
132 3
            throw MappingException::classIsNotAValidEntityOrMappedSuperClass($classname);
133
        }
134
135 14
        $type = $this->types[$classname];
136
137
        // TODO: extends
138
139
        $table = [
140 14
            'name' => $type->table,
141
            'options' => [
142
                //Doctrine's default on MySQL is InnoDB, and the foreign keys don't play well with Midgard logic
143
                //TODO: Maybe at some point we could try to figure out how to explicitly disable foreign key constraint creation instead
144
                'engine' => 'MyISAM'
145 14
            ]
146 14
        ];
147
148 14
        $metadata->setPrimaryTable($table);
149
150 14
        $metadata->midgard['parent'] = $type->parent;
0 ignored issues
show
Bug introduced by
Accessing midgard on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
151 14
        $metadata->midgard['parentfield'] = $type->parentfield;
152 14
        $metadata->midgard['upfield'] = $type->upfield;
153 14
        $metadata->midgard['childtypes'] = $this->manager->get_child_classes($type->name);
154 14
        $metadata->midgard['field_aliases'] = $type->field_aliases;
155
156 14
        foreach ($type->get_properties() as $name => $property) {
157
            // doctrine can handle id links only
158 14
            if (   $property->link
159 14
                && $target_class = $this->manager->resolve_targetclass($property)) {
160
                $link_mapping = [
161 10
                    'fieldName' => $property->name,
162 10
                    'targetEntity' => $target_class,
163
                    'joinColumns' => [
164
                        [
165 10
                            'name' => $property->field,
166 10
                            'referencedColumnName' => $property->link['field']
167 10
                        ]
168 10
                    ],
169 10
                    'midgard:link_target' => $property->link['field'],
170 10
                    'midgard:link_name' => $property->link['target'],
171 10
                ];
172
173 10
                if ($link_mapping['fieldName'] == 'id') {
174
                    $link_mapping['id'] = true;
175
                }
176
177 10
                $metadata->mapManyToOne($link_mapping);
178 10
                continue;
179
            }
180
181 14
            if (empty($this->dbtypemap[$property->dbtype])) {
182 5
                $mapping = $this->parse_dbtype($property);
183 5
            } else {
184 14
                $mapping = $this->dbtypemap[$property->dbtype];
185
            }
186
187 14
            if ($property->unique) {
188 14
                if ($property->name == 'guid') {
189 14
                    $mapping['unique'] = true;
190 14
                } else {
191
                    //we can't set this as a real DB constraint because of softdelete and tree hierarchies
192 6
                    $metadata->midgard['unique_fields'][] = $property->name;
193
                }
194 14
            }
195
196 14
            $mapping['columnName'] = $property->field;
197 14
            $mapping['midgard:midgard_type'] = translator::to_constant($property->type);
198 14
            $mapping['midgard:description'] = $property->description;
199
200
            // its some other link (like guid link)
201 14
            if ($property->noidlink) {
202 4
                $mapping['noidlink'] = $property->noidlink;
203 4
            }
204
205 14
            if ($property->name == $type->primaryfield) {
206 14
                $mapping['id'] = true;
207 14
                unset($mapping['default']);
208 14
                if ($mapping['type'] == Type::INTEGER) {
209 14
                    $metadata->setIdGeneratorType(CM::GENERATOR_TYPE_AUTO);
210 14
                } else {
211 3
                    $metadata->setIdGeneratorType(CM::GENERATOR_TYPE_NONE);
212
                }
213 14
            }
214
215 14
            $mapping['fieldName'] = $name;
216
217 14
            $metadata->mapField($mapping);
218
219 14
            if ($property->index) {
220 13
                if (empty($metadata->table['indexes'])) {
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
221 13
                    $metadata->table['indexes'] = [];
222 13
                }
223 13
                $metadata->table['indexes'][$type->name . '_' . $property->name . '_idx'] = ['columns' => [$property->field]];
224 13
            }
225 14
        }
226 14
    }
227
228 5
    private function parse_dbtype(property $property)
229
    {
230 5
        if (strpos($property->dbtype, 'varchar') === 0) {
231
            $mapping = [
232 5
                'type' => Type::STRING,
233 5
            ];
234
235 5
            if (substr($property->dbtype, -1) == ')') {
236 4
                $mapping['length'] = (int) substr($property->dbtype, 8, -1);
237 4
                return $mapping;
238
            }
239
240 2
            if (substr($property->dbtype, -8) == ') binary') {
241
                // see http://www.doctrine-project.org/jira/browse/DDC-1817
242 2
                $mapping['length'] = (int) substr($property->dbtype, 8, -1);
243 2
                $mapping['comment'] = 'BINARY';
244 2
                return $mapping;
245
            }
246 2
        } elseif (strpos($property->dbtype, 'set') === 0) {
247
            // see http://docs.doctrine-project.org/en/latest/cookbook/mysql-enums.html
248 2
            if (!empty($this->dbtypemap[$property->type])) {
249 2
                $mapping = $this->dbtypemap[$property->type];
250 2
                $mapping['comment'] = $property->dbtype;
251 2
                return $mapping;
252
            }
253
        } elseif (strpos(strtolower($property->dbtype), 'decimal') === 0) {
254
            $matches = [];
255
            preg_match('/DECIMAL\((\d+),(\d+)\)/i', $property->dbtype, $matches);
256
            $mapping = [
257
                'type' => Type::DECIMAL,
258
                'precision' => $matches[1],
259
                'scale' => $matches[2]
260
            ];
261
            return $mapping;
262
        }
263
264
        throw new \Exception($property->get_parent()->name . ': ' . $property->name . ' ' . $property->dbtype . ' not implemented yet');
265
    }
266
}
267