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

driver::get_namespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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