update 2024-05-23 10:43:13 #7
@ -12,7 +12,7 @@
|
||||
namespace VDM\Joomla\Utilities;
|
||||
|
||||
|
||||
use Joomla\CMS\Filter\InputFilter;
|
||||
use Joomla\Filter\InputFilter;
|
||||
use Joomla\CMS\Language\Language;
|
||||
use VDM\Joomla\Utilities\Component\Helper;
|
||||
|
||||
|
@ -22,6 +22,6 @@
|
||||
"namespace": "[[[NamespacePrefix]]]\\Joomla\\Utilities.StringHelper",
|
||||
"description": "Some string tricks\r\n\r\n@since 3.0.9",
|
||||
"licensing_template": "\/**\r\n * @package Joomla.Component.Builder\r\n *\r\n * @created 3rd September, 2020\r\n * @author Llewellyn van der Merwe <https:\/\/dev.vdm.io>\r\n * @git Joomla Component Builder <https:\/\/git.vdm.dev\/joomla\/Component-Builder>\r\n * @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.\r\n * @license GNU General Public License version 2 or later; see LICENSE.txt\r\n *\/\r\n",
|
||||
"head": "use Joomla\\CMS\\Filter\\InputFilter;\r\nuse Joomla\\CMS\\Language\\Language;",
|
||||
"head": "use Joomla\\Filter\\InputFilter;\r\nuse Joomla\\CMS\\Language\\Language;",
|
||||
"composer": ""
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
abstract ActiveRegistry #Orange {
|
||||
# array $active
|
||||
# bool $addAsArray
|
||||
# bool $uniqueArray
|
||||
+ isActive() : bool
|
||||
+ allActive() : array
|
||||
+ setActive(mixed $value, $keys) : void
|
||||
|
@ -40,6 +40,14 @@ abstract class ActiveRegistry implements Activeregistryinterface
|
||||
**/
|
||||
protected bool $addAsArray = false;
|
||||
|
||||
/**
|
||||
* Base switch to keep array values unique
|
||||
*
|
||||
* @var boolean
|
||||
* @since 3.2.2
|
||||
**/
|
||||
protected bool $uniqueArray = false;
|
||||
|
||||
/**
|
||||
* Check if the registry has any content.
|
||||
*
|
||||
@ -157,8 +165,17 @@ abstract class ActiveRegistry implements Activeregistryinterface
|
||||
// Convert to array if it's not already an array
|
||||
$array = [$array];
|
||||
}
|
||||
|
||||
if ($this->uniqueArray && in_array($value, $array))
|
||||
{
|
||||
// we do nothing
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$array[] = $value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_string($value) || is_numeric($value))
|
||||
|
@ -14,6 +14,14 @@
|
||||
**/
|
||||
protected bool $addAsArray = false;
|
||||
|
||||
/**
|
||||
* Base switch to keep array values unique
|
||||
*
|
||||
* @var boolean
|
||||
* @since 3.2.2
|
||||
**/
|
||||
protected bool $uniqueArray = false;
|
||||
|
||||
/**
|
||||
* Check if the registry has any content.
|
||||
*
|
||||
@ -131,8 +139,17 @@
|
||||
// Convert to array if it's not already an array
|
||||
$array = [$array];
|
||||
}
|
||||
|
||||
if ($this->uniqueArray && in_array($value, $array))
|
||||
{
|
||||
// we do nothing
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$array[] = $value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_string($value) || is_numeric($value))
|
||||
|
@ -30,10 +30,10 @@ abstract Schema #Orange {
|
||||
# addMissingColumns(string $table, array $columns) : void
|
||||
# checkColumnsDataType(string $table, array $columns) : void
|
||||
# getColumnDefinition(string $table, string $field) : ?string
|
||||
# checkDefault(string $table, string $column) : void
|
||||
# checkDefault(string $table, string $column) : bool
|
||||
# updateColumnsDataType(string $table, array $columns) : void
|
||||
# getTable(string $table) : string
|
||||
isDataTypeChangeSignificant(string $currentType, string $expectedType) : bool
|
||||
# isDataTypeChangeSignificant(string $currentType, string $expectedType) : bool
|
||||
# adjustExistingDefaults(string $table, string $column, ...) : bool
|
||||
# updateColumnDataType(string $updateString, string $table, ...) : bool
|
||||
# getTableKeys() : string
|
||||
@ -41,6 +41,7 @@ abstract Schema #Orange {
|
||||
# setUniqueKey(array $column) : void
|
||||
# setKey(array $column) : void
|
||||
# getDefaultValue(string $type, ?string $defaultValue, ...) : string
|
||||
# quote(mixed $value) : mixed
|
||||
}
|
||||
|
||||
note right of Schema::__construct
|
||||
@ -117,7 +118,7 @@ note right of Schema::checkDefault
|
||||
Check and Update the default values if needed, including existing data adjustments
|
||||
|
||||
since: 3.2.1
|
||||
return: void
|
||||
return: bool
|
||||
end note
|
||||
|
||||
note left of Schema::updateColumnsDataType
|
||||
@ -138,8 +139,8 @@ note left of Schema::isDataTypeChangeSignificant
|
||||
Determines if the change in data type between two definitions is significant.
|
||||
This function checks if there's a significant difference between the current
|
||||
data type and the expected data type that would require updating the database schema.
|
||||
It ignores size and other modifiers for certain data types where MySQL considers
|
||||
these attributes irrelevant for storage.
|
||||
It ignores display width for numeric types where MySQL considers these attributes
|
||||
irrelevant for storage but considers size and other modifiers for types like VARCHAR.
|
||||
|
||||
since: 3.2.1
|
||||
return: bool
|
||||
@ -213,6 +214,13 @@ defaults by either leaving them unset or applying the provided default, properly
|
||||
bool $pure = false
|
||||
end note
|
||||
|
||||
note left of Schema::quote
|
||||
Set a value based on data type
|
||||
|
||||
since: 3.2.0
|
||||
return: mixed
|
||||
end note
|
||||
|
||||
@enduml
|
||||
```
|
||||
|
||||
|
@ -342,9 +342,16 @@ abstract class Schema implements SchemaInterface
|
||||
'current' => $current->Type,
|
||||
'expected' => $expected['type']
|
||||
];
|
||||
}
|
||||
|
||||
// check if update of default values is needed
|
||||
$this->checkDefault($table, $column);
|
||||
if ($this->checkDefault($table, $column) && !isset($requireUpdate[$column]))
|
||||
{
|
||||
$requireUpdate[$column] = [
|
||||
'column' => $column,
|
||||
'current' => $current->Type,
|
||||
'expected' => $expected['type']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,29 +409,23 @@ abstract class Schema implements SchemaInterface
|
||||
* @param string $table The table to update.
|
||||
* @param string $column The column/field to check.
|
||||
*
|
||||
* @return void
|
||||
* @return bool
|
||||
* @since 3.2.1
|
||||
*/
|
||||
protected function checkDefault(string $table, string $column): void
|
||||
protected function checkDefault(string $table, string $column): bool
|
||||
{
|
||||
// Retrieve the expected column configuration
|
||||
$expected = $this->table->get($table, $column, 'db');
|
||||
|
||||
// Skip updates if the column is auto_increment
|
||||
if (isset($expected['auto_increment']) && $expected['auto_increment'])
|
||||
if (isset($expected['auto_increment']) && $expected['auto_increment'] === true)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the current column configuration
|
||||
$current = $this->columns[$column];
|
||||
|
||||
// Check if default should be empty and current default is null, skip processing
|
||||
if (strtoupper($expected['default']) === 'EMPTY' && $current->Default === NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the new default value based on the expected settings
|
||||
$type = $expected['type'] ?? 'TEXT';
|
||||
$db_default = isset($expected['default']) ? $expected['default'] : null;
|
||||
@ -434,7 +435,17 @@ abstract class Schema implements SchemaInterface
|
||||
if (is_numeric($newDefault) && $this->adjustExistingDefaults($table, $column, $current->Default, $newDefault))
|
||||
{
|
||||
$this->success[] = "Success: updated the ($column) defaults in $table table.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_string($expected['default']) && strtoupper($expected['default']) === 'EMPTY' &&
|
||||
is_string($current->Default) && strpos($current->Default, 'EMPTY') !== false)
|
||||
{
|
||||
return true; // little fix
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -486,8 +497,8 @@ abstract class Schema implements SchemaInterface
|
||||
*
|
||||
* This function checks if there's a significant difference between the current
|
||||
* data type and the expected data type that would require updating the database schema.
|
||||
* It ignores size and other modifiers for certain data types where MySQL considers
|
||||
* these attributes irrelevant for storage.
|
||||
* It ignores display width for numeric types where MySQL considers these attributes
|
||||
* irrelevant for storage but considers size and other modifiers for types like VARCHAR.
|
||||
*
|
||||
* @param string $currentType The current data type from the database schema.
|
||||
* @param string $expectedType The expected data type to validate against.
|
||||
@ -495,47 +506,43 @@ abstract class Schema implements SchemaInterface
|
||||
* @return bool Returns true if the data type change is significant, otherwise false.
|
||||
* @since 3.2.1
|
||||
*/
|
||||
function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool
|
||||
{
|
||||
// we only do this for Joomla 4+
|
||||
if ($this->currentVersion != 3)
|
||||
protected function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool
|
||||
{
|
||||
// Normalize both input types to lowercase for case-insensitive comparison
|
||||
$currentType = strtolower($currentType);
|
||||
$expectedType = strtolower($expectedType);
|
||||
|
||||
// Define types where size or other modifiers are irrelevant
|
||||
// Regex to extract the base data type and numeric parameters with named groups
|
||||
$typePattern = '/^(?<datatype>\w+)(\((?<params>\d+(,\d+)?)\))?/';
|
||||
|
||||
// Match types and parameters
|
||||
preg_match($typePattern, $currentType, $currentMatches);
|
||||
preg_match($typePattern, $expectedType, $expectedMatches);
|
||||
|
||||
// Compare base types
|
||||
if ($currentMatches['datatype'] !== $expectedMatches['datatype'])
|
||||
{
|
||||
return true; // Base types differ
|
||||
}
|
||||
|
||||
// Define types where size and other modifiers are irrelevant
|
||||
$sizeIrrelevantTypes = [
|
||||
'int', 'tinyint', 'smallint', 'mediumint', 'bigint', // Standard integer types
|
||||
'int unsigned', 'tinyint unsigned', 'smallint unsigned', 'mediumint unsigned', 'bigint unsigned', // Unsigned integer types
|
||||
'int', 'tinyint', 'smallint', 'mediumint', 'bigint',
|
||||
'float', 'double', 'decimal', 'numeric' // Numeric types where display width is irrelevant
|
||||
];
|
||||
|
||||
// Check if the type involves size-irrelevant types
|
||||
foreach ($sizeIrrelevantTypes as $type)
|
||||
// If the type is not in the size irrelevant list, compare full definitions
|
||||
if (!in_array($currentMatches['datatype'], $sizeIrrelevantTypes))
|
||||
{
|
||||
if (strpos($expectedType, $type) !== false)
|
||||
{
|
||||
// Remove any numeric sizes and modifiers for comparison
|
||||
$pattern = '/\(\d+\)|unsigned|\s*/';
|
||||
$cleanCurrentType = preg_replace($pattern, '', $currentType);
|
||||
$cleanExpectedType = preg_replace($pattern, '', $expectedType);
|
||||
|
||||
// Compare the cleaned types
|
||||
if ($cleanCurrentType === $cleanExpectedType)
|
||||
{
|
||||
return false; // No significant change
|
||||
}
|
||||
}
|
||||
}
|
||||
return $currentType !== $expectedType; // Use full definition for types where size matters
|
||||
}
|
||||
|
||||
// Perform a standard case-insensitive comparison for other types
|
||||
if (strcasecmp($currentType, $expectedType) == 0)
|
||||
{
|
||||
return false; // No significant change
|
||||
}
|
||||
// For size irrelevant types, only compare base type, ignoring size and unsigned
|
||||
$currentBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $currentType);
|
||||
$expectedBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $expectedType);
|
||||
|
||||
return true; // Significant datatype change detected
|
||||
// Perform a final comparison for numeric types ignoring size
|
||||
return $currentBaseType !== $expectedBaseType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -684,7 +691,7 @@ abstract class Schema implements SchemaInterface
|
||||
*/
|
||||
protected function getDefaultValue(string $type, ?string $defaultValue, bool $pure = false): string
|
||||
{
|
||||
if ($defaultValue === null || strtoupper($defaultValue) === 'EMPTY')
|
||||
if ($defaultValue === null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
@ -696,7 +703,52 @@ abstract class Schema implements SchemaInterface
|
||||
}
|
||||
|
||||
// Apply and quote the default value
|
||||
return $pure ? $defaultValue : " DEFAULT " . $this->db->quote($defaultValue);
|
||||
$sql_default = $this->quote($defaultValue);
|
||||
return $pure ? $defaultValue : " DEFAULT $sql_default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value based on data type
|
||||
*
|
||||
* @param mixed $value The value to set
|
||||
*
|
||||
* @return mixed
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected function quote($value)
|
||||
{
|
||||
if ($value === null) // hmm the null does pose an issue (will keep an eye on this)
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (is_string($value) && strtoupper($value) === 'EMPTY')
|
||||
{
|
||||
return "''";
|
||||
}
|
||||
elseif (is_numeric($value))
|
||||
{
|
||||
if (filter_var($value, FILTER_VALIDATE_INT))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
elseif (filter_var($value, FILTER_VALIDATE_FLOAT))
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
}
|
||||
elseif (is_bool($value)) // not sure if this will work well (but its correct)
|
||||
{
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
// For date and datetime values
|
||||
elseif ($value instanceof \DateTime)
|
||||
{
|
||||
return $this->db->quote($value->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
// For other data types, just escape it
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,9 +315,16 @@
|
||||
'current' => $current->Type,
|
||||
'expected' => $expected['type']
|
||||
];
|
||||
}
|
||||
|
||||
// check if update of default values is needed
|
||||
$this->checkDefault($table, $column);
|
||||
if ($this->checkDefault($table, $column) && !isset($requireUpdate[$column]))
|
||||
{
|
||||
$requireUpdate[$column] = [
|
||||
'column' => $column,
|
||||
'current' => $current->Type,
|
||||
'expected' => $expected['type']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,29 +382,23 @@
|
||||
* @param string $table The table to update.
|
||||
* @param string $column The column/field to check.
|
||||
*
|
||||
* @return void
|
||||
* @return bool
|
||||
* @since 3.2.1
|
||||
*/
|
||||
protected function checkDefault(string $table, string $column): void
|
||||
protected function checkDefault(string $table, string $column): bool
|
||||
{
|
||||
// Retrieve the expected column configuration
|
||||
$expected = $this->table->get($table, $column, 'db');
|
||||
|
||||
// Skip updates if the column is auto_increment
|
||||
if (isset($expected['auto_increment']) && $expected['auto_increment'])
|
||||
if (isset($expected['auto_increment']) && $expected['auto_increment'] === true)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve the current column configuration
|
||||
$current = $this->columns[$column];
|
||||
|
||||
// Check if default should be empty and current default is null, skip processing
|
||||
if (strtoupper($expected['default']) === 'EMPTY' && $current->Default === NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the new default value based on the expected settings
|
||||
$type = $expected['type'] ?? 'TEXT';
|
||||
$db_default = isset($expected['default']) ? $expected['default'] : null;
|
||||
@ -407,7 +408,17 @@
|
||||
if (is_numeric($newDefault) && $this->adjustExistingDefaults($table, $column, $current->Default, $newDefault))
|
||||
{
|
||||
$this->success[] = "Success: updated the ($column) defaults in $table table.";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_string($expected['default']) && strtoupper($expected['default']) === 'EMPTY' &&
|
||||
is_string($current->Default) && strpos($current->Default, 'EMPTY') !== false)
|
||||
{
|
||||
return true; // little fix
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -459,8 +470,8 @@
|
||||
*
|
||||
* This function checks if there's a significant difference between the current
|
||||
* data type and the expected data type that would require updating the database schema.
|
||||
* It ignores size and other modifiers for certain data types where MySQL considers
|
||||
* these attributes irrelevant for storage.
|
||||
* It ignores display width for numeric types where MySQL considers these attributes
|
||||
* irrelevant for storage but considers size and other modifiers for types like VARCHAR.
|
||||
*
|
||||
* @param string $currentType The current data type from the database schema.
|
||||
* @param string $expectedType The expected data type to validate against.
|
||||
@ -468,47 +479,43 @@
|
||||
* @return bool Returns true if the data type change is significant, otherwise false.
|
||||
* @since 3.2.1
|
||||
*/
|
||||
function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool
|
||||
{
|
||||
// we only do this for Joomla 4+
|
||||
if ($this->currentVersion != 3)
|
||||
protected function isDataTypeChangeSignificant(string $currentType, string $expectedType): bool
|
||||
{
|
||||
// Normalize both input types to lowercase for case-insensitive comparison
|
||||
$currentType = strtolower($currentType);
|
||||
$expectedType = strtolower($expectedType);
|
||||
|
||||
// Define types where size or other modifiers are irrelevant
|
||||
// Regex to extract the base data type and numeric parameters with named groups
|
||||
$typePattern = '/^(?<datatype>\w+)(\((?<params>\d+(,\d+)?)\))?/';
|
||||
|
||||
// Match types and parameters
|
||||
preg_match($typePattern, $currentType, $currentMatches);
|
||||
preg_match($typePattern, $expectedType, $expectedMatches);
|
||||
|
||||
// Compare base types
|
||||
if ($currentMatches['datatype'] !== $expectedMatches['datatype'])
|
||||
{
|
||||
return true; // Base types differ
|
||||
}
|
||||
|
||||
// Define types where size and other modifiers are irrelevant
|
||||
$sizeIrrelevantTypes = [
|
||||
'int', 'tinyint', 'smallint', 'mediumint', 'bigint', // Standard integer types
|
||||
'int unsigned', 'tinyint unsigned', 'smallint unsigned', 'mediumint unsigned', 'bigint unsigned', // Unsigned integer types
|
||||
'int', 'tinyint', 'smallint', 'mediumint', 'bigint',
|
||||
'float', 'double', 'decimal', 'numeric' // Numeric types where display width is irrelevant
|
||||
];
|
||||
|
||||
// Check if the type involves size-irrelevant types
|
||||
foreach ($sizeIrrelevantTypes as $type)
|
||||
// If the type is not in the size irrelevant list, compare full definitions
|
||||
if (!in_array($currentMatches['datatype'], $sizeIrrelevantTypes))
|
||||
{
|
||||
if (strpos($expectedType, $type) !== false)
|
||||
{
|
||||
// Remove any numeric sizes and modifiers for comparison
|
||||
$pattern = '/\(\d+\)|unsigned|\s*/';
|
||||
$cleanCurrentType = preg_replace($pattern, '', $currentType);
|
||||
$cleanExpectedType = preg_replace($pattern, '', $expectedType);
|
||||
|
||||
// Compare the cleaned types
|
||||
if ($cleanCurrentType === $cleanExpectedType)
|
||||
{
|
||||
return false; // No significant change
|
||||
}
|
||||
}
|
||||
}
|
||||
return $currentType !== $expectedType; // Use full definition for types where size matters
|
||||
}
|
||||
|
||||
// Perform a standard case-insensitive comparison for other types
|
||||
if (strcasecmp($currentType, $expectedType) == 0)
|
||||
{
|
||||
return false; // No significant change
|
||||
}
|
||||
// For size irrelevant types, only compare base type, ignoring size and unsigned
|
||||
$currentBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $currentType);
|
||||
$expectedBaseType = preg_replace('/\(\d+(,\d+)?\)|unsigned/', '', $expectedType);
|
||||
|
||||
return true; // Significant datatype change detected
|
||||
// Perform a final comparison for numeric types ignoring size
|
||||
return $currentBaseType !== $expectedBaseType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -657,7 +664,7 @@
|
||||
*/
|
||||
protected function getDefaultValue(string $type, ?string $defaultValue, bool $pure = false): string
|
||||
{
|
||||
if ($defaultValue === null || strtoupper($defaultValue) === 'EMPTY')
|
||||
if ($defaultValue === null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
@ -669,5 +676,50 @@
|
||||
}
|
||||
|
||||
// Apply and quote the default value
|
||||
return $pure ? $defaultValue : " DEFAULT " . $this->db->quote($defaultValue);
|
||||
$sql_default = $this->quote($defaultValue);
|
||||
return $pure ? $defaultValue : " DEFAULT $sql_default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a value based on data type
|
||||
*
|
||||
* @param mixed $value The value to set
|
||||
*
|
||||
* @return mixed
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected function quote($value)
|
||||
{
|
||||
if ($value === null) // hmm the null does pose an issue (will keep an eye on this)
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (is_string($value) && strtoupper($value) === 'EMPTY')
|
||||
{
|
||||
return "''";
|
||||
}
|
||||
elseif (is_numeric($value))
|
||||
{
|
||||
if (filter_var($value, FILTER_VALIDATE_INT))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
elseif (filter_var($value, FILTER_VALIDATE_FLOAT))
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
}
|
||||
elseif (is_bool($value)) // not sure if this will work well (but its correct)
|
||||
{
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
// For date and datetime values
|
||||
elseif ($value instanceof \DateTime)
|
||||
{
|
||||
return $this->db->quote($value->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
// For other data types, just escape it
|
||||
return $this->db->quote($value);
|
||||
}
|
Loading…
Reference in New Issue
Block a user