Release of v5.1.1-beta5
Completely refactors the SQL tweaks and SQL dump classes.
This commit is contained in:
@ -15,6 +15,7 @@ namespace VDM\Joomla\Abstraction;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface as JoomlaDatabase;
|
||||
use VDM\Joomla\Utilities\Component\Helper;
|
||||
use VDM\Joomla\Database\QuoteTrait;
|
||||
|
||||
|
||||
/**
|
||||
@ -24,6 +25,13 @@ use VDM\Joomla\Utilities\Component\Helper;
|
||||
*/
|
||||
abstract class Database
|
||||
{
|
||||
/**
|
||||
* Function to quote values
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
use QuoteTrait;
|
||||
|
||||
/**
|
||||
* Database object to query local DB
|
||||
*
|
||||
@ -32,14 +40,6 @@ abstract class Database
|
||||
*/
|
||||
protected JoomlaDatabase $db;
|
||||
|
||||
/**
|
||||
* Date format to return
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $dateFormat = 'Y-m-d H:i:s';
|
||||
|
||||
/**
|
||||
* Current component code name
|
||||
*
|
||||
@ -70,82 +70,6 @@ abstract class Database
|
||||
$this->table = '#__' . $this->componentCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely quote a value for database use, preserving data integrity.
|
||||
*
|
||||
* - Native ints/floats passed as-is
|
||||
* - Clean integer strings are cast to int
|
||||
* - Clean float strings are cast to float
|
||||
* - Scientific notation is quoted to preserve original form
|
||||
* - Leading-zero integers are quoted
|
||||
* - Dates are formatted and quoted
|
||||
* - Booleans are converted to TRUE/FALSE
|
||||
* - Null is converted to NULL
|
||||
* - All else is quoted with Joomla's db quote
|
||||
*
|
||||
* @param mixed $value The value to quote.
|
||||
*
|
||||
* @return mixed
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function quote($value)
|
||||
{
|
||||
// NULL handling
|
||||
if ($value === null)
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
// DateTime handling
|
||||
if ($value instanceof \DateTimeInterface)
|
||||
{
|
||||
return $this->db->quote($value->format($this->getDateFormat()));
|
||||
}
|
||||
|
||||
// Native numeric types
|
||||
if (is_int($value) || is_float($value))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Stringified numeric values
|
||||
if (is_string($value) && is_numeric($value))
|
||||
{
|
||||
// Case 1: Leading-zero integers like "007"
|
||||
if ($value[0] === '0' && strlen($value) > 1 && ctype_digit($value))
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
// Case 2: Scientific notation - preserve exact format
|
||||
if (stripos($value, 'e') !== false)
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
// Case 3: Decimal float string (not scientific)
|
||||
if (str_contains($value, '.'))
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
// Case 4: Pure integer string
|
||||
if (ctype_digit($value))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Boolean handling
|
||||
if (is_bool($value))
|
||||
{
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
|
||||
// Everything else
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a table name, adding the
|
||||
* core component as needed
|
||||
|
@ -55,6 +55,8 @@ class Infusion extends Interpretation
|
||||
*/
|
||||
public $removeSiteEditFolder = true;
|
||||
|
||||
public $secondRunAdmin;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -63,8 +63,17 @@ class Sql
|
||||
*/
|
||||
public function set(object &$item)
|
||||
{
|
||||
if (isset($item->add_sql) && (int) $item->add_sql === 1 && isset($item->source))
|
||||
if (isset($item->add_sql) && (int) $item->add_sql === 1
|
||||
&& isset($item->source) && isset($item->name_single_code))
|
||||
{
|
||||
// avoid setting this a multiple time for the same name_singe_code
|
||||
if ((int) $item->source === 1 && isset($this->dispenser->hub['sql'])
|
||||
&& is_array($this->dispenser->hub['sql'])
|
||||
&& isset($this->dispenser->hub['sql'][$item->name_single_code]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int) $item->source === 1 && isset($item->tables) &&
|
||||
($string = $this->dump->get(
|
||||
$item->tables, $item->name_single_code, $item->guid
|
||||
|
@ -12,11 +12,11 @@
|
||||
namespace VDM\Joomla\Componentbuilder\Compiler\Model;
|
||||
|
||||
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\Database\DatabaseInterface as JoomlaDatabase;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Database\QuoteTrait;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Utilities\Placefix;
|
||||
use VDM\Joomla\Utilities\StringHelper;
|
||||
|
||||
|
||||
/**
|
||||
@ -26,6 +26,13 @@ use VDM\Joomla\Utilities\StringHelper;
|
||||
*/
|
||||
class Sqldump
|
||||
{
|
||||
/**
|
||||
* Function to quote values
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
use QuoteTrait;
|
||||
|
||||
/**
|
||||
* The compiler registry
|
||||
*
|
||||
@ -37,251 +44,91 @@ class Sqldump
|
||||
/**
|
||||
* Database object to query local DB
|
||||
*
|
||||
* @var JoomlaDatabase
|
||||
* @since 3.2.0
|
||||
**/
|
||||
protected $db;
|
||||
protected JoomlaDatabase $db;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Registry $registry The compiler registry object.
|
||||
|
||||
* @param Registry $registry The compiler registry object.
|
||||
* @param JoomlaDatabase|null $db The joomla database object.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
public function __construct(Registry $registry, ?JoomlaDatabase $db = null)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->db = Factory::getDbo();
|
||||
$this->db = $db ?: Factory::getContainer()->get(JoomlaDatabase::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SQL Dump
|
||||
* Generate SQL dump for given view data.
|
||||
*
|
||||
* @param array $tables The tables to use in build
|
||||
* @param string $view The target view/table to dump in
|
||||
* @param string $view_guid The guid of the target view
|
||||
* @param array $tables Tables configuration array.
|
||||
* @param string $view Target view name.
|
||||
* @param string $viewGuid Unique GUID for view (used in registry path).
|
||||
*
|
||||
* @return string|null The data found with the alias
|
||||
* @since 3.2.0
|
||||
* @return string|null SQL dump or null on failure.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function get(array $tables, string $view, string $view_guid): ?string
|
||||
public function get(array $tables, string $view, string $viewGuid): ?string
|
||||
{
|
||||
// first build a query statement to get all the data (insure it must be added - check the tweaking)
|
||||
if (ArrayHelper::check($tables)
|
||||
&& $this->registry-> // default is to add
|
||||
get('builder.sql_tweak.' . $view_guid . '.add', true))
|
||||
if (empty($tables) || !$this->shouldBuildDump($viewGuid))
|
||||
{
|
||||
$counter = 'a';
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new query object.
|
||||
$query = $this->db->getQuery(true);
|
||||
$query = $this->db->getQuery(true);
|
||||
$runQuery = false;
|
||||
$alias = 'a';
|
||||
$fieldsAdded = false;
|
||||
|
||||
// switch to only trigger the run of the query if we have tables to query
|
||||
$run_query = false;
|
||||
foreach ($tables as $table)
|
||||
foreach ($tables as $tableConfig)
|
||||
{
|
||||
if (empty($tableConfig['table']) || empty($tableConfig['sourcemap']))
|
||||
{
|
||||
if (isset($table['table']))
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldMappings = $this->parseFieldMappings($tableConfig['sourcemap'], $alias);
|
||||
|
||||
if ($alias === 'a')
|
||||
{
|
||||
if (!empty($fieldMappings['select']))
|
||||
{
|
||||
if ($counter === 'a')
|
||||
{
|
||||
// the main table fields
|
||||
if (strpos((string) $table['sourcemap'], PHP_EOL) !== false)
|
||||
{
|
||||
$fields = explode(PHP_EOL, (string) $table['sourcemap']);
|
||||
if (ArrayHelper::check($fields))
|
||||
{
|
||||
// reset array buckets
|
||||
$sourceArray = [];
|
||||
$targetArray = [];
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
if (strpos($field, "=>") !== false)
|
||||
{
|
||||
list($source, $target) = explode(
|
||||
"=>", $field
|
||||
);
|
||||
$sourceArray[] = $counter . '.' . trim(
|
||||
$source
|
||||
);
|
||||
$targetArray[] = trim($target);
|
||||
}
|
||||
}
|
||||
if (ArrayHelper::check(
|
||||
$sourceArray
|
||||
)
|
||||
&& ArrayHelper::check(
|
||||
$targetArray
|
||||
))
|
||||
{
|
||||
// add to query
|
||||
$query->select(
|
||||
$this->db->quoteName(
|
||||
$sourceArray, $targetArray
|
||||
)
|
||||
);
|
||||
$query->from(
|
||||
'#__' . $table['table'] . ' AS a'
|
||||
);
|
||||
$run_query = true;
|
||||
}
|
||||
// we may need to filter the selection
|
||||
if (($ids_ = $this->registry->
|
||||
get('builder.sql_tweak.' . $view_guid . '.where', null)) !== null)
|
||||
{
|
||||
// add to query the where filter
|
||||
$query->where(
|
||||
'a.id IN (' . $ids_ . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the other tables
|
||||
if (strpos((string) $table['sourcemap'], PHP_EOL) !== false)
|
||||
{
|
||||
$fields = explode(PHP_EOL, (string) $table['sourcemap']);
|
||||
if (ArrayHelper::check($fields))
|
||||
{
|
||||
// reset array buckets
|
||||
$sourceArray = [];
|
||||
$targetArray = [];
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
if (strpos($field, "=>") !== false)
|
||||
{
|
||||
list($source, $target) = explode(
|
||||
"=>", $field
|
||||
);
|
||||
$sourceArray[] = $counter . '.' . trim(
|
||||
$source
|
||||
);
|
||||
$targetArray[] = trim($target);
|
||||
}
|
||||
if (strpos($field, "==") !== false)
|
||||
{
|
||||
list($aKey, $bKey) = explode(
|
||||
"==", $field
|
||||
);
|
||||
// add to query
|
||||
$query->join(
|
||||
'LEFT', $this->db->quoteName(
|
||||
'#__' . $table['table'],
|
||||
$counter
|
||||
) . ' ON (' . $this->db->quoteName(
|
||||
'a.' . trim($aKey)
|
||||
) . ' = ' . $this->db->quoteName(
|
||||
$counter . '.' . trim($bKey)
|
||||
) . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
if (ArrayHelper::check(
|
||||
$sourceArray
|
||||
)
|
||||
&& ArrayHelper::check(
|
||||
$targetArray
|
||||
))
|
||||
{
|
||||
// add to query
|
||||
$query->select(
|
||||
$this->db->quoteName(
|
||||
$sourceArray, $targetArray
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$counter++;
|
||||
$query->select($this->db->quoteName($fieldMappings['select'], $fieldMappings['alias']));
|
||||
$query->from($this->db->quoteName('#__' . $tableConfig['table'], $alias));
|
||||
$this->applyWhereFilter($query, $viewGuid);
|
||||
$fieldsAdded = true;
|
||||
$runQuery = true;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->applyJoins($query, $tableConfig['table'], $alias, $fieldMappings['joins']);
|
||||
if (!empty($fieldMappings['select']))
|
||||
{
|
||||
// see where
|
||||
// var_dump($view, $view_guid);
|
||||
// exit;
|
||||
$query->select($this->db->quoteName($fieldMappings['select'], $fieldMappings['alias']));
|
||||
$fieldsAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check if we should run query
|
||||
if ($run_query)
|
||||
{
|
||||
try{
|
||||
// now get the data
|
||||
$this->db->setQuery($query);
|
||||
$this->db->execute();
|
||||
if ($this->db->getNumRows())
|
||||
{
|
||||
// get the data
|
||||
$data = $this->db->loadObjectList();
|
||||
$alias++;
|
||||
}
|
||||
|
||||
// start building the MySql dump
|
||||
$dump = "--";
|
||||
$dump .= PHP_EOL . "-- Dumping data for table `#__"
|
||||
. Placefix::_("component") . "_" . $view
|
||||
. "`";
|
||||
$dump .= PHP_EOL . "--";
|
||||
$dump .= PHP_EOL . PHP_EOL . "INSERT INTO `#__" . Placefix::_("component") . "_" . $view . "` (";
|
||||
foreach ($data as $line)
|
||||
{
|
||||
$comaSet = 0;
|
||||
foreach ($line as $fieldName => $fieldValue)
|
||||
{
|
||||
if ($comaSet == 0)
|
||||
{
|
||||
$dump .= $this->db->quoteName($fieldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
$dump .= ", " . $this->db->quoteName(
|
||||
$fieldName
|
||||
);
|
||||
}
|
||||
$comaSet++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$dump .= ") VALUES";
|
||||
$coma = 0;
|
||||
foreach ($data as $line)
|
||||
{
|
||||
if ($coma == 0)
|
||||
{
|
||||
$dump .= PHP_EOL . "(";
|
||||
}
|
||||
else
|
||||
{
|
||||
$dump .= "," . PHP_EOL . "(";
|
||||
}
|
||||
$comaSet = 0;
|
||||
foreach ($line as $fieldName => $fieldValue)
|
||||
{
|
||||
if ($comaSet == 0)
|
||||
{
|
||||
$dump .= $this->escape($fieldValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$dump .= ", " . $this->escape(
|
||||
$fieldValue
|
||||
);
|
||||
}
|
||||
$comaSet++;
|
||||
}
|
||||
$dump .= ")";
|
||||
$coma++;
|
||||
}
|
||||
$dump .= ";";
|
||||
if ($runQuery && $fieldsAdded)
|
||||
{
|
||||
try {
|
||||
$this->db->setQuery($query)->execute();
|
||||
|
||||
// return build dump query
|
||||
return $dump;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// see where
|
||||
// var_dump($view, $view_guid);
|
||||
// exit;
|
||||
if ($this->db->getNumRows())
|
||||
{
|
||||
$data = $this->db->loadObjectList();
|
||||
return $this->buildSqlDump($view, $data);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Log or handle exception if needed
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,35 +136,141 @@ class Sqldump
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape the values for a SQL dump
|
||||
* Determine if a dump should be built.
|
||||
*
|
||||
* @param string|array $value the value to escape
|
||||
* @param string $viewGuid
|
||||
*
|
||||
* @return string|array on success with escaped string
|
||||
* @since 3.2.0
|
||||
* @return bool
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function shouldBuildDump(string $viewGuid): bool
|
||||
{
|
||||
return (bool) $this->registry->get("builder.sql_tweak.{$viewGuid}.add", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply optional WHERE clause if set in registry.
|
||||
*
|
||||
* @param $query
|
||||
* @param string $viewGuid
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function applyWhereFilter($query, string $viewGuid): void
|
||||
{
|
||||
if ($ids = $this->registry->get("builder.sql_tweak.{$viewGuid}.where"))
|
||||
{
|
||||
$query->where("a.id IN ({$ids})");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse sourcemap lines into SELECT and JOIN definitions.
|
||||
*
|
||||
* @param string $map
|
||||
* @param string $alias
|
||||
*
|
||||
* @return array{select: string[], alias: string[], joins: array<int, array{from: string, to: string}>}
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function parseFieldMappings(string $map, string $alias): array
|
||||
{
|
||||
$lines = explode(PHP_EOL, trim($map));
|
||||
$select = [];
|
||||
$aliasFields = [];
|
||||
$joins = [];
|
||||
|
||||
foreach ($lines as $line)
|
||||
{
|
||||
$line = trim($line);
|
||||
|
||||
if (str_contains($line, '=>'))
|
||||
{
|
||||
[$from, $to] = array_map('trim', explode('=>', $line));
|
||||
$select[] = "{$alias}.{$from}";
|
||||
$aliasFields[] = $to;
|
||||
}
|
||||
elseif (str_contains($line, '=='))
|
||||
{
|
||||
[$left, $right] = array_map('trim', explode('==', $line));
|
||||
$joins[] = ['from' => $left, 'to' => $right];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'select' => $select,
|
||||
'alias' => $aliasFields,
|
||||
'joins' => $joins,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply JOINs to the query.
|
||||
*
|
||||
* @param $query
|
||||
* @param string $table
|
||||
* @param string $alias
|
||||
* @param array $joins
|
||||
*
|
||||
* @return void
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function applyJoins($query, string $table, string $alias, array $joins): void
|
||||
{
|
||||
foreach ($joins as $join)
|
||||
{
|
||||
$query->join(
|
||||
'LEFT',
|
||||
$this->db->quoteName("#__{$table}", $alias) . ' ON (' .
|
||||
$this->db->quoteName("a.{$join['from']}") . ' = ' .
|
||||
$this->db->quoteName("{$alias}.{$join['to']}") . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the SQL INSERT DUMP statement from data.
|
||||
*
|
||||
* @param string $view
|
||||
* @param array<object> $data
|
||||
*
|
||||
* @return string
|
||||
* @since 5.1.1
|
||||
*/
|
||||
protected function buildSqlDump(string $view, array $data): string
|
||||
{
|
||||
$tableName = "#__" . Placefix::_("component") . "_{$view}";
|
||||
$fields = array_keys((array) $data[0]);
|
||||
|
||||
$header = "--\n-- Dumping data for table `{$tableName}`\n--\n";
|
||||
$insert = "INSERT INTO `{$tableName}` (" . implode(', ', array_map([$this->db, 'quoteName'], $fields)) . ") VALUES\n";
|
||||
|
||||
$rows = array_map(function ($row)
|
||||
{
|
||||
$values = array_map([$this, 'escape'], (array) $row);
|
||||
return '(' . implode(', ', $values) . ')';
|
||||
}, $data);
|
||||
|
||||
return $header . $insert . implode(",\n", $rows) . ";";
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape SQL value for safe dump using strict quoting rules.
|
||||
*
|
||||
* @param mixed $value The value to escape.
|
||||
*
|
||||
* @return mixed Escaped SQL-safe literal or quoted string.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function escape($value)
|
||||
{
|
||||
// if array then return mapped
|
||||
if (ArrayHelper::check($value))
|
||||
if (is_array($value))
|
||||
{
|
||||
return array_map(__METHOD__, $value);
|
||||
return implode(', ', array_map([$this, 'escape'], $value));
|
||||
}
|
||||
|
||||
// if string make sure it is correctly escaped
|
||||
if (StringHelper::check($value) && !is_numeric($value))
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
// if empty value return place holder
|
||||
if (empty($value))
|
||||
{
|
||||
return "''";
|
||||
}
|
||||
|
||||
// if not array or string then return number
|
||||
return $value;
|
||||
return $this->quote($value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ namespace VDM\Joomla\Componentbuilder\Compiler\Model;
|
||||
use VDM\Joomla\Componentbuilder\Compiler\Registry;
|
||||
use VDM\Joomla\Utilities\JsonHelper;
|
||||
use VDM\Joomla\Utilities\ArrayHelper;
|
||||
use VDM\Joomla\Utilities\ObjectHelper;
|
||||
|
||||
|
||||
/**
|
||||
@ -63,117 +62,101 @@ class Sqltweaking
|
||||
if (ArrayHelper::check($item->sql_tweak))
|
||||
{
|
||||
// build the tweak settings
|
||||
$this->tweak(
|
||||
array_map(
|
||||
fn($array) => array_map(
|
||||
function ($value) {
|
||||
if (!ArrayHelper::check($value)
|
||||
&& !ObjectHelper::check(
|
||||
$value
|
||||
)
|
||||
&& strval($value) === strval(
|
||||
intval($value)
|
||||
))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, $array
|
||||
), array_values($item->sql_tweak)
|
||||
)
|
||||
);
|
||||
$this->tweak($item->sql_tweak);
|
||||
}
|
||||
|
||||
unset($item->sql_tweak);
|
||||
}
|
||||
|
||||
/**
|
||||
* To limit the SQL Demo data build in the views
|
||||
* Limit the SQL Demo data build in the views by applying tweak settings.
|
||||
*
|
||||
* @param array $settings Tweaking array.
|
||||
* @param array $settings The tweak configuration array.
|
||||
*
|
||||
* @return void
|
||||
* @since 3.2.0
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function tweak($settings)
|
||||
protected function tweak(array $settings): void
|
||||
{
|
||||
if (ArrayHelper::check($settings))
|
||||
if (!ArrayHelper::check($settings))
|
||||
{
|
||||
foreach ($settings as $setting)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($settings as $setting)
|
||||
{
|
||||
$adminView = $setting['adminview'] ?? null;
|
||||
|
||||
if (!$adminView)
|
||||
{
|
||||
// should sql dump be added
|
||||
if (1 == $setting['add_sql'])
|
||||
continue;
|
||||
}
|
||||
|
||||
$addSql = (int) ($setting['add_sql'] ?? 0);
|
||||
$addSqlOptions = (int) ($setting['add_sql_options'] ?? 0);
|
||||
|
||||
if ($addSql === 1 && $addSqlOptions === 2)
|
||||
{
|
||||
$ids = $setting['ids'] ?? '';
|
||||
$idArray = $this->normalizeIds($ids);
|
||||
|
||||
if (!empty($idArray))
|
||||
{
|
||||
// add sql (by option)
|
||||
if (2 == $setting['add_sql_options'])
|
||||
{
|
||||
// rest always
|
||||
$id_array = [];
|
||||
|
||||
// by id (first remove backups)
|
||||
$ids = $setting['ids'];
|
||||
|
||||
// now get the ids
|
||||
if (strpos((string) $ids, ',') !== false)
|
||||
{
|
||||
$id_array = (array) array_map(
|
||||
'trim', explode(',', (string) $ids)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$id_array[] = trim((string) $ids);
|
||||
}
|
||||
$id_array_new = [];
|
||||
|
||||
// check for ranges
|
||||
foreach ($id_array as $key => $id)
|
||||
{
|
||||
if (strpos($id, '=>') !== false)
|
||||
{
|
||||
$id_range = (array) array_map(
|
||||
'trim', explode('=>', $id)
|
||||
);
|
||||
unset($id_array[$key]);
|
||||
// build range
|
||||
if (count((array) $id_range) == 2)
|
||||
{
|
||||
$range = range(
|
||||
$id_range[0], $id_range[1]
|
||||
);
|
||||
$id_array_new = [...$id_array_new, ...$range];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ArrayHelper::check($id_array_new))
|
||||
{
|
||||
$id_array = [...$id_array_new, ...$id_array];
|
||||
}
|
||||
|
||||
// final fixing to array
|
||||
if (ArrayHelper::check($id_array))
|
||||
{
|
||||
// unique
|
||||
$id_array = array_unique($id_array, SORT_NUMERIC);
|
||||
// sort
|
||||
sort($id_array, SORT_NUMERIC);
|
||||
// now set it to global
|
||||
$this->registry->
|
||||
set('builder.sql_tweak.' . $setting['adminview'] . '.where', implode(',', $id_array));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// do not add sql dump options
|
||||
$this->registry->
|
||||
set('builder.sql_tweak.' . $setting['adminview'] . '.add', false);
|
||||
$this->registry->set(
|
||||
'builder.sql_tweak.' . $adminView . '.where',
|
||||
implode(',', $idArray)
|
||||
);
|
||||
}
|
||||
}
|
||||
elseif ($addSql === 0)
|
||||
{
|
||||
$this->registry->set(
|
||||
'builder.sql_tweak.' . $adminView . '.add',
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normalize a comma-separated string of IDs or ID ranges into a unique, sorted array.
|
||||
*
|
||||
* Supports individual IDs (e.g., "1,3,5") and ranges (e.g., "10 => 12").
|
||||
*
|
||||
* @param string $ids Raw ID string from settings.
|
||||
*
|
||||
* @return array<int> Normalized list of numeric IDs.
|
||||
* @since 5.1.1
|
||||
*/
|
||||
private function normalizeIds(string $ids): array
|
||||
{
|
||||
$rawIds = array_map('trim', explode(',', $ids));
|
||||
$finalIds = [];
|
||||
|
||||
foreach ($rawIds as $id)
|
||||
{
|
||||
if (strpos($id, '=>') !== false)
|
||||
{
|
||||
$rangeParts = array_map('trim', explode('=>', $id));
|
||||
|
||||
if (count($rangeParts) === 2 && is_numeric($rangeParts[0]) && is_numeric($rangeParts[1]))
|
||||
{
|
||||
$range = range((int) $rangeParts[0], (int) $rangeParts[1]);
|
||||
$finalIds = array_merge($finalIds, $range);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_numeric($id))
|
||||
{
|
||||
$finalIds[] = (int) $id;
|
||||
}
|
||||
}
|
||||
|
||||
$finalIds = array_unique($finalIds, SORT_NUMERIC);
|
||||
sort($finalIds, SORT_NUMERIC);
|
||||
|
||||
return $finalIds;
|
||||
}
|
||||
}
|
||||
|
||||
|
120
libraries/vendor_jcb/VDM.Joomla/src/Database/QuoteTrait.php
Normal file
120
libraries/vendor_jcb/VDM.Joomla/src/Database/QuoteTrait.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Joomla.Component.Builder
|
||||
*
|
||||
* @created 4th September, 2022
|
||||
* @author Llewellyn van der Merwe <https://dev.vdm.io>
|
||||
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
|
||||
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace VDM\Joomla\Database;
|
||||
|
||||
|
||||
/**
|
||||
* Database Quote Trait
|
||||
*
|
||||
* @since 5.1.1
|
||||
*/
|
||||
trait QuoteTrait
|
||||
{
|
||||
/**
|
||||
* Date format to return
|
||||
*
|
||||
* @var string
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected string $dateFormat = 'Y-m-d H:i:s';
|
||||
|
||||
/**
|
||||
* Safely quote a value for database use, preserving data integrity.
|
||||
*
|
||||
* - Native ints/floats passed as-is
|
||||
* - Clean integer strings are cast to int
|
||||
* - Clean float strings are cast to float
|
||||
* - Scientific notation is quoted to preserve original form
|
||||
* - Leading-zero integers are quoted
|
||||
* - Dates are formatted and quoted
|
||||
* - Booleans are converted to TRUE/FALSE
|
||||
* - Null is converted to NULL
|
||||
* - All else is quoted with Joomla's db quote
|
||||
*
|
||||
* @param mixed $value The value to quote.
|
||||
*
|
||||
* @return mixed
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected function quote($value)
|
||||
{
|
||||
// NULL handling
|
||||
if ($value === null)
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
// DateTime handling
|
||||
if ($value instanceof \DateTimeInterface)
|
||||
{
|
||||
return $this->db->quote($value->format($this->getDateFormat()));
|
||||
}
|
||||
|
||||
// Native numeric types
|
||||
if (is_int($value) || is_float($value))
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Stringified numeric values
|
||||
if (is_string($value) && is_numeric($value))
|
||||
{
|
||||
// Case 1: Leading-zero integers like "007"
|
||||
if ($value[0] === '0' && strlen($value) > 1 && ctype_digit($value))
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
// Case 2: Scientific notation - preserve exact format
|
||||
if (stripos($value, 'e') !== false)
|
||||
{
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
// Case 3: Decimal float string (not scientific)
|
||||
if (str_contains($value, '.'))
|
||||
{
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
// Case 4: Pure integer string
|
||||
if (ctype_digit($value))
|
||||
{
|
||||
return (int) $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Boolean handling
|
||||
if (is_bool($value))
|
||||
{
|
||||
return $value ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
|
||||
// Everything else
|
||||
return $this->db->quote($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date format used for SQL dumps.
|
||||
*
|
||||
* This format is used when quoting DateTimeInterface values
|
||||
* to ensure consistent formatting in INSERT statements.
|
||||
*
|
||||
* @return string The SQL-compatible date format.
|
||||
* @since 5.0.2
|
||||
*/
|
||||
protected function getDateFormat(): string
|
||||
{
|
||||
return $this->dateFormat;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user