/** * Get the MySQL-specific representation of a value for a column * @param Column $column * @param mixed $value * @return mixed */ public function getColumnDenormalizedValue(Column $column, $value) { if ($value === null) { return null; } else { switch ($column->getType()) { case 'date': return date('Ymd', $value); break; case 'datetime': return date('Y-m-d\\TH:i:s', $value); break; default: return $value; break; } } }
/** * Get a Table object for the given name * TODO table/column properties should not be public * @param Database $database * @param string $name * @throws \Exception * @return Table */ public function buildTable(Database $database, $name) { $createTableInfo = $this->fetchAll("SHOW CREATE TABLE `{$database->getPrefix()}{$name}`"); if (!isset($createTableInfo[0]['Create Table'])) { throw new \Exception('Could not find table ' . $name); } $createTableSql = $createTableInfo[0]['Create Table']; $columns = []; $primaryKeys = []; $constraints = []; $lowerToRealCaseTableNames = []; $actualTableNames = []; foreach ($this->getTables($database) as $tableName) { $actualTableNames[$tableName] = true; $lowerToRealCaseTableNames[strtolower($tableName)] = $tableName; } // parse columns if (preg_match_all(self::CREATE_TABLE_SQL_COLUMNS_REGEX, $createTableSql, $columns, PREG_SET_ORDER)) { $tableClass = $database->getClassMapper()->getClassForTable($name); /** @var Table $table */ $table = new $tableClass($name, $database); foreach ($columns as $ordinal => $columnInfo) { $column = new Column($table); $default = null; if (isset($columnInfo['default']) && $columnInfo['default'] !== 'NULL') { if ($columnInfo['default'] === '') { $default = ''; } else { if ($columnInfo['default'] !== 'CURRENT_TIMESTAMP' && $columnInfo['default'] !== 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP') { // we need to parse the SQL default value $defaultResult = $this->fetchAll('SELECT ' . $columnInfo['default']); $default = current(current($defaultResult)); } } } $column->setName($columnInfo['name'])->setOrdinal($ordinal)->setType(strtolower($columnInfo['type']))->setDefault($default)->setAllowNull(!isset($columnInfo['allowNull']) || $columnInfo['allowNull'] === 'NULL')->setAutoIncrement(!empty($columnInfo['autoIncrement'])); if ($column->getType() == 'set' || $column->getType() == 'enum') { // we need to parse the SQL options $optionsResult = $this->fetchAll('SELECT ' . $columnInfo['length']); $options = []; $i = 0; foreach (current($optionsResult) as $option) { $options[$column->getType() == 'set' ? pow(2, $i) : $i] = $option; $i++; } $column->setOptions($options); } else { $column->setLength(isset($columnInfo['length']) && $columnInfo['length'] !== '' ? $columnInfo['length'] : null); } $table->columns[$column->getName()] = $column; } // parse primary keys preg_match(self::CREATE_TABLE_SQL_PRIMARY_KEY_REGEX, $createTableSql, $primaryKeys); $primaryKeys = explode('`,`', trim($primaryKeys[1], '`')); foreach ($primaryKeys as $primaryKey) { $table->primaryKeys[] = $primaryKey; $table->columns[$primaryKey]->setPrimaryKey(true); } // parse relationships preg_match_all(self::CREATE_TABLE_SQL_CONSTRAINT_REGEX, $createTableSql, $constraints, PREG_SET_ORDER); $offset = strlen($database->getPrefix()); foreach ($constraints as $constraint) { $localColumns = explode('`, `', trim($constraint['localColumns'], '`')); $relatedColumns = explode('`, `', trim($constraint['relatedColumns'], '`')); foreach ($localColumns as $localColumn) { if (!isset($table->foreignKeys[$localColumn])) { // columns can have multiple foreign keys; we can only use one of them $table->foreignKeys[$localColumn] = $constraint['name']; } } $foreignTableName = substr($constraint['relatedTable'], $offset); // workaround for http://bugs.mysql.com/bug.php?id=6555 // map lower case table names to actual case table names if (!isset($actualTableNames[$foreignTableName]) && isset($lowerToRealCaseTableNames[$foreignTableName])) { $foreignTableName = $lowerToRealCaseTableNames[$foreignTableName]; } if (!isset($table->relatedTables[$foreignTableName])) { // tables can be related to another table multiple times; we can only use one of them $table->relatedTables[$foreignTableName] = $constraint['name']; } $table->constraints[$constraint['name']] = ['localColumns' => $localColumns, 'relatedTable' => $foreignTableName, 'relatedColumns' => $relatedColumns]; } return $table; } else { throw new \Exception('Could not parse table definition'); } }