From 5a44b550037184cb52e6142f5c408a398607b2c1 Mon Sep 17 00:00:00 2001 From: vc-urvin Date: Thu, 27 Apr 2023 18:21:16 +0530 Subject: [PATCH] Add validation for unique constraint update messages add suggestion for varchar datatype --- src/Commands/DBConstraintCommand.php | 27 ++++++---- src/Commands/DBStandardCommand.php | 2 +- src/Constants/Constant.php | 1 + .../migrations/update_table_index.php | 2 +- src/Lang/en/messages.php | 29 ++++++----- src/Services/AuditService.php | 51 ++++++++++++------- src/Services/DBConnectionService.php | 23 ++++++++- src/Services/NamingRuleService.php | 8 +-- src/Services/RuleService.php | 22 +++++++- src/views/constraint.blade.php | 2 +- src/views/fail_standard_table.blade.php | 7 ++- 11 files changed, 120 insertions(+), 54 deletions(-) diff --git a/src/Commands/DBConstraintCommand.php b/src/Commands/DBConstraintCommand.php index 6d76d70..7110435 100644 --- a/src/Commands/DBConstraintCommand.php +++ b/src/Commands/DBConstraintCommand.php @@ -198,7 +198,7 @@ public function selectedConstraint(string $selectConstrain, array $noConstraintF $auditService = app(AuditService::class); - if ($selectConstrain === Constant::CONSTRAINT_FOREIGN_KEY || $selectConstrain === Constant::CONSTRAINT_UNIQUE_KEY) { + if ($selectConstrain === Constant::CONSTRAINT_FOREIGN_KEY) { $tableHasValue = $auditService->tableHasValue($tableName); if ($tableHasValue) { @@ -213,15 +213,24 @@ public function selectedConstraint(string $selectConstrain, array $noConstraintF $fields = $noConstraintFields['mix']; } - $selectField = $this->choice( - __('Lang::messages.constraint.question.field_selection') . ' ' . strtolower($selectConstrain) . ' key', - $fields - ); + if($selectConstrain === Constant::CONSTRAINT_UNIQUE_KEY) { + $fields = $auditService->getUniqueFields($tableName, $noConstraintFields['mix']); + if (empty($fields)) { + $this->errorMessage(__('Lang::messages.constraint.error_message.unique_constraint_not_apply')); + } + } - if ($selectConstrain === Constant::CONSTRAINT_FOREIGN_KEY) { - $this->foreignKeyConstraint($tableName, $selectField); - } else { - $auditService->addConstraint($tableName, $selectField, $selectConstrain); + if (!$this->skip) { + $selectField = $this->choice( + __('Lang::messages.constraint.question.field_selection') . ' ' . strtolower($selectConstrain) . ' key', + $fields + ); + + if ($selectConstrain === Constant::CONSTRAINT_FOREIGN_KEY) { + $this->foreignKeyConstraint($tableName, $selectField); + } else { + $auditService->addConstraint($tableName, $selectField, $selectConstrain); + } } } diff --git a/src/Commands/DBStandardCommand.php b/src/Commands/DBStandardCommand.php index f1ff88d..1c7a6ed 100644 --- a/src/Commands/DBStandardCommand.php +++ b/src/Commands/DBStandardCommand.php @@ -40,7 +40,7 @@ public function handle(): ?int $continue = Constant::STATUS_TRUE; do { - $tableName = $this->ask('Please enter table name if you want to see the table report'); + $tableName = $this->anticipate('Please enter table name if you want to see the table report', $ruleService->getTableList()); if (empty($tableName)) { return render(view('DBAuditor::error_message', ['message' => 'No Table Found'])); diff --git a/src/Constants/Constant.php b/src/Constants/Constant.php index a3d6672..fb843ec 100644 --- a/src/Constants/Constant.php +++ b/src/Constants/Constant.php @@ -43,6 +43,7 @@ class Constant // Datatype List public const DATATYPE_VARCHAR = 'varchar'; + public const DATATYPE_VARCHAR_SIZE = '255'; public const DATATYPE_STRING = 'string'; public const DATATYPE_INT = 'int'; public const DATATYPE_INTEGER = 'integer'; diff --git a/src/Database/migrations/update_table_index.php b/src/Database/migrations/update_table_index.php index 7dfffa0..2628de4 100644 --- a/src/Database/migrations/update_table_index.php +++ b/src/Database/migrations/update_table_index.php @@ -12,7 +12,7 @@ public function up(): void { Schema::table($tableName, function (Blueprint $table) { - $table->$dataType($fieldName)->change()->index(); + $table->$dataType($fieldName, $length)->change()->index(); }); } diff --git a/src/Lang/en/messages.php b/src/Lang/en/messages.php index d660b4f..94f5a70 100644 --- a/src/Lang/en/messages.php +++ b/src/Lang/en/messages.php @@ -19,29 +19,34 @@ 'question' => [ 'table_selection' => 'Which table would you like to audit?', 'continue' => 'Do you want add more constraint?', - 'constraint_selection' => 'Please select a constraint which you want to add', - 'field_selection' => 'Please select a field to add constraint', - 'foreign_table' => 'Please add foreign table name', - 'foreign_field' => 'Please add foreign table primary key name', + 'constraint_selection' => 'Please select a constraint which you want to add.', + 'field_selection' => 'Please select a field to add constraint.', + 'foreign_table' => 'Please add foreign table name.', + 'foreign_field' => 'Please add primary key name of foreign table.', ], 'success_message' => [ - 'constraint_added' => 'Congratulations! Constraint Added Successfully' + 'constraint_added' => 'Congratulations! Constraint Added Successfully.' ], 'error_message' => [ - 'constraint_not_apply' => 'Can not apply :constraint key | Please truncate table', - 'foreign_not_apply' => 'Columns must have the same data type', + 'constraint_not_apply' => 'Can not apply :constraint key | Please truncate table.', + 'foreign_not_apply' => 'Columns must have the same datatype.', 'table_not_found' => 'Foreign table not found.', 'field_not_found' => 'Foreign field not found.', - 'foreign_selected_table_match' => "Can't add constraint because foreign :foreign table and selected :selected table match." + 'foreign_selected_table_match' => "Can't add constraint because :selected table and foreign :foreign table are same. Please use different table name.", + 'unique_constraint_not_apply' => "All field values are duplicate. You can't add unique constraint.", ] ], 'standard' => [ 'error_message' => [ - 'length' => 'Table name should not be more than 64 characters', - 'plural' => 'Table name should be plural', + 'length' => 'Table name should not be more than 64 characters.', + 'plural' => 'Table name should be plural.', 'space' => 'Space between words is not advised. Please Use Underscore "_"', - 'alphabets' => 'Numbers are not for names and is not advised! Please use alphabets for name', - 'lowercase' => 'Name should be lowercase' + 'alphabets' => 'Numbers are not for names and is not advised! Please use alphabets for name.', + 'lowercase' => 'Name should be in lowercase.', + 'datatype_change' => 'Here you can use CHAR datatype instead of VARCHAR if data values in a column are of the same length.', + ], + 'question' => [ + 'table_selection' => 'Please enter table name if you want to see the table report', ] ] ]; diff --git a/src/Services/AuditService.php b/src/Services/AuditService.php index 677bee1..82b09b9 100644 --- a/src/Services/AuditService.php +++ b/src/Services/AuditService.php @@ -87,7 +87,10 @@ public function getNoConstraintFields(string $tableName): array if (!in_array($field->DATA_TYPE, Constant::RESTRICT_DATATYPE)) { if (str_contains($field->DATA_TYPE, "int")) { $fields['integer'][] = $field->COLUMN_NAME; - } else { + } + $fieldDetails = $this->dBConnectionService->getFieldDataType($tableName, $field->COLUMN_NAME); + + if($fieldDetails['size'] <= Constant::DATATYPE_VARCHAR_SIZE) { $fields['mix'][] = $field->COLUMN_NAME; } } @@ -109,7 +112,10 @@ public function getConstraintList(string $tableName, array $fields): array $constrainList = Constant::ARRAY_DECLARATION; if (!empty($fields['integer'])) { - $constrainList[] = Constant::CONSTRAINT_FOREIGN_KEY; + + if(!$this->tableHasValue($tableName)) { + $constrainList[] = Constant::CONSTRAINT_FOREIGN_KEY; + } if (empty($this->getConstraintField($tableName, Constant::CONSTRAINT_PRIMARY_KEY))) { $constrainList[] = Constant::CONSTRAINT_PRIMARY_KEY; @@ -118,7 +124,10 @@ public function getConstraintList(string $tableName, array $fields): array if (!empty($fields['mix'])) { $constrainList[] = Constant::CONSTRAINT_INDEX_KEY; - $constrainList[] = Constant::CONSTRAINT_UNIQUE_KEY; + + if(!empty($this->getUniqueFields($tableName, $fields['mix']))) { + $constrainList[] = Constant::CONSTRAINT_UNIQUE_KEY; + } } return $constrainList; } @@ -254,16 +263,16 @@ public function migrateConstrain( string $referenceField = null, string $referenceTableName = null): bool { try { - $dataType = $this->getFieldDataType($tableName, $fieldName); + $fieldDetails = $this->dBConnectionService->getFieldDataType($tableName, $fieldName); $fieldDataType = Constant::NULL; - if ($dataType) { - if ($dataType === Constant::DATATYPE_VARCHAR) { + if (!empty($fieldDetails['data_type'])) { + if ($fieldDetails['data_type'] === Constant::DATATYPE_VARCHAR) { $fieldDataType = Constant::DATATYPE_STRING; - } elseif ($dataType === Constant::DATATYPE_INT) { + } elseif ($fieldDetails['data_type'] === Constant::DATATYPE_INT) { $fieldDataType = Constant::DATATYPE_INTEGER; } else { - $fieldDataType = $dataType; + $fieldDataType = $fieldDetails['data_type']; } } @@ -272,7 +281,8 @@ public function migrateConstrain( "fieldName" => $fieldName, "referenceField" => $referenceField, "referenceTable" => $referenceTableName, - "dataType" => $fieldDataType + "dataType" => $fieldDataType, + 'length' => $fieldDetails['size'], ]; $contents = file_get_contents(__DIR__ . "/../Database/migrations/" . $fileName); @@ -290,7 +300,6 @@ public function migrateConstrain( File::put(database_path("/migrations/" . $time . "_update_" . $tableName . "_" . $fieldName . "_" . strtolower($constrainName) . ".php"), $contents); Artisan::call("migrate", [ - '--force' => true, '--path' => "database/migrations/" . $time . "_update_" . $tableName . "_" . $fieldName . "_" . strtolower($constrainName) . ".php" ]); } catch (Exception $exception) { @@ -300,23 +309,27 @@ public function migrateConstrain( } /** - * Get Field Data Type + * Get Unique Fields * @param string $tableName - * @param string $fieldName - * @return mixed + * @param array $fields + * @return array */ - public function getFieldDataType(string $tableName, string $fieldName): mixed + public function getUniqueFields(string $tableName, array $fields): array { + $uniqueField = Constant::ARRAY_DECLARATION; try { - $dataType = DB::select("SELECT `DATA_TYPE` FROM `INFORMATION_SCHEMA`.`COLUMNS` - WHERE `TABLE_SCHEMA`= '" . env('DB_DATABASE') . "' AND `TABLE_NAME`= '" . $tableName . "' AND `COLUMN_NAME` = '" . $fieldName . "' "); + foreach ($fields as $field) { + $query = "SELECT `". $field ."`, COUNT(`". $field ."`) as count FROM ".$tableName." GROUP BY `".$field."` HAVING COUNT(`".$field."`) > 1"; + $result = DB::select($query); - if (isset($dataType[0]->DATA_TYPE) && $dataType[0]->DATA_TYPE !== null) { - return $dataType[0]->DATA_TYPE; + if (empty($result)) { + $uniqueField[] = $field; + } } + } catch (Exception $exception) { Log::error($exception->getMessage()); } - return Constant::STATUS_FALSE; + return $uniqueField; } } diff --git a/src/Services/DBConnectionService.php b/src/Services/DBConnectionService.php index 9c5372c..d74e697 100644 --- a/src/Services/DBConnectionService.php +++ b/src/Services/DBConnectionService.php @@ -42,7 +42,7 @@ public function getFields(string $tableName): array { $fields = Constant::ARRAY_DECLARATION; try { - $fieldDetails = DB::select("Describe {$tableName}"); + $fieldDetails = DB::select("Describe `$tableName`"); foreach ($fieldDetails as $field) { $fields[] = $field->Field; } @@ -112,4 +112,25 @@ public function getTableSize(string $tableName): string } return Constant::STATUS_FALSE; } + + /** + * Get Field Data Type + * @param string $tableName + * @param string $fieldName + * @return array|bool + */ + public function getFieldDataType(string $tableName, string $fieldName): array|bool + { + try { + $dataType = DB::select("SELECT `DATA_TYPE`, `CHARACTER_MAXIMUM_LENGTH` FROM `INFORMATION_SCHEMA`.`COLUMNS` + WHERE `TABLE_SCHEMA`= '" . env('DB_DATABASE') . "' AND `TABLE_NAME`= '" . $tableName . "' AND `COLUMN_NAME` = '" . $fieldName . "' "); + + if (isset($dataType[0]->DATA_TYPE) && $dataType[0]->DATA_TYPE !== null) { + return ['data_type' => $dataType[0]->DATA_TYPE, 'size' => $dataType[0]->CHARACTER_MAXIMUM_LENGTH]; + } + } catch (Exception $exception) { + Log::error($exception->getMessage()); + } + return Constant::STATUS_FALSE; + } } diff --git a/src/Services/NamingRuleService.php b/src/Services/NamingRuleService.php index 6f3a5b1..0dbcf80 100644 --- a/src/Services/NamingRuleService.php +++ b/src/Services/NamingRuleService.php @@ -56,14 +56,14 @@ public function nameHasNoSpace(string $name): string|bool /** * Check name only in alphabets. - * @param string $tableName + * @param string $name * @return string|bool */ - public function nameHasOnlyAlphabets(string $tableName): string|bool + public function nameHasOnlyAlphabets(string $name): string|bool { - $name = $this->removeSpecialCharacter($tableName); + $name = str_replace(' ', '', $this->removeSpecialCharacter($name)); if (!ctype_alpha($name)) { - return $this->addSpecialCharacter(preg_replace(Constant::NUMERIC_PATTERN, '', $tableName)); + return $this->addSpecialCharacter(preg_replace(Constant::NUMERIC_PATTERN, '', $name)); } return Constant::STATUS_TRUE; } diff --git a/src/Services/RuleService.php b/src/Services/RuleService.php index ac4c06e..4369467 100644 --- a/src/Services/RuleService.php +++ b/src/Services/RuleService.php @@ -23,6 +23,15 @@ public function __construct( { } + /** + * Get Table List + * @return array + */ + public function getTableList() : array + { + return $this->dBConnectionService->getTableList(); + } + /** * Check table name rules * @return array @@ -119,9 +128,18 @@ public function fieldRules(string $tableName): array { $checkFields = Constant::ARRAY_DECLARATION; try { - $filedDetails = $this->dBConnectionService->getFields($tableName); - foreach ($filedDetails as $field) { + $fields = $this->dBConnectionService->getFields($tableName); + + foreach ($fields as $field) { $checkFields[$field] = $this->checkRules($field, Constant::FIELD_RULES); + + $fieldDetails = $this->dBConnectionService->getFieldDataType($tableName, $field); + + if ($fieldDetails['data_type'] === Constant::DATATYPE_VARCHAR) { + if($fieldDetails['size'] <= Constant::DATATYPE_VARCHAR_SIZE) { + $checkFields[$field] = ["" => __('Lang::messages.standard.error_message.datatype_change')]; + } + } } } catch (Exception $exception) { Log::error($exception->getMessage()); diff --git a/src/views/constraint.blade.php b/src/views/constraint.blade.php index b3e7222..fbd009f 100644 --- a/src/views/constraint.blade.php +++ b/src/views/constraint.blade.php @@ -1,7 +1,7 @@
TABLE NAME : {{ str_replace("_", ' ', strtoupper($data['table'])) }} + class="px-1 bg-blue-500 text-black">{{ $data['table'] }}
diff --git a/src/views/fail_standard_table.blade.php b/src/views/fail_standard_table.blade.php index e07368d..7d5a2fe 100644 --- a/src/views/fail_standard_table.blade.php +++ b/src/views/fail_standard_table.blade.php @@ -1,6 +1,5 @@
- TABLE NAME : {{ str_replace("_", ' ', $tableStatus['table']) }} + TABLE NAME : {{ str_replace("_", ' ', $tableStatus['table']) }} @if ($tableStatus['table_comment'])
suggestion(s) @@ -8,7 +7,7 @@ class="px-2 font-bold bg-blue text-white"> {{ str_replace("_", ' ', $tableStatus
    @foreach ($tableStatus['table_comment'] as $commentKey => $comment)
  1. - {{ $comment }} + {{ $comment }} ( {{ $commentKey }} )
  2. @endforeach @@ -33,7 +32,7 @@ class="px-2 font-bold bg-blue text-white"> {{ str_replace("_", ' ', $tableStatus - 👉 {{ $fieldComment }} ( {{ $solution }} ) + 👉 {{ $fieldComment }} {{ $solution !== "" ? '(' .$solution. ')' : null }} @endforeach @else