Total Complexity | 122 |
Total Lines | 711 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Schema often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Schema, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | class Schema |
||
35 | { |
||
36 | public const TYPE_INTEGER = 'integer'; |
||
37 | public const TYPE_FLOAT = 'float'; |
||
38 | public const TYPE_DECIMAL = 'decimal'; |
||
39 | public const TYPE_STRING = 'string'; |
||
40 | public const TYPE_TEXT = 'text'; |
||
41 | public const TYPE_DATE = 'date'; |
||
42 | public const TYPE_TIME = 'time'; |
||
43 | public const TYPE_DATETIME = 'datetime'; |
||
44 | public const TYPE_BOOLEAN = 'bool'; |
||
45 | |||
46 | public const TYPES = [ |
||
47 | self::TYPE_INTEGER => ['tinyint', 'smallint', 'mediumint', 'int', 'bigint'], |
||
48 | self::TYPE_FLOAT => ['real', 'double'], |
||
49 | self::TYPE_DECIMAL => ['decimal', 'numeric'], |
||
50 | self::TYPE_STRING => ['char', 'varchar'], |
||
51 | self::TYPE_TEXT => ['tinytext', 'text', 'mediumtext', 'longtext', 'blob'], |
||
52 | self::TYPE_DATE => ['date'], |
||
53 | self::TYPE_TIME => ['time'], |
||
54 | self::TYPE_DATETIME => ['datetime', 'timestamp'], |
||
55 | self::TYPE_BOOLEAN => ['boolean'], |
||
56 | ]; |
||
57 | public const YAML_CACHE_TABLES_FOLDER = 'models'; |
||
58 | |||
59 | /** |
||
60 | * Carriage Return and Line Feed |
||
61 | */ |
||
62 | const CRLF = "\r\n"; |
||
63 | const DB_INDEX_TYPE = 'bigint (20) unsigned'; |
||
64 | |||
65 | public static array $tables = []; |
||
66 | |||
67 | /** |
||
68 | * Contains the database structure data. |
||
69 | * Each table is an index of the associative array. |
||
70 | * |
||
71 | * @var array |
||
72 | */ |
||
73 | public static array $bbddStructure; |
||
74 | |||
75 | /** |
||
76 | * Return true if $tableName exists in database |
||
77 | * |
||
78 | * @param string $tableName |
||
79 | * |
||
80 | * @return bool |
||
81 | * @throws DebugBarException |
||
82 | */ |
||
83 | public static function tableExists($tableName): bool |
||
93 | } |
||
94 | |||
95 | private static function getFields($tableName): array |
||
96 | { |
||
97 | $yamlFilename = PhpFileCache::getYamlFileName(self::YAML_CACHE_TABLES_FOLDER, $tableName); |
||
98 | if (file_exists($yamlFilename)) { |
||
99 | return PhpFileCache::loadYamlFile(self::YAML_CACHE_TABLES_FOLDER, $tableName); |
||
100 | } |
||
101 | |||
102 | if (empty(self::$tables)) { |
||
103 | self::$tables = YamlSchema::getTables(); |
||
104 | } |
||
105 | |||
106 | $yamlSourceFilename = self::$tables[$tableName]; |
||
107 | if (!file_exists($yamlSourceFilename)) { |
||
108 | dump('No existe el archivo ' . $yamlSourceFilename); |
||
109 | } |
||
110 | |||
111 | $data = Yaml::parseFile($yamlSourceFilename); |
||
112 | |||
113 | $result = []; |
||
114 | foreach ($data as $key => $datum) { |
||
115 | $datum['key'] = $key; |
||
116 | $result[$key] = Schema::normalize($datum); |
||
117 | } |
||
118 | |||
119 | /* |
||
120 | Igual conviene crear una clase: |
||
121 | - DBSchema (con los datos de la base de datos real) |
||
122 | - DefinedSchema (con los datos definidos) |
||
123 | y que Schema cree o adapte según los datos de ambas. Que cada una lleve lo suyo |
||
124 | |||
125 | Que el resultado se guarde en el yaml y que se encargue de realizar las conversines |
||
126 | oportunas siempre que no suponga una pérdida de datos. |
||
127 | */ |
||
128 | |||
129 | return $result; |
||
130 | } |
||
131 | |||
132 | private static function getIndexes($tableName): array |
||
133 | { |
||
134 | $result = []; |
||
135 | return $result; |
||
136 | } |
||
137 | |||
138 | private static function getRelated($tableName): array |
||
139 | { |
||
140 | $result = []; |
||
141 | return $result; |
||
142 | } |
||
143 | |||
144 | private static function getSeed($tableName): array |
||
145 | { |
||
146 | $result = []; |
||
147 | return $result; |
||
148 | } |
||
149 | |||
150 | public static function checkStructure(string $tableName, bool $create) |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * Normalize an array that has the file structure defined in the model by setStructure, |
||
168 | * so that it has fields with all the values it must have. Those that do not exist are |
||
169 | * created with the default value, avoiding having to do the check each time, or |
||
170 | * calculating their value based on the data provided by the other fields. |
||
171 | * |
||
172 | * It also ensures that the keys and default values exist as an empty array if they |
||
173 | * did not exist. |
||
174 | * |
||
175 | * @param array $structure |
||
176 | * @param string $tableName |
||
177 | * |
||
178 | * @return array |
||
179 | */ |
||
180 | public static function setNormalizedStructure(array $structure, string $tableName): array |
||
181 | { |
||
182 | $ret['keys'] = $structure['keys'] ?? []; |
||
183 | $ret['values'] = $structure['values'] ?? []; |
||
184 | foreach ($structure['fields'] as $key => $value) { |
||
185 | $ret['fields'][$key] = self::normalizeField($tableName, $key, $value); |
||
186 | } |
||
187 | return $ret; |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * Take the definition of a field, and make sure you have all the information |
||
192 | * that is necessary for its creation or maintenance, calculating the missing |
||
193 | * data if possible. |
||
194 | * It can cause an exception if some vital data is missing, but this should |
||
195 | * only occur at the design stage. |
||
196 | * |
||
197 | * @param string $tableName |
||
198 | * @param string $field |
||
199 | * @param array $structure |
||
200 | * |
||
201 | * @return array |
||
202 | */ |
||
203 | protected static function normalizeField(string $tableName, string $field, array $structure): array |
||
204 | { |
||
205 | if (!isset($structure['type'])) { |
||
206 | dump("The type parameter is mandatory in {$field}. Error in table " . $tableName); |
||
207 | dump($structure); |
||
208 | } |
||
209 | |||
210 | $dbType = $structure['type']; |
||
211 | |||
212 | if ($dbType == 'boolean') { |
||
213 | $dbType = 'tinyint'; |
||
214 | $structure['min'] = 0; |
||
215 | $structure['max'] = 1; |
||
216 | } |
||
217 | |||
218 | if ($dbType == 'int' || $dbType == 'tinyint' || $dbType == 'number') { |
||
219 | $type = 'number'; |
||
220 | } else { |
||
221 | if ($dbType == 'float') { |
||
222 | $type = 'float'; |
||
223 | } else { |
||
224 | if ($dbType == 'double') { |
||
225 | $type = 'double'; |
||
226 | } else { |
||
227 | if ($dbType == 'char' || $dbType == 'varchar' || $dbType == 'text') { |
||
228 | $type = 'text'; |
||
229 | } else { |
||
230 | if ($dbType == 'date') { |
||
231 | $type = 'date'; |
||
232 | } else { |
||
233 | if ($dbType == 'datetime' || $dbType == 'timestamp') { |
||
234 | $type = 'datetime-local'; |
||
235 | } else { |
||
236 | echo "<p>Check Schema.normalizeField if you think that {$dbType} might be necessary.</p>"; |
||
237 | die("Type {$dbType} is not valid for field {$field} of table {$tableName}"); |
||
238 | } |
||
239 | } |
||
240 | } |
||
241 | } |
||
242 | } |
||
243 | } |
||
244 | |||
245 | $min = (isset($structure['min'])) ? $structure['min'] : 0; |
||
246 | $max = (isset($structure['max'])) ? $structure['max'] : 0; |
||
247 | $default = (isset($structure['default'])) ? $structure['default'] : null; |
||
248 | $label = (isset($structure['label'])) ? $structure['label'] : $field; |
||
249 | $unsigned = (!isset($structure['unsigned']) || $structure['unsigned'] == true); |
||
250 | $null = ((isset($structure['null'])) && $structure['null'] == true); |
||
251 | |||
252 | $ret = []; |
||
253 | if ($type == 'text') { |
||
254 | if ($max == 0) { |
||
255 | $max = DEFAULT_STRING_LENGTH; |
||
256 | } |
||
257 | $dbType = "$dbType($max)"; |
||
258 | $ret['pattern'] = '.{' . $min . ',' . $max . '}'; |
||
259 | } else { |
||
260 | if ($type == 'number') { |
||
261 | if ($default === true) { |
||
262 | $default = '1'; |
||
263 | } |
||
264 | if ($max == 0) { |
||
265 | $tmpLength = DEFAULT_INTEGER_SIZE; |
||
266 | $max = pow(10, $tmpLength) - 1; |
||
267 | } else { |
||
268 | $tmpLength = strlen($max); |
||
269 | } |
||
270 | |||
271 | if ($min == 0) { |
||
272 | $min = $unsigned ? 0 : -$max; |
||
273 | } else { |
||
274 | if ($tmpLength < strlen($min)) { |
||
275 | $tmpLength = strlen($min); |
||
276 | } |
||
277 | } |
||
278 | |||
279 | if (isset($structure['decimals'])) { |
||
280 | $decimales = $structure['decimals']; |
||
281 | $precision = pow(10, -$decimales); |
||
282 | $tmpLength += $decimales; |
||
283 | $dbType = "decimal($tmpLength,$decimales)" . ($unsigned ? ' unsigned' : ''); |
||
284 | $ret['min'] = $min == 0 ? 0 : ($min < 0 ? $min - 1 + $precision : $min + 1 - $precision); |
||
285 | $ret['max'] = $max > 0 ? $max + 1 - $precision : $max - 1 + $precision; |
||
286 | } else { |
||
287 | $precision = null; |
||
288 | $dbType = "integer($tmpLength)" . ($unsigned ? ' unsigned' : ''); |
||
289 | $ret['min'] = $min; |
||
290 | $ret['max'] = $max; |
||
291 | } |
||
292 | } |
||
293 | } |
||
294 | |||
295 | $ret['type'] = $type; |
||
296 | $ret['dbtype'] = $dbType; |
||
297 | $ret['default'] = $default; |
||
298 | $ret['null'] = $null; |
||
299 | $ret['label'] = $label; |
||
300 | if (isset($precision)) { |
||
301 | $ret['step'] = $precision; |
||
302 | } |
||
303 | if (isset($structure['key'])) { |
||
304 | $ret['key'] = $structure['key']; |
||
305 | } |
||
306 | if (isset($structure['placeholder'])) { |
||
307 | $ret['placeholder'] = $structure['placeholder']; |
||
308 | } |
||
309 | if (isset($structure['extra'])) { |
||
310 | $ret['extra'] = $structure['extra']; |
||
311 | } |
||
312 | if (isset($structure['help'])) { |
||
313 | $ret['help'] = $structure['help']; |
||
314 | } |
||
315 | if (isset($structure['unique']) && $structure['unique']) { |
||
316 | $ret['unique'] = $structure['unique']; |
||
317 | } |
||
318 | |||
319 | if (isset($structure['relations'][$field]['table'])) { |
||
320 | $ret['relation'] = [ |
||
321 | 'table' => $structure['relations'][$field]['table'], |
||
322 | 'id' => isset($structure['relations'][$field]['id']) ? $structure['relations'][$field]['id'] : 'id', |
||
323 | 'name' => isset($structure['relations'][$field]['name']) ? $structure['relations'][$field]['name'] : 'name', |
||
324 | ]; |
||
325 | } |
||
326 | |||
327 | return $ret; |
||
328 | } |
||
329 | |||
330 | private static function getTypeOf(string $type): string |
||
331 | { |
||
332 | foreach (self::TYPES as $index => $types) { |
||
333 | if (in_array(strtolower($type), $types)) { |
||
334 | return $index; |
||
335 | } |
||
336 | } |
||
337 | Debug::addMessage('messages', $type . ' not found in DBSchema::getTypeOf()'); |
||
338 | return 'text'; |
||
339 | } |
||
340 | |||
341 | private static function splitType(string $originalType): array |
||
342 | { |
||
343 | $replacesSources = [ |
||
344 | 'character varying', |
||
345 | // 'timestamp without time zone', |
||
346 | 'double precision', |
||
347 | ]; |
||
348 | $replacesDestination = [ |
||
349 | 'varchar', |
||
350 | // 'timestamp', |
||
351 | 'double', |
||
352 | ]; |
||
353 | $modifiedType = (str_replace($replacesSources, $replacesDestination, $originalType)); |
||
354 | |||
355 | if ($originalType !== $modifiedType) { |
||
356 | Debug::addMessage('messages', "XML: Uso de '{$originalType}' en lugar de '{$modifiedType}'."); |
||
357 | } |
||
358 | $explode = explode(' ', strtolower($modifiedType)); |
||
359 | |||
360 | $pos = strpos($explode[0], '('); |
||
361 | if ($pos > 0) { |
||
362 | $begin = $pos + 1; |
||
363 | $end = strpos($explode[0], ')'); |
||
364 | $type = substr($explode[0], 0, $pos); |
||
365 | $length = substr($explode[0], $begin, $end - $begin); |
||
366 | } else { |
||
367 | $type = $explode[0]; |
||
368 | $length = null; |
||
369 | } |
||
370 | |||
371 | $pos = array_search('unsigned', $explode, true); |
||
372 | $unsigned = $pos ? 'yes' : 'no'; |
||
373 | |||
374 | $pos = array_search('zerofill', $explode, true); |
||
375 | $zerofill = $pos ? 'yes' : 'no'; |
||
376 | |||
377 | return ['type' => $type, 'length' => $length, 'unsigned' => $unsigned, 'zerofill' => $zerofill]; |
||
378 | } |
||
379 | |||
380 | /** |
||
381 | * Toma los datos del fichero de definición de una tabla y genera el definitivo. |
||
382 | * |
||
383 | * @author Rafael San José Tovar <[email protected]> |
||
384 | * @version 2022.1224 |
||
385 | * |
||
386 | * @param array $structure |
||
387 | * |
||
388 | * @return array |
||
389 | */ |
||
390 | protected static function normalize(array $structure): array |
||
391 | { |
||
392 | $column = []; |
||
393 | $key = (string) $structure['key']; |
||
394 | $type = (string) $structure['type']; |
||
395 | $column['key'] = $key; |
||
396 | |||
397 | /** |
||
398 | * Entrada: |
||
399 | * - type es el tipo lógico del campo y tiene que estar definido como índice en |
||
400 | * TYPES, o ser uno de los predefinidos como 'autoincrement', 'relationship', etc. |
||
401 | * |
||
402 | * Salida: |
||
403 | * - type queda intacto. |
||
404 | * - dbtype es como queda definido en la tabla, por ejemplo, varchar(20) |
||
405 | * - realtype es el tipo resultado, por ejemplo varchar (sin el tamaño) |
||
406 | * - generictype es uno de los índices de TYPE. P.E. autoincrement se cambiará por integer |
||
407 | * |
||
408 | */ |
||
409 | |||
410 | $column['type'] = $type; |
||
411 | switch ($type) { |
||
412 | case 'autoincrement': |
||
413 | case 'relationship': |
||
414 | $colType = self::DB_INDEX_TYPE; |
||
415 | break; |
||
416 | case 'boolean': |
||
417 | $colType = 'tinyint(1) unsigned'; |
||
418 | break; |
||
419 | default: |
||
420 | $colType = $type; |
||
421 | } |
||
422 | |||
423 | $typeArray = static::splitType($colType); |
||
424 | /** |
||
425 | * ^ array:4 [▼ |
||
426 | * "type" => "bigint" |
||
427 | * "length" => null |
||
428 | * "unsigned" => "yes" |
||
429 | * "zerofill" => "no" |
||
430 | * ] |
||
431 | */ |
||
432 | $type = $typeArray['type']; |
||
433 | $length = $typeArray['length'] ?? $structure['length']; |
||
434 | $unsigned = $typeArray['unsigned'] === 'yes'; |
||
435 | $zerofill = $typeArray['zerofill'] === 'yes'; |
||
436 | $genericType = static::getTypeOf($type); |
||
437 | |||
438 | $column['dbtype'] = $colType; |
||
439 | $column['realtype'] = $type; |
||
440 | $column['generictype'] = $genericType; |
||
441 | |||
442 | $column['null'] = 'YES'; |
||
443 | if ($structure['null'] && mb_strtolower($structure['null']) == 'no') { |
||
444 | $column['null'] = 'NO'; |
||
445 | } |
||
446 | |||
447 | if (empty($structure['default'])) { |
||
448 | $column['default'] = null; |
||
449 | } else { |
||
450 | $column['default'] = (string) $structure['default']; |
||
451 | } |
||
452 | |||
453 | /** |
||
454 | * Pueden existir otras definiciones de limitaciones físicas como min y max |
||
455 | * De existir, tienen que ser contempladas en el método test y tener mayor peso que |
||
456 | * la limitación en plantilla. |
||
457 | */ |
||
458 | foreach (['min', 'max'] as $field) { |
||
459 | if (isset($structure[$field])) { |
||
460 | $column[$field] = (string) $structure[$field]; |
||
461 | } |
||
462 | } |
||
463 | |||
464 | if (isset($structure['comment'])) { |
||
465 | $column['comentario'] = (string) $structure['comment']; |
||
466 | } |
||
467 | |||
468 | if (isset($structure['default'])) { |
||
469 | $column['default'] = trim($structure['default'], " \"'`"); |
||
470 | } |
||
471 | |||
472 | switch ($genericType) { |
||
473 | case 'text': |
||
474 | $column['dbtype'] = 'varchar(' . $length . ')'; |
||
475 | $column['maxlength'] = $length; |
||
476 | break; |
||
477 | case 'integer': |
||
478 | /** |
||
479 | * Lo primero es ver la capacidad física máxima según el tipo de dato. |
||
480 | */ |
||
481 | $bytes = 4; |
||
482 | switch ($type) { |
||
483 | case 'tinyint': |
||
484 | $bytes = 1; |
||
485 | break; |
||
486 | case 'smallint': |
||
487 | $bytes = 2; |
||
488 | break; |
||
489 | case 'mediumint': |
||
490 | $bytes = 3; |
||
491 | break; |
||
492 | case 'int': |
||
493 | $bytes = 4; |
||
494 | break; |
||
495 | case 'bigint': |
||
496 | $bytes = 8; |
||
497 | break; |
||
498 | } |
||
499 | $bits = 8 * (int) $bytes; |
||
500 | $physicalMaxLength = 2 ** $bits; |
||
501 | |||
502 | /** |
||
503 | * $minDataLength y $maxDataLength contendrán el mínimo y máximo valor que puede contener el campo. |
||
504 | */ |
||
505 | $minDataLength = $unsigned ? 0 : -$physicalMaxLength / 2; |
||
506 | $maxDataLength = ($unsigned ? $physicalMaxLength : $physicalMaxLength / 2) - 1; |
||
507 | |||
508 | /** |
||
509 | * De momento, se asignan los límites máximos por el tipo de dato. |
||
510 | * En $min y $max, iremos arrastrando los límites conforme se vayan comprobando. |
||
511 | * $min nunca podrá ser menor que $minDataLength. |
||
512 | * $max nunca podrá ser mayor que $maxDataLength. |
||
513 | */ |
||
514 | $min = $minDataLength; |
||
515 | $max = $maxDataLength; |
||
516 | |||
517 | /** |
||
518 | * Se puede hacer una limitación física Se puede haber definido en el xml un min y un max. |
||
519 | * A todos los efectos, lo definido en el XML como min o max se toma como limitación |
||
520 | * física del campo. |
||
521 | */ |
||
522 | if (isset($structure['min'])) { |
||
523 | $minXmlLength = $structure['min']; |
||
524 | if ($minXmlLength > $minDataLength) { |
||
525 | $min = $minXmlLength; |
||
526 | } else { |
||
527 | Debug::addMessage('messages', "({$key}): Se ha especificado un min {$minXmlLength} en el XML, pero por el tipo de datos, el mínimo es {$minDataLength}."); |
||
528 | } |
||
529 | } |
||
530 | if (isset($structure['max'])) { |
||
531 | $maxXmlLength = $structure['max']; |
||
532 | if ($maxXmlLength < $maxDataLength) { |
||
533 | $max = $maxXmlLength; |
||
534 | } else { |
||
535 | Debug::addMessage('messages', "({$key}): Se ha especificado un min {$maxXmlLength} en el XML, pero por el tipo de datos, el máximo es {$maxDataLength}."); |
||
536 | } |
||
537 | } |
||
538 | |||
539 | $column['min'] = $min; |
||
540 | $column['max'] = $max; |
||
541 | break; |
||
542 | default: |
||
543 | // ??? |
||
544 | } |
||
545 | |||
546 | return $column; |
||
547 | } |
||
548 | |||
549 | /** |
||
550 | * Create a table in the database. |
||
551 | * Build the default fields, indexes and values defined in the model. |
||
552 | * |
||
553 | * @param string $tableName |
||
554 | * |
||
555 | * @return bool |
||
556 | * @throws DebugBarException |
||
557 | */ |
||
558 | public static function createTable(string $tableName): bool |
||
559 | { |
||
560 | $tabla = self::$bbddStructure[$tableName]; |
||
561 | $sql = self::createFields($tableName, $tabla['fields']); |
||
562 | |||
563 | foreach ($tabla['keys'] as $name => $index) { |
||
564 | $sql .= self::createIndex($tableName, $name, $index); |
||
565 | } |
||
566 | if (isset($tabla['values'])) { |
||
567 | $sql .= self::setValues($tableName, $tabla['values']); |
||
568 | } |
||
569 | |||
570 | return Engine::exec($sql); |
||
571 | } |
||
572 | |||
573 | /** |
||
574 | * Build the SQL statement to create the fields in the table. |
||
575 | * It can also create the primary key if the auto_increment attribute is defined. |
||
576 | * |
||
577 | * @param string $tablename |
||
578 | * @param array $fieldList |
||
579 | * |
||
580 | * @return string |
||
581 | */ |
||
582 | protected static function createFields(string $tablename, array $fieldList): string |
||
583 | { |
||
584 | $tablenameWithPrefix = Config::$dbPrefix . $tablename; |
||
585 | |||
586 | $sql = "CREATE TABLE $tablenameWithPrefix ( "; |
||
587 | foreach ($fieldList as $index => $col) { |
||
588 | if (!isset($col['dbtype'])) { |
||
589 | die('Tipo no especificado en createTable ' . $index); |
||
590 | } |
||
591 | |||
592 | $sql .= '`' . $index . '` ' . $col['dbtype']; |
||
593 | $nulo = isset($col['null']) && $col['null']; |
||
594 | |||
595 | $sql .= ($nulo ? '' : ' NOT') . ' NULL'; |
||
596 | |||
597 | if (isset($col['extra']) && (strtolower($col['extra']) == 'auto_increment')) { |
||
598 | $sql .= ' PRIMARY KEY AUTO_INCREMENT'; |
||
599 | } |
||
600 | |||
601 | $tmpDefecto = $col['default'] ?? null; |
||
602 | $defecto = ''; |
||
603 | if (isset($tmpDefecto)) { |
||
604 | if ($tmpDefecto == 'CURRENT_TIMESTAMP') { |
||
605 | $defecto = "$tmpDefecto"; |
||
606 | } else { |
||
607 | $defecto = "'$tmpDefecto'"; |
||
608 | } |
||
609 | } else { |
||
610 | if ($nulo) { |
||
611 | $defecto = 'NULL'; |
||
612 | } |
||
613 | } |
||
614 | |||
615 | if ($defecto != '') { |
||
616 | $sql .= ' DEFAULT ' . $defecto; |
||
617 | } |
||
618 | |||
619 | $sql .= ', '; |
||
620 | } |
||
621 | $sql = substr($sql, 0, -2); // Quitamos la coma y el espacio del final |
||
622 | $sql .= ') ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;' . self::CRLF; |
||
623 | |||
624 | return $sql; |
||
625 | } |
||
626 | |||
627 | /** |
||
628 | * Create the SQL statements for the construction of one index. |
||
629 | * In the case of the primary index, it is not necessary if it is auto_increment. |
||
630 | * |
||
631 | * TODO: |
||
632 | * |
||
633 | * Moreover, it should not be defined if it is auto_increment because it would |
||
634 | * generate an error when it already exists. |
||
635 | * |
||
636 | * @param string $tableName |
||
637 | * @param string $indexname |
||
638 | * @param array $indexData |
||
639 | * |
||
640 | * @return string |
||
641 | */ |
||
642 | protected static function createIndex($tableName, $indexname, $indexData) |
||
643 | { |
||
644 | $sql = "ALTER TABLE $tableName ADD CONSTRAINT $indexname "; |
||
645 | |||
646 | $command = ''; |
||
647 | // https://www.w3schools.com/sql/sql_primarykey.asp |
||
648 | // ALTER TABLE Persons ADD CONSTRAINT PK_Person PRIMARY KEY (ID,LastName); |
||
649 | if (isset($indexData['PRIMARY'])) { |
||
650 | $command = 'PRIMARY KEY '; |
||
651 | $fields = $indexData['PRIMARY']; |
||
652 | } |
||
653 | |||
654 | // https://www.w3schools.com/sql/sql_create_index.asp |
||
655 | // CREATE INDEX idx_pname ON Persons (LastName, FirstName); |
||
656 | if (isset($indexData['INDEX'])) { |
||
657 | $command = 'INDEX '; |
||
658 | $fields = $indexData['INDEX']; |
||
659 | } |
||
660 | |||
661 | // https://www.w3schools.com/sql/sql_unique.asp |
||
662 | // ALTER TABLE Persons ADD CONSTRAINT UC_Person UNIQUE (ID,LastName); |
||
663 | if (isset($indexData['UNIQUE'])) { |
||
664 | $command = 'UNIQUE INDEX '; |
||
665 | $fields = $indexData['UNIQUE']; |
||
666 | } |
||
667 | |||
668 | if ($command == '') { |
||
669 | // https://www.w3schools.com/sql/sql_foreignkey.asp |
||
670 | // ALTER TABLE Orders ADD CONSTRAINT FK_PersonOrder FOREIGN KEY (PersonID) REFERENCES Persons(PersonID); |
||
671 | if (isset($indexData['FOREIGN'])) { |
||
672 | $command = 'FOREIGN KEY '; |
||
673 | $foreignField = $indexData['FOREIGN']; |
||
674 | if (isset($indexData['REFERENCES'])) { |
||
675 | $references = $indexData['REFERENCES']; |
||
676 | if (!is_array($references)) { |
||
677 | die('Esperaba un array en REFERENCES: ' . $tableName . '/' . $indexname); |
||
678 | } |
||
679 | if (count($references) != 1) { |
||
680 | die('Esperaba un array de 1 elemento en REFERENCES: ' . $tableName . '/' . $indexname); |
||
681 | } |
||
682 | $refTable = key($references); |
||
683 | $fields = '(' . implode(',', $references) . ')'; |
||
684 | } else { |
||
685 | die('FOREIGN necesita REFERENCES en ' . $tableName . '/' . $indexname); |
||
686 | } |
||
687 | |||
688 | $sql .= $command . ' ' . $foreignField . ' REFERENCES ' . $refTable . $fields; |
||
689 | |||
690 | if (isset($indexData['ON']) && is_array($indexData['ON'])) { |
||
691 | foreach ($indexData['ON'] as $key => $value) { |
||
692 | $sql .= ' ON ' . $key . ' ' . $value . ', '; |
||
693 | } |
||
694 | $sql = substr($sql, 0, -2); // Quitamos el ', ' de detrás |
||
695 | } |
||
696 | } |
||
697 | } else { |
||
698 | if (is_array($fields)) { |
||
699 | $fields = '(' . implode(',', $fields) . ')'; |
||
700 | } else { |
||
701 | $fields = "($fields)"; |
||
702 | } |
||
703 | |||
704 | if ($command == 'INDEX ') { |
||
705 | $sql = "CREATE INDEX {$indexname} ON {$tableName}" . $fields; |
||
706 | } else { |
||
707 | $sql .= $command . ' ' . $fields; |
||
708 | } |
||
709 | } |
||
710 | |||
711 | return $sql . ';' . self::CRLF; |
||
712 | } |
||
713 | |||
714 | /** |
||
715 | * Create the SQL statements to fill the table with default data. |
||
716 | * |
||
717 | * @param string $tableName |
||
718 | * @param array $values |
||
719 | * |
||
720 | * @return string |
||
721 | */ |
||
722 | protected static function setValues(string $tableName, array $values): string |
||
745 | } |
||
746 | } |
||
747 |