diff --git a/classes/DatabaseTableModel.php b/classes/DatabaseTableModel.php index 802ee7c6..86faa6b2 100644 --- a/classes/DatabaseTableModel.php +++ b/classes/DatabaseTableModel.php @@ -57,7 +57,7 @@ public static function listPluginTables($pluginCode) $tables = self::getSchemaManager()->listTableNames(); - return array_filter($tables, function($item) use($prefix) { + return array_filter($tables, function ($item) use ($prefix) { return Str::startsWith($item, $prefix); }); } @@ -104,7 +104,7 @@ public function validate() 'name.max' => Lang::get('rainlab.builder::lang.database.error_table_name_too_long') ]; - Validator::extend('tablePrefix', function($attribute, $value, $parameters) use ($prefix) { + Validator::extend('tablePrefix', function ($attribute, $value, $parameters) use ($prefix) { $value = trim($value); if (!Str::startsWith($value, $prefix)) { @@ -114,7 +114,7 @@ public function validate() return true; }); - Validator::extend('uniqueTableName', function($attribute, $value, $parameters) { + Validator::extend('uniqueTableName', function ($attribute, $value, $parameters) { $value = trim($value); $schema = $this->getSchema(); @@ -189,7 +189,8 @@ protected function validateColumnNameLengths() if (Str::length($name) > 64) { throw new ValidationException([ - 'columns' => Lang::get('rainlab.builder::lang.database.error_column_name_too_long', + 'columns' => Lang::get( + 'rainlab.builder::lang.database.error_column_name_too_long', ['column' => $name] ) ]); @@ -199,11 +200,12 @@ protected function validateColumnNameLengths() protected function validateDuplicateColumns() { - foreach ($this->columns as $outerIndex=>$outerColumn) { - foreach ($this->columns as $innerIndex=>$innerColumn) { + foreach ($this->columns as $outerIndex => $outerColumn) { + foreach ($this->columns as $innerIndex => $innerColumn) { if ($innerIndex != $outerIndex && $innerColumn['name'] == $outerColumn['name']) { throw new ValidationException([ - 'columns' => Lang::get('rainlab.builder::lang.database.error_table_duplicate_column', + 'columns' => Lang::get( + 'rainlab.builder::lang.database.error_table_duplicate_column', ['column' => $outerColumn['name']] ) ]); @@ -281,8 +283,7 @@ protected function validateColumnsLengthParameter() foreach ($this->columns as $column) { try { MigrationColumnType::validateLength($column['type'], $column['length']); - } - catch (Exception $ex) { + } catch (Exception $ex) { throw new ValidationException([ 'columns' => $ex->getMessage() ]); @@ -296,6 +297,10 @@ protected function validateDefaultValues() if (!strlen($column['default'])) { continue; } + // Allow null value for all nullable columns + if (strtolower($column['default']) === 'null' && (bool) $column['allow_null'] === true) { + continue; + } $default = trim($column['default']); @@ -326,7 +331,7 @@ protected function validateDefaultValues() } if ($column['type'] == MigrationColumnType::TYPE_BOOLEAN) { - if (!preg_match('/^0|1$/', $default)) { + if (!preg_match('/^0|1|true|false$/i', $default)) { throw new ValidationException([ 'columns' => Lang::get('rainlab.builder::lang.database.error_boolean_default_value', ['column'=>$column['name']]) ]); @@ -383,6 +388,15 @@ protected function loadColumnsFromTableInfo() 'id' => $columnName, ]; + // Format quoted "null" values with quotes + if ($column->getNotnull() === false) { + if ($item['default'] === null) { + $item['default'] = 'null'; + } elseif (strtolower($item['default']) === 'null') { + $item['default'] = "'null'"; + } + } + $this->columns[] = $item; } } diff --git a/classes/DatabaseTableSchemaCreator.php b/classes/DatabaseTableSchemaCreator.php index f2bced1d..055377ce 100644 --- a/classes/DatabaseTableSchemaCreator.php +++ b/classes/DatabaseTableSchemaCreator.php @@ -54,12 +54,22 @@ protected function formatOptions($type, $options) $default = trim($options['default']); - // Note - this code doesn't allow to set empty string as default. + // Note - this code doesn't allow to set empty string as default. // But converting empty strings to NULLs is required for the further // work with Doctrine types. As an option - empty strings could be specified // as '' in the editor UI (table column editor). - $result['default'] = $default === '' ? null : $default; + if ($result['notnull'] === false) { + if (strtolower($default) === 'null') { + $result['default'] = null; + } elseif (preg_match('/^[\'"]null[\'"]$/i', $default)) { + $result['default'] = 'null'; + } else { + $result['default'] = $default === '' ? null : $default; + } + } else { + $result['default'] = $default === '' ? null : $default; + } return $result; } -} \ No newline at end of file +} diff --git a/classes/TableMigrationCodeGenerator.php b/classes/TableMigrationCodeGenerator.php index 7f41a5db..50050d08 100644 --- a/classes/TableMigrationCodeGenerator.php +++ b/classes/TableMigrationCodeGenerator.php @@ -26,8 +26,8 @@ class TableMigrationCodeGenerator extends BaseModel * Generates code for creating or updating a database table. * @param \Doctrine\DBAL\Schema\Table $updatedTable Specifies the updated table schema. * @param \Doctrine\DBAL\Schema\Table $existingTable Specifies the existing table schema, if applicable. - * @param string $newTableName An updated name of the theme. - * @return string|boolean Returns the migration up() and down() methods code. + * @param string $newTableName An updated name of the theme. + * @return string|boolean Returns the migration up() and down() methods code. * Returns false if there the table was not changed. */ public function createOrUpdateTable($updatedTable, $existingTable, $newTableName) @@ -48,8 +48,7 @@ public function createOrUpdateTable($updatedTable, $existingTable, $newTableName $tableDiff->newName = $newTableName; } - } - else { + } else { /* * The table doesn't exist */ @@ -99,7 +98,7 @@ public function wrapMigrationCode($scriptFilename, $code, $pluginCodeObj) /** * Generates code for dropping a database table. * @param \Doctrine\DBAL\Schema\Table $existingTable Specifies the existing table schema. - * @return string Returns the migration up() and down() methods code. + * @return string Returns the migration up() and down() methods code. */ public function dropTable($existingTable) { @@ -113,7 +112,7 @@ protected function generateCreateOrUpdateCode($tableDiff, $isNewTable, $newOrUpd { /* * Although it might seem that a reverse diff could be used - * for the down() method, that's not so. The up and down operations + * for the down() method, that's not so. The up and down operations * are not fully symmetrical. */ @@ -171,16 +170,16 @@ protected function generateCreateOrUpdateUpCode($tableDiff, $isNewTable, $newOrU $result .= $this->generateColumnCode($columnDiff, self::COLUMN_MODE_CHANGE); } - foreach ($tableDiff->renamedColumns as $oldName=>$column) { + foreach ($tableDiff->renamedColumns as $oldName => $column) { $result .= $this->generateColumnRenameCode($oldName, $column->getName()); } - foreach ($tableDiff->removedColumns as $name=>$column) { + foreach ($tableDiff->removedColumns as $name => $column) { $result .= $this->generateColumnRemoveCode($name); } $primaryKey = $changedPrimaryKey ? - $this->findPrimaryKeyIndex($tableDiff->changedIndexes, $newOrUpdatedTable) : + $this->findPrimaryKeyIndex($tableDiff->changedIndexes, $newOrUpdatedTable) : $this->findPrimaryKeyIndex($tableDiff->addedIndexes, $newOrUpdatedTable); if ($primaryKey) { @@ -198,8 +197,7 @@ protected function generateCreateOrUpdateDownCode($tableDiff, $isNewTable, $newO if ($isNewTable) { $result = $this->generateTableDropCode($tableDiff->name); - } - else { + } else { $changedPrimaryKey = $this->getChangedOrRemovedPrimaryKey($tableDiff); $addedPrimaryKey = $this->findPrimaryKeyIndex($tableDiff->addedIndexes, $newOrUpdatedTable); @@ -232,11 +230,11 @@ protected function generateCreateOrUpdateDownCode($tableDiff, $isNewTable, $newO $result .= $this->generateColumnCode($columnDiff, self::COLUMN_MODE_REVERT); } - foreach ($tableDiff->renamedColumns as $oldName=>$column) { + foreach ($tableDiff->renamedColumns as $oldName => $column) { $result .= $this->generateColumnRenameCode($column->getName(), $oldName); } - foreach ($tableDiff->removedColumns as $name=>$column) { + foreach ($tableDiff->removedColumns as $name => $column) { $result .= $this->generateColumnCode($column, self::COLUMN_MODE_CREATE); } @@ -361,22 +359,22 @@ protected function generateColumnCode($columnData, $mode) $forceFlagsChange = false; switch ($mode) { - case self::COLUMN_MODE_CREATE: + case self::COLUMN_MODE_CREATE: $column = $columnData; $changeMode = false; - break; - case self::COLUMN_MODE_CHANGE: + break; + case self::COLUMN_MODE_CHANGE: $column = $columnData->column; $changeMode = true; $forceFlagsChange = in_array('type', $columnData->changedProperties); - break; - case self::COLUMN_MODE_REVERT: + break; + case self::COLUMN_MODE_REVERT: $column = $columnData->fromColumn; $changeMode = true; $forceFlagsChange = in_array('type', $columnData->changedProperties); - break; + break; } $result = $this->generateColumnMethodCall($column); @@ -433,8 +431,7 @@ protected function generateNullable($column, $changeMode, $columnData, $forceFla if (!$column->getNotnull()) { $result = $this->generateBooleanMethod('nullable', true); } - } - elseif (in_array('notnull', $columnData->changedProperties) || $forceFlagsChange) { + } elseif (in_array('notnull', $columnData->changedProperties) || $forceFlagsChange) { $result = $this->generateBooleanMethod('nullable', !$column->getNotnull()); } @@ -449,8 +446,7 @@ protected function generateUnsigned($column, $changeMode, $columnData, $forceFla if ($column->getUnsigned()) { $result = $this->generateBooleanMethod('unsigned', true); } - } - elseif (in_array('unsigned', $columnData->changedProperties) || $forceFlagsChange) { + } elseif (in_array('unsigned', $columnData->changedProperties) || $forceFlagsChange) { $result = $this->generateBooleanMethod('unsigned', $column->getUnsigned()); } @@ -470,12 +466,10 @@ protected function generateDefault($column, $changeMode, $columnData, $forceFlag if (strlen($default)) { $result = $this->generateDefaultMethodCall($default, $column); } - } - elseif (in_array('default', $columnData->changedProperties) || $forceFlagsChange) { + } elseif (in_array('default', $columnData->changedProperties) || $forceFlagsChange) { if (strlen($default)) { $result = $this->generateDefaultMethodCall($default, $column); - } - elseif ($changeMode) { + } elseif ($changeMode) { $result = sprintf('->default(null)'); } } @@ -490,7 +484,7 @@ protected function generateDefaultMethodCall($default, $column) $type = MigrationColumnType::toMigrationMethodName($typeName, $columnName); - if (in_array($type, MigrationColumnType::getIntegerTypes()) || + if (in_array($type, MigrationColumnType::getIntegerTypes()) || in_array($type, MigrationColumnType::getDecimalTypes()) || $type == MigrationColumnType::TYPE_BOOLEAN) { return sprintf('->default(%s)', $default); @@ -548,9 +542,9 @@ protected function implodeColumnList($columnNames) protected function tableHasNameOrColumnChanges($tableDiff, $columnChangesOnly = false) { - $result = $tableDiff->addedColumns - || $tableDiff->changedColumns - || $tableDiff->removedColumns + $result = $tableDiff->addedColumns + || $tableDiff->changedColumns + || $tableDiff->removedColumns || $tableDiff->renamedColumns; if ($columnChangesOnly) { @@ -562,7 +556,7 @@ protected function tableHasNameOrColumnChanges($tableDiff, $columnChangesOnly = protected function tableHasPrimaryKeyChanges($tableDiff) { - return $this->findPrimaryKeyIndex($tableDiff->addedIndexes, $tableDiff->fromTable) || + return $this->findPrimaryKeyIndex($tableDiff->addedIndexes, $tableDiff->fromTable) || $this->findPrimaryKeyIndex($tableDiff->changedIndexes, $tableDiff->fromTable) || $this->findPrimaryKeyIndex($tableDiff->removedIndexes, $tableDiff->fromTable); } diff --git a/lang/en/lang.php b/lang/en/lang.php index 8fb338bb..597a2488 100644 --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -74,7 +74,7 @@ 'error_unsigned_type_not_int' => "Error in the ':column' column. The Unsigned flag can be applied only to integer type columns.", 'error_integer_default_value' => "Invalid default value for the integer column ':column'. The allowed formats are '10', '-10'.", 'error_decimal_default_value' => "Invalid default value for the decimal or double column ':column'. The allowed formats are '1.00', '-1.00'.", - 'error_boolean_default_value' => "Invalid default value for the boolean column ':column'. The allowed values are '0' and '1'.", + 'error_boolean_default_value' => "Invalid default value for the boolean column ':column'. The allowed values are '0' and '1', or 'true' and 'false'.", 'error_unsigned_negative_value' => "The default value for the unsigned column ':column' can't be negative.", 'error_table_already_exists' => "The table ':name' already exists in the database.", 'error_table_name_too_long' => "The table name should not be longer than 64 characters.",