|
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 = !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
|
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 !isset($this->types[$classname]); |
|
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 (!isset($this->types[$classname])) { |
|
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; |
|
|
|
|
|
|
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 |
|
$mapping = $this->dbtypemap[$property->dbtype] ?? $this->parse_dbtype($property); |
|
182
|
5 |
|
|
|
183
|
5 |
|
if ($property->unique) { |
|
184
|
14 |
|
if ($property->name == 'guid') { |
|
185
|
|
|
$mapping['unique'] = true; |
|
186
|
|
|
} else { |
|
187
|
14 |
|
//we can't set this as a real DB constraint because of softdelete and tree hierarchies |
|
188
|
14 |
|
$metadata->midgard['unique_fields'][] = $property->name; |
|
189
|
14 |
|
} |
|
190
|
14 |
|
} |
|
191
|
|
|
|
|
192
|
4 |
|
$mapping['columnName'] = $property->field; |
|
193
|
|
|
$mapping['midgard:midgard_type'] = translator::to_constant($property->type); |
|
194
|
14 |
|
$mapping['midgard:description'] = $property->description; |
|
195
|
|
|
|
|
196
|
14 |
|
// its some other link (like guid link) |
|
197
|
14 |
|
if ($property->noidlink) { |
|
198
|
14 |
|
$mapping['noidlink'] = $property->noidlink; |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
14 |
|
if ($property->name == $type->primaryfield) { |
|
202
|
4 |
|
$mapping['id'] = true; |
|
203
|
4 |
|
unset($mapping['default']); |
|
204
|
|
|
if ($mapping['type'] == Type::INTEGER) { |
|
|
|
|
|
|
205
|
14 |
|
$metadata->setIdGeneratorType(CM::GENERATOR_TYPE_AUTO); |
|
206
|
14 |
|
} else { |
|
207
|
14 |
|
$metadata->setIdGeneratorType(CM::GENERATOR_TYPE_NONE); |
|
208
|
14 |
|
} |
|
209
|
14 |
|
} |
|
210
|
14 |
|
|
|
211
|
3 |
|
$mapping['fieldName'] = $name; |
|
212
|
|
|
|
|
213
|
14 |
|
$metadata->mapField($mapping); |
|
214
|
|
|
|
|
215
|
14 |
|
if ($property->index) { |
|
216
|
|
|
if (empty($metadata->table['indexes'])) { |
|
|
|
|
|
|
217
|
14 |
|
$metadata->table['indexes'] = []; |
|
218
|
|
|
} |
|
219
|
14 |
|
$metadata->table['indexes'][$type->name . '_' . $property->name . '_idx'] = ['columns' => [$property->field]]; |
|
220
|
13 |
|
} |
|
221
|
13 |
|
} |
|
222
|
13 |
|
} |
|
223
|
13 |
|
|
|
224
|
13 |
|
private function parse_dbtype(property $property) |
|
225
|
14 |
|
{ |
|
226
|
14 |
|
if (strpos($property->dbtype, 'varchar') === 0) { |
|
227
|
|
|
$mapping = [ |
|
228
|
5 |
|
'type' => Type::STRING, |
|
|
|
|
|
|
229
|
|
|
]; |
|
230
|
5 |
|
|
|
231
|
|
|
if (substr($property->dbtype, -1) == ')') { |
|
232
|
5 |
|
$mapping['length'] = (int) substr($property->dbtype, 8, -1); |
|
233
|
5 |
|
return $mapping; |
|
234
|
|
|
} |
|
235
|
5 |
|
|
|
236
|
4 |
|
if (substr($property->dbtype, -8) == ') binary') { |
|
237
|
4 |
|
// see http://www.doctrine-project.org/jira/browse/DDC-1817 |
|
238
|
|
|
$mapping['length'] = (int) substr($property->dbtype, 8, -1); |
|
239
|
|
|
$mapping['comment'] = 'BINARY'; |
|
240
|
2 |
|
return $mapping; |
|
241
|
|
|
} |
|
242
|
2 |
|
} elseif (strpos($property->dbtype, 'set') === 0) { |
|
243
|
2 |
|
// see http://docs.doctrine-project.org/en/latest/cookbook/mysql-enums.html |
|
244
|
2 |
|
if (!empty($this->dbtypemap[$property->type])) { |
|
245
|
|
|
$mapping = $this->dbtypemap[$property->type]; |
|
246
|
2 |
|
$mapping['comment'] = $property->dbtype; |
|
247
|
|
|
return $mapping; |
|
248
|
2 |
|
} |
|
249
|
2 |
|
} elseif (strpos(strtolower($property->dbtype), 'decimal') === 0) { |
|
250
|
2 |
|
$matches = []; |
|
251
|
2 |
|
preg_match('/DECIMAL\((\d+),(\d+)\)/i', $property->dbtype, $matches); |
|
252
|
|
|
$mapping = [ |
|
253
|
|
|
'type' => Type::DECIMAL, |
|
|
|
|
|
|
254
|
|
|
'precision' => $matches[1], |
|
255
|
|
|
'scale' => $matches[2] |
|
256
|
|
|
]; |
|
257
|
|
|
return $mapping; |
|
258
|
|
|
} |
|
259
|
|
|
|
|
260
|
|
|
throw new \Exception($property->get_parent()->name . ': ' . $property->name . ' ' . $property->dbtype . ' not implemented yet'); |
|
261
|
|
|
} |
|
262
|
|
|
} |
|
263
|
|
|
|
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.