" 26 | cat "$SP_QUERY_RESULT_FILE" 27 | echo "" 28 | echo "" 29 | exit "$SP_BUNDLE_EXIT_SHOW_AS_HTML" 30 | fi 31 | } 32 | 33 | # set up HTML styles 34 | echo " 35 | 45 | " 46 | 47 | # start clean 48 | clear_temp 49 | 50 | # Check if one table is selected 51 | if [ -z "$SP_SELECTED_TABLES" ]; then 52 | echo "
No table selected.
" 53 | echo "" 54 | exit "$SP_BUNDLE_EXIT_SHOW_AS_HTML" 55 | fi 56 | 57 | # build dest dir 58 | DESTDIR=~/Desktop/SequelProLaravelExport 59 | if mkdir -p $DESTDIR; then 60 | echo "Output directory: $DESTDIR
"; 61 | else 62 | echo "Could not create directory: $DESTDIR
" 63 | echo "" 64 | exit "$SP_BUNDLE_EXIT_SHOW_AS_HTML" 65 | fi 66 | 67 | CONSTRAINTS_TABLE="no" 68 | 69 | echo " 70 | SELECT * 71 | FROM information_schema.tables 72 | WHERE table_schema = 'information_schema' 73 | AND table_name = 'REFERENTIAL_CONSTRAINTS' 74 | LIMIT 1;" > "$SP_QUERY_FILE" 75 | execute_sql 76 | 77 | if [ `cat "$SP_QUERY_RESULT_STATUS_FILE"` -gt 0 ] && [[ $(wc -l < "$SP_QUERY_RESULT_FILE") -ge 2 ]]; then 78 | CONSTRAINTS_TABLE="yes" 79 | fi 80 | clear_temp 81 | 82 | # Split by tab 83 | IFS=$'\t' 84 | read -r -a tables <<< "$SP_SELECTED_TABLES" 85 | 86 | # Loop through tables 87 | for table in "${tables[@]}" 88 | do 89 | 90 | # get the table structure, including field comments 91 | echo " 92 | SELECT 93 | COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, CHARACTER_SET_NAME, COLLATION_NAME, EXTRA, COLUMN_COMMENT 94 | FROM 95 | information_schema.COLUMNS 96 | WHERE 97 | TABLE_SCHEMA = '${SP_SELECTED_DATABASE}' 98 | AND 99 | TABLE_NAME = '${table}' 100 | ORDER BY ORDINAL_POSITION;" > "$SP_QUERY_FILE" 101 | 102 | # execute and save the table structure result 103 | execute_sql 104 | cp "$SP_QUERY_RESULT_FILE" "$SP_BUNDLE_PATH/rowsStructure.tsv" 105 | 106 | clear_temp 107 | 108 | # build SHOW INDEXES query 109 | echo "SHOW INDEXES FROM \`${table}\`" > "$SP_QUERY_FILE" 110 | 111 | # execute and save the SHOW INDEXES result 112 | execute_sql 113 | cp "$SP_QUERY_RESULT_FILE" "$SP_BUNDLE_PATH/rowsKeys.tsv" 114 | 115 | clear_temp 116 | 117 | # get character set and collation name 118 | echo " 119 | SELECT 120 | CHARACTER_SET_NAME, TABLE_COLLATION 121 | FROM 122 | information_schema.TABLES, information_schema.COLLATION_CHARACTER_SET_APPLICABILITY 123 | WHERE 124 | COLLATION_NAME = TABLE_COLLATION 125 | AND 126 | TABLE_SCHEMA = '${SP_SELECTED_DATABASE}' 127 | AND 128 | TABLE_NAME = '${table}';" > "$SP_QUERY_FILE" 129 | 130 | # execute and save the character set and collation name result 131 | execute_sql 132 | cp "$SP_QUERY_RESULT_FILE" "$SP_BUNDLE_PATH/rowsTableCharsetAndCollation.tsv" 133 | 134 | clear_temp 135 | 136 | if [ "$CONSTRAINTS_TABLE" == "yes" ]; then 137 | 138 | # check if CONSTRAINTS exists 139 | echo " 140 | SELECT count(kcu.CONSTRAINT_NAME) 141 | FROM 142 | information_schema.REFERENTIAL_CONSTRAINTS rc 143 | LEFT JOIN 144 | information_schema.KEY_COLUMN_USAGE kcu 145 | ON 146 | (rc.CONSTRAINT_NAME=kcu.CONSTRAINT_NAME) 147 | WHERE 148 | kcu.CONSTRAINT_SCHEMA = '${SP_SELECTED_DATABASE}' 149 | AND 150 | rc.TABLE_NAME = '${table}';" > "$SP_QUERY_FILE" 151 | 152 | # check for errors 153 | execute_sql 154 | 155 | fi 156 | 157 | if [ "$CONSTRAINTS_TABLE" == "yes" ] && [ `cat "$SP_QUERY_RESULT_STATUS_FILE"` -gt 0 ]; then 158 | clear_temp 159 | 160 | # build CONSTRAINTS query 161 | echo " 162 | SELECT 163 | kcu.CONSTRAINT_NAME, kcu.COLUMN_NAME, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME, rc.UPDATE_RULE, rc.DELETE_RULE 164 | FROM 165 | information_schema.REFERENTIAL_CONSTRAINTS rc 166 | LEFT JOIN 167 | information_schema.KEY_COLUMN_USAGE kcu 168 | ON 169 | (rc.CONSTRAINT_NAME=kcu.CONSTRAINT_NAME) 170 | WHERE 171 | kcu.CONSTRAINT_SCHEMA = '${SP_SELECTED_DATABASE}' 172 | AND 173 | rc.TABLE_NAME = '${table}';" > "$SP_QUERY_FILE" 174 | 175 | # save the CONSTRAINTS result 176 | execute_sql 177 | cp $SP_QUERY_RESULT_FILE "$SP_BUNDLE_PATH/rowsConstraints.tsv" 178 | else 179 | echo -n "" > "$SP_BUNDLE_PATH/rowsConstraints.tsv" 180 | fi 181 | 182 | 183 | # process the results and save to the desktop 184 | FILENAME=$(date "+%Y_%m_%d_%H%M%S_create_${table}_table.php") 185 | /usr/bin/php "$SP_BUNDLE_PATH/parse.php" "${table}" > $DESTDIR/$FILENAME 186 | echo "Migration saved: $FILENAME
" 187 | # clean up 188 | clear_temp 189 | 190 | # end loop through tables 191 | done 192 | 193 | # ensure timestamps for any foreign key migrations occur after creation migrations 194 | sleep 1 195 | 196 | # Loop through tables 197 | for table in "${tables[@]}" 198 | do 199 | # get the table foreign key 200 | echo "SELECT 201 | TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME 202 | FROM 203 | INFORMATION_SCHEMA.KEY_COLUMN_USAGE 204 | WHERE 205 | REFERENCED_TABLE_SCHEMA = '${SP_SELECTED_DATABASE}' AND TABLE_NAME = '${table}';" > "$SP_QUERY_FILE" 206 | 207 | # execute and save the table structure result 208 | execute_sql 209 | cp $SP_QUERY_RESULT_FILE "$SP_BUNDLE_PATH/rowsForeignStructure.tsv" 210 | 211 | hasForeignKey=`cat "$SP_BUNDLE_PATH/rowsForeignStructure.tsv" | grep -v "TABLE_NAME" | wc -l | awk '{print $1}'`; 212 | if [ $hasForeignKey != 0 ]; then 213 | FILENAME=$(date "+%Y_%m_%d_%H%M%S_add_foreign_key_to_${table}_table.php") 214 | /usr/bin/php "$SP_BUNDLE_PATH/parse.php" "${table}" "foreignkey" > $DESTDIR/$FILENAME 215 | echo "Migration for foreign key saved: $FILENAME
" 216 | # clean up 217 | clear_temp 218 | fi 219 | clear_temp 220 | # end loop through tables 221 | done 222 | 223 | echo "" 224 | exit $SP_BUNDLE_EXIT_SHOW_AS_HTML 225 | -------------------------------------------------------------------------------- /src/MigrationParser.php: -------------------------------------------------------------------------------- 1 | 'integer', 48 | 'bigint' => 'bigInteger', 49 | 'mediumint' => 'mediumInteger', 50 | 'smallint' => 'smallInteger', 51 | 'tinyint' => 'tinyInteger', 52 | ]; 53 | 54 | /** 55 | * @var string 56 | */ 57 | protected $tableName; 58 | 59 | /** 60 | * @var string 61 | */ 62 | protected $structureFile; 63 | 64 | /** 65 | * @var string 66 | */ 67 | protected $keysFile; 68 | 69 | /** 70 | * @var string 71 | */ 72 | protected $constraintsFile; 73 | 74 | /** 75 | * @var string 76 | */ 77 | protected $tableCharsetAndCollationFile; 78 | 79 | /** 80 | * @var string 81 | */ 82 | protected $foreignStructureFile; 83 | 84 | /** 85 | * @var string 86 | */ 87 | protected $hasForeign; 88 | 89 | /** 90 | * MigrationParser constructor. 91 | * 92 | * @param string $tableName 93 | * @param string $structureFile 94 | * @param string $keysFile 95 | * @param string $constraintsFile 96 | * @param string $tableCharsetAndCollationFile 97 | * @param string $foreignStructureFile 98 | * @param string $hasForeign 99 | */ 100 | public function __construct($tableName, $structureFile, $keysFile, $constraintsFile, $tableCharsetAndCollationFile, $foreignStructureFile, $hasForeign) 101 | { 102 | $this->tableName = $tableName; 103 | $this->structureFile = $structureFile; 104 | $this->keysFile = $keysFile; 105 | $this->constraintsFile = $constraintsFile; 106 | $this->tableCharsetAndCollationFile = $tableCharsetAndCollationFile; 107 | $this->foreignStructureFile = $foreignStructureFile; 108 | $this->hasForeign = $hasForeign; 109 | } 110 | 111 | public function makeMigration() 112 | { 113 | $this->buildTableCollationAndCharset(); 114 | $this->buildStructure(); 115 | $this->buildKeys(); 116 | $this->buildConstraints(); 117 | 118 | $indent8 = str_repeat(' ', 8); 119 | $indent12 = str_repeat(' ', 12); 120 | $eol = "\n"; 121 | 122 | $structure = trim(implode($eol . $indent12, $this->formatStructure())) . $eol; 123 | $keys = trim(implode($eol . $indent12, $this->formatKeys())) . $eol; 124 | $constraints = trim(implode($eol . $indent12, $this->formatConstraints())) . $eol; 125 | $tableCollationAndCharset = trim(implode($eol . $indent12, $this->formatTableCollationAndCharset())) . $eol; 126 | $extras = trim(implode($eol . $indent8, $this->formatExtras())) . $eol; 127 | 128 | if ($this->hasForeign === "true") { 129 | $foreign = trim(implode($eol . $indent12, $this->formatForeign())) . $eol; 130 | $foreignDrop = trim(implode($eol . $indent12, $this->formatForeignDrop())) . $eol; 131 | $className = 'AddForeignKeyTo' . $this->studly($this->tableName) . 'Table'; 132 | $output = file_get_contents(__DIR__ . '/foreign_key.stub'); 133 | $output = str_replace( 134 | [ 135 | ':VERSION:', 136 | 'DummyClass', 137 | 'DummyTable', 138 | "// foreign\n", 139 | "// foreignDrop\n", 140 | ], 141 | [ 142 | $this->version, 143 | $className, 144 | $this->tableName, 145 | $foreign, 146 | $foreignDrop, 147 | ], 148 | $output 149 | ); 150 | } else { 151 | $className = 'Create' . $this->studly($this->tableName) . 'Table'; 152 | $output = file_get_contents(__DIR__ . '/create.stub'); 153 | $output = str_replace( 154 | [ 155 | ':VERSION:', 156 | 'DummyClass', 157 | 'DummyTable', 158 | "// structure\n", 159 | "// keys\n", 160 | "// constraints\n", 161 | "// tableCollationAndCharset\n", 162 | "// extras\n", 163 | ], 164 | [ 165 | $this->version, 166 | $className, 167 | $this->tableName, 168 | $structure, 169 | $keys, 170 | $constraints, 171 | $tableCollationAndCharset, 172 | $extras, 173 | ], 174 | $output 175 | ); 176 | } 177 | 178 | 179 | $output = preg_replace("/^(\s*\R){2,}/m", "\n", $output); 180 | 181 | return $output; 182 | } 183 | 184 | public function formatForeign() 185 | { 186 | $fields = []; 187 | 188 | $rows = file($this->foreignStructureFile); 189 | array_shift($rows); 190 | 191 | foreach ($rows as $row) { 192 | list($table, $colName, $constName, $refTable, $refColumnName) = explode("\t", 193 | $row, 5); 194 | $fields[] = '$table->foreign(\''. trim($colName) .'\')->references(\''. trim($refColumnName) .'\')->on(\''. trim($refTable) .'\');'; 195 | } 196 | return array_filter($fields); 197 | } 198 | 199 | public function formatForeignDrop() 200 | { 201 | $fields = []; 202 | 203 | $rows = file($this->foreignStructureFile); 204 | array_shift($rows); 205 | 206 | foreach ($rows as $row) { 207 | list($table, $colName, $constName, $refTable, $refColumnName) = explode("\t", 208 | $row, 5); 209 | $fields[] = '$table->dropForeign(\''. trim($table) .'_'. trim($colName) .'_foreign\');'; 210 | } 211 | return array_filter($fields); 212 | } 213 | 214 | protected function studly($value) 215 | { 216 | $value = ucwords(str_replace(['-', '_'], ' ', $value)); 217 | 218 | return str_replace(' ', '', $value); 219 | } 220 | 221 | public function buildStructure() 222 | { 223 | $this->structure = []; 224 | 225 | $rows = file($this->structureFile); 226 | array_shift($rows); 227 | 228 | foreach ($rows as $row) { 229 | 230 | list($field, $colType, $null, $key, $default, $characterSet, $collation, $extra, $comment) = explode("\t", 231 | $row); 232 | 233 | if (preg_match('#^(\w+)(\((.*?)\))?(.*?)?$#', $colType, $matches)) { 234 | 235 | $type = strtolower($matches[1]); 236 | $args = $matches[3] ?: null; 237 | $typeExtra = trim($matches[4]) ?: null; 238 | 239 | if (strpos($args, ',') === false) { 240 | $args = $args ?: null; 241 | } else { 242 | $args = explode(',', $args); 243 | } 244 | 245 | $data = [ 246 | 'field' => $field, 247 | 'nullable' => ($null === 'YES'), 248 | 'default' => ($default !== 'NULL') ? $default : null, 249 | 'characterSet' => ($characterSet !== 'NULL' && $characterSet !== $this->tableCharset) ? $characterSet : null, 250 | 'collation' => ($collation !== 'NULL' && $collation !== $this->tableCollation) ? $collation : null, 251 | '_colType' => $colType, 252 | ]; 253 | 254 | $method = 'parse' . ucfirst($type); 255 | 256 | if (method_exists($this, $method)) { 257 | $data = array_merge( 258 | $data, 259 | $this->{$method}($type, $args, $typeExtra, $extra) 260 | ); 261 | } else { 262 | $data['method'] = 'UNKNOWN:' . $type; 263 | } 264 | 265 | $data['comment'] = trim(str_replace(["\r", "\n"], '', $comment)); 266 | if ($data['comment']==='') { 267 | $data['comment'] = null; 268 | } 269 | 270 | $this->structure[$field] = $data; 271 | } 272 | } 273 | 274 | // look for softDeletes 275 | if ( 276 | array_key_exists('deleted_at', $this->structure) 277 | && $this->structure['deleted_at']['method'] === 'timestamp' 278 | ) { 279 | $this->structure['deleted_at']['method'] = 'softDeletes'; 280 | $this->structure['deleted_at']['args'] = null; 281 | $this->structure['deleted_at']['default'] = null; 282 | $this->structure['deleted_at']['nullable'] = false; 283 | $this->structure['deleted_at']['field'] = null; 284 | } 285 | 286 | // look for timestamps 287 | if ( 288 | array_key_exists('created_at', $this->structure) 289 | && $this->structure['created_at']['method'] === 'timestamp' 290 | && array_key_exists('updated_at', $this->structure) 291 | && $this->structure['updated_at']['method'] === 'timestamp' 292 | ) { 293 | unset($this->structure['updated_at']); 294 | $method = $this->structure['created_at']['nullable'] ? 'nullableTimestamps' : 'timestamps'; 295 | $this->structure['created_at']['method'] = $method; 296 | $this->structure['created_at']['args'] = null; 297 | $this->structure['created_at']['default'] = null; 298 | $this->structure['created_at']['nullable'] = false; 299 | $this->structure['created_at']['field'] = null; 300 | } 301 | 302 | // look for rememberToken 303 | if ( 304 | array_key_exists('remember_token', $this->structure) 305 | && $this->structure['remember_token']['method'] === 'string' 306 | && $this->structure['remember_token']['nullable'] === true 307 | && $this->structure['remember_token']['args'] === '100' 308 | ) { 309 | $this->structure['remember_token']['method'] = 'rememberToken'; 310 | $this->structure['remember_token']['args'] = null; 311 | $this->structure['remember_token']['default'] = null; 312 | $this->structure['remember_token']['nullable'] = false; 313 | $this->structure['remember_token']['field'] = null; 314 | } 315 | 316 | // look for id 317 | foreach ($this->structure as $field=>$struct) { 318 | if ( 319 | $struct['method'] === 'bigInteger' 320 | && $struct['autoIncrement'] === true 321 | && $struct['args'] === null 322 | ) { 323 | $this->structure[$field]['method'] = 'id'; 324 | $this->structure[$field]['args'] = null; 325 | $this->structure[$field]['default'] = null; 326 | $this->structure[$field]['nullable'] = false; 327 | $this->structure[$field]['autoIncrement'] = null; 328 | $this->structure[$field]['unsigned'] = null; 329 | if ($field==='id') { 330 | $this->structure[$field]['field'] = null; 331 | } 332 | } 333 | } 334 | } 335 | 336 | public function formatStructure() 337 | { 338 | $fields = []; 339 | foreach ($this->structure as $field => $data) { 340 | 341 | $method = $data['method']; 342 | $isNumeric = $this->isNumeric($method); 343 | $isInteger = $this->isInteger($method); 344 | 345 | if ($isInteger) { 346 | if ($data['autoIncrement']) { 347 | $method = str_replace('nteger', 'ncrements', $method); 348 | } elseif ($data['unsigned']) { 349 | $method = 'unsigned' . ucfirst($method); 350 | } 351 | } 352 | 353 | if ($method === 'timestamp' && $data['args'] === self::TS_UPDATE_STRING) { 354 | $data['default'] .= ' ' . $data['args']; 355 | $data['args'] = null; 356 | } 357 | 358 | $temp = '$table->' . $method; 359 | if ($data['field']) { 360 | $temp .= '(\'' . $field . '\''; 361 | if ($method === 'enum' || $method === 'set') { 362 | $temp .= ', [' . implode(', ', (array) $data['args']) . '])'; 363 | } elseif ($data['args']) { 364 | $temp .= ', ' . implode(', ', (array) $data['args']) . ')'; 365 | } else { 366 | $temp .= ')'; 367 | } 368 | } else { 369 | $temp .= '()'; 370 | } 371 | if (!$isInteger) { 372 | if ($data['autoIncrement']) { 373 | $temp .= '->autoIncrement()'; 374 | } 375 | if ($data['unsigned']) { 376 | $temp .= '->unsigned()'; 377 | } 378 | } 379 | if ($data['nullable']) { 380 | $temp .= '->nullable()'; 381 | } 382 | if ($data['characterSet']) { 383 | $temp .= "->charset('" . $data['characterSet'] . "')"; 384 | } 385 | if ($data['collation']) { 386 | $temp .= "->collation('" . $data['collation'] . "')"; 387 | } 388 | if (isset($data['default'])) { 389 | if ($isNumeric || (($method === 'enum' || $method === 'set') && is_numeric($data['default']))) { 390 | $temp .= '->default(' . $data['default'] . ')'; 391 | } elseif ($method === 'boolean') { 392 | $temp .= '->default(' . ($data['default'] ? 'true' : 'false') . ')'; 393 | } elseif (stripos(trim($data['default']), 'CURRENT_TIMESTAMP') !== false) { 394 | $temp .= '->default(\DB::raw(\'' . trim($data['default']) . '\'))'; 395 | } else { 396 | $temp .= '->default(\'' . $this->trimStringQuotes($data['default']) . '\')'; 397 | } 398 | } 399 | 400 | // If isn't empty, set the comment 401 | if ($data['comment'] !== null) { 402 | $temp .= '->comment(\'' . addslashes($data['comment']) . '\')'; 403 | } 404 | 405 | $fields[$field] = $temp . ';'; 406 | } 407 | 408 | return array_filter($fields); 409 | } 410 | 411 | public function buildKeys() 412 | { 413 | $this->keys = []; 414 | 415 | $rows = file($this->keysFile); 416 | array_shift($rows); 417 | 418 | foreach ($rows as $row) { 419 | list($table, $nonUnique, $keyName, $seq, $colName, $collation, $cardinality, $subPart, $packed, $null, $indexType, $extra) = explode("\t", 420 | $row, 12); 421 | 422 | if ($indexType === 'FULLTEXT') { 423 | if (!array_key_exists($keyName, $this->extras)) { 424 | $this->extras[$keyName] = [ 425 | 'method' => 'fulltext', 426 | 'table' => $table, 427 | 'columns' => [], 428 | ]; 429 | $this->extras[$keyName]['columns'][$seq] = $colName; 430 | } 431 | } else { 432 | if (!array_key_exists($keyName, $this->keys)) { 433 | $this->keys[$keyName] = [ 434 | 'method' => $nonUnique ? 'index' : 'unique', 435 | 'table' => $table, 436 | 'columns' => [], 437 | ]; 438 | } 439 | $this->keys[$keyName]['columns'][$seq] = $colName; 440 | } 441 | } 442 | 443 | // if we have a primary key ... 444 | if (array_key_exists('PRIMARY', $this->keys)) { 445 | $primary = $this->keys['PRIMARY']; 446 | // and it's for one columns ... 447 | if (count($primary['columns']) === 1) { 448 | $primaryColumn = reset($primary['columns']); 449 | $field = $this->structure[$primaryColumn]; 450 | // and that column is an "increments" field ... 451 | if (isset($field['args']['autoIncrement']) && $field['args']['autoIncrement'] === 'true') { 452 | // then don't build the primary key, since Laravel takes care of it 453 | unset($this->keys['PRIMARY']); 454 | } 455 | } 456 | } 457 | } 458 | 459 | public function formatKeys() 460 | { 461 | $fields = []; 462 | 463 | foreach ($this->keys as $field => $data) { 464 | $columns = $this->escapeArray($data['columns']); 465 | 466 | if ($field === 'PRIMARY') { 467 | //$fields[$field] = sprintf('$table->primary(%s);', $columns); 468 | } else { 469 | $fields[$field] = sprintf('$table->%s(%s, \'%s\');', 470 | $data['method'], 471 | $columns, 472 | $field 473 | ); 474 | } 475 | } 476 | 477 | return array_filter($fields); 478 | } 479 | 480 | public function formatExtras() 481 | { 482 | $fields = []; 483 | 484 | foreach ($this->extras as $field => $data) { 485 | if ($data['method'] === 'fulltext') { 486 | $columns = $this->escapeColumnList($data['columns']); 487 | $fields[$field] = sprintf('\\DB::statement("ALTER TABLE `%s` ADD FULLTEXT INDEX `%s` (%s)");', 488 | $data['table'], 489 | $field, 490 | $columns 491 | ); 492 | } 493 | } 494 | 495 | return array_filter($fields); 496 | } 497 | 498 | public function buildConstraints() 499 | { 500 | $this->constraints = []; 501 | 502 | $rows = file($this->constraintsFile); 503 | array_shift($rows); 504 | 505 | foreach ($rows as $row) { 506 | $row = preg_replace('/\n+$/', '', $row); 507 | list($constraint, $colName, $refTable, $refColumn, $updateRule, $deleteRule) = explode("\t", $row); 508 | 509 | if (array_key_exists($constraint, $this->keys)) { 510 | unset($this->keys[$constraint]); 511 | } 512 | 513 | $this->constraints[$constraint][] = compact('colName', 'refTable', 'refColumn', 'updateRule', 'deleteRule'); 514 | } 515 | } 516 | 517 | public function formatConstraints() 518 | { 519 | $fields = []; 520 | foreach ($this->constraints as $field => $data) { 521 | $colNames = $this->escapeArray(array_map(function($entry) { return $entry['colName']; }, $data)); 522 | $refColumns = $this->escapeArray(array_map(function($entry) { return $entry['refColumn']; }, $data)); 523 | $temp = '$table->foreign(' . $colNames . ', \'' . $field . '\')' . 524 | '->references(' . $refColumns . ')' . 525 | '->on(\'' . $data[0]['refTable'] . '\')' . 526 | '->onDelete(\'' . $data[0]['deleteRule'] . '\')' . 527 | '->onUpdate(\'' . $data[0]['updateRule'] . '\')'; 528 | 529 | $fields[$field] = $temp . ';'; 530 | } 531 | 532 | return array_filter($fields); 533 | } 534 | 535 | public function buildTableCollationAndCharset() 536 | { 537 | $this->constraints = []; 538 | 539 | $rows = file($this->tableCharsetAndCollationFile); 540 | array_shift($rows); 541 | 542 | if (!empty($rows)) { 543 | $row = array_shift($rows); 544 | $row = preg_replace('/\n+$/', '', $row); 545 | list($this->tableCharset, $this->tableCollation) = explode("\t", $row); 546 | } 547 | } 548 | 549 | public function formatTableCollationAndCharset() 550 | { 551 | $output = []; 552 | 553 | if ($this->tableCharset) { 554 | $output[] = '$table->charset = \'' . $this->tableCharset . "';"; 555 | } 556 | 557 | if ($this->tableCollation) { 558 | $output[] = '$table->collation = \'' . $this->tableCollation . "';"; 559 | } 560 | 561 | return $output; 562 | } 563 | 564 | protected function copyToClipboard($content) 565 | { 566 | $cmd = 'echo ' . escapeshellarg($content) . ' | __CF_USER_TEXT_ENCODING=' . posix_getuid() . ':0x8000100:0x8000100 pbcopy'; 567 | shell_exec($cmd); 568 | } 569 | 570 | protected function escapeArray($array) 571 | { 572 | $array = (array) $array; 573 | array_walk($array, function(&$value, $idx) { 574 | if (!is_numeric($value)) { 575 | $value = '\'' . str_replace('\'', '\\\'', $value) . '\''; 576 | } 577 | }); 578 | 579 | $string = implode(', ', $array); 580 | 581 | if (count($array) > 1) { 582 | return '[' . $string . ']'; 583 | } 584 | 585 | return $string; 586 | } 587 | 588 | protected function escapeColumnList($array) 589 | { 590 | $array = (array) $array; 591 | array_walk($array, function(&$value, $idx) { 592 | $value = '`' . $value . '`'; 593 | }); 594 | 595 | return implode(', ', $array); 596 | } 597 | 598 | // protected function getAutoIncrementArgument($extra) 599 | // { 600 | // $arguments = [ 601 | // 'autoIncrement' => false, 602 | // ]; 603 | // 604 | // if (strpos($extra, 'auto_increment') !== false) { 605 | // $arguments['autoIncrement'] = true; 606 | // } 607 | // 608 | // return $arguments; 609 | // } 610 | // 611 | 612 | protected function isAutoIncrement($extra) 613 | { 614 | return strpos($extra, 'auto_increment') !== false; 615 | } 616 | 617 | // protected function getUnsignedArgument($typeExtra) 618 | // { 619 | // $arguments = [ 620 | // 'unsigned' => false 621 | // ]; 622 | // 623 | // if (strpos($typeExtra, 'unsigned') !== false) { 624 | // $arguments['unsigned'] = true; 625 | // } 626 | // 627 | // return $arguments; 628 | // } 629 | 630 | protected function isUnsigned($typeExtra) 631 | { 632 | return strpos($typeExtra, 'unsigned') !== false; 633 | } 634 | 635 | protected function parseInt($type, $args, $typeExtra, $extra) 636 | { 637 | return [ 638 | 'method' => $this->integerMaps[$type], 639 | 'args' => null, 640 | 'autoIncrement' => $this->isAutoIncrement($extra), 641 | 'unsigned' => $this->isUnsigned($typeExtra), 642 | ]; 643 | } 644 | 645 | protected function parseBigint($type, $args, $typeExtra, $extra) 646 | { 647 | return $this->parseInt($type, $args, $typeExtra, $extra); 648 | } 649 | 650 | protected function parseMediumint($type, $args, $typeExtra, $extra) 651 | { 652 | return $this->parseInt($type, $args, $typeExtra, $extra); 653 | } 654 | 655 | protected function parseSmallint($type, $args, $typeExtra, $extra) 656 | { 657 | return $this->parseInt($type, $args, $typeExtra, $extra); 658 | } 659 | 660 | protected function parseTinyint($type, $args, $typeExtra, $extra) 661 | { 662 | if ($args === 1) { 663 | $method = 'boolean'; 664 | $args = null; 665 | $unsigned = false; 666 | 667 | return compact('method', 'args', 'unsigned'); 668 | } 669 | 670 | return $this->parseInt($type, $args, $typeExtra, $extra); 671 | } 672 | 673 | protected function parseBlob($type, $args, $typeExtra, $extra) 674 | { 675 | return $this->defaultParse('binary', $args); 676 | } 677 | 678 | protected function parseChar($type, $args, $typeExtra, $extra) 679 | { 680 | return $this->defaultParse('char', $args); 681 | } 682 | 683 | protected function parseDate($type, $args, $typeExtra, $extra) 684 | { 685 | return $this->defaultParse('date'); 686 | } 687 | 688 | protected function parseDatetime($type, $args, $typeExtra, $extra) 689 | { 690 | return $this->defaultParse('dateTime'); 691 | } 692 | 693 | protected function parseDecimal($type, $args, $typeExtra, $extra) 694 | { 695 | return [ 696 | 'method' => 'decimal', 697 | 'args' => $args, 698 | 'unsigned' => $this->isUnsigned($typeExtra), 699 | ]; 700 | } 701 | 702 | protected function parseNumeric($type, $args, $typeExtra, $extra) 703 | { 704 | return $this->parseDecimal($type, $args, $typeExtra, $extra); 705 | } 706 | 707 | protected function parseFixed($type, $args, $typeExtra, $extra) 708 | { 709 | return $this->parseDecimal($type, $args, $typeExtra, $extra); 710 | } 711 | 712 | protected function parseDouble($type, $args, $typeExtra, $extra) 713 | { 714 | return [ 715 | 'method' => 'double', 716 | 'args' => $args, 717 | 'unsigned' => $this->isUnsigned($typeExtra), 718 | ]; 719 | } 720 | 721 | protected function parseDoublePrecision($type, $args, $typeExtra, $extra) 722 | { 723 | return $this->parseDouble($type, $args, $typeExtra, $extra); 724 | } 725 | 726 | protected function parseReal($type, $args, $typeExtra, $extra) 727 | { 728 | return $this->parseDouble($type, $args, $typeExtra, $extra); 729 | } 730 | 731 | protected function parseFloat($type, $args, $typeExtra, $extra) 732 | { 733 | return [ 734 | 'method' => 'float', 735 | 'args' => $args, 736 | 'unsigned' => $this->isUnsigned($typeExtra), 737 | ]; 738 | } 739 | 740 | protected function parseLongtext($type, $args, $typeExtra, $extra) 741 | { 742 | return $this->defaultParse('longText', $args); 743 | } 744 | 745 | protected function parseMediumtext($type, $args, $typeExtra, $extra) 746 | { 747 | return $this->defaultParse('mediumText', $args); 748 | } 749 | 750 | protected function parseTinytext($type, $args, $typeExtra, $extra) 751 | { 752 | return $this->defaultParse('tinyText', $args); 753 | } 754 | 755 | protected function parseText($type, $args, $typeExtra, $extra) 756 | { 757 | return $this->defaultParse('text', $args); 758 | } 759 | 760 | protected function parseVarchar($type, $args, $typeExtra, $extra) 761 | { 762 | return $this->defaultParse('string', $args); 763 | } 764 | 765 | protected function parseEnum($type, $args, $typeExtra, $extra) 766 | { 767 | return $this->defaultParse('enum', $args); 768 | } 769 | 770 | protected function parseSet($type, $args, $typeExtra, $extra) 771 | { 772 | return $this->defaultParse('set', $args); 773 | } 774 | 775 | protected function parseTime($type, $args, $typeExtra, $extra) 776 | { 777 | return $this->defaultParse('time', $args); 778 | } 779 | 780 | protected function parseTimestamp($type, $args, $typeExtra, $extra) 781 | { 782 | if (stripos($extra, self::TS_UPDATE_STRING) !== false) { 783 | $args = self::TS_UPDATE_STRING; 784 | } 785 | return $this->defaultParse('timestamp', $args); 786 | } 787 | 788 | protected function parseJson($type, $args, $typeExtra, $extra) 789 | { 790 | return $this->defaultParse('json', $args); 791 | } 792 | 793 | private function defaultParse($method, $args = null) 794 | { 795 | return compact('method', 'args'); 796 | } 797 | 798 | private function trimStringQuotes($string) 799 | { 800 | return trim( 801 | trim($string, '"\'') 802 | ); 803 | } 804 | 805 | private function isInteger($method) 806 | { 807 | return stripos($method, 'integer') !== false; 808 | } 809 | 810 | private function isNumeric($method) 811 | { 812 | return $this->isInteger($method) 813 | || $method === 'decimal' 814 | || $method === 'double' 815 | || $method === 'float' 816 | || $method === 'real'; 817 | } 818 | } 819 | --------------------------------------------------------------------------------