Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 220 additions & 19 deletions src/Batch.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public function update(Model $table, array $values, string|null $index = null, b
{
$final = [];
$ids = [];
$timestampConditions = []; // Track conditions for timestamp updates

if (!count($values)) {
return false;
Expand All @@ -67,20 +68,23 @@ public function update(Model $table, array $values, string|null $index = null, b
}

$driver = $table->getConnection()->getDriverName();
$updatedAtColumn = null;
$timestampValue = null;

if ($table->usesTimestamps()) {
$updatedAtColumn = $table->getUpdatedAtColumn();
$timestampValue = Carbon::now()->format($table->getDateFormat());
}

foreach ($values as $key => $val) {
$ids[] = $val[$index];

if ($table->usesTimestamps()) {
$updatedAtColumn = $table->getUpdatedAtColumn();

if (!isset($val[$updatedAtColumn])) {
$val[$updatedAtColumn] = Carbon::now()->format($table->getDateFormat());
}
}
$hasChanges = false;
$changeConditions = [];

foreach (array_keys($val) as $field) {
if ($field !== $index) {
if ($field !== $index && $field !== $updatedAtColumn) {
$hasChanges = true;

// If increment / decrement
if (gettype($val[$field]) == 'array') {
// If array has two values
Expand All @@ -96,15 +100,33 @@ public function update(Model $table, array $values, string|null $index = null, b
throw new \TypeError('Second value in Increment/Decrement array needs to be numeric');
}
// Increment / decrement
// For increment/decrement, always consider it a change
if (Common::disableBacktick($driver)) {
$value = $field . $val[$field][0] . $val[$field][1];
$changeConditions[] = '(' . $field . ' IS NOT NULL)';
} else {
$value = '`' . $field . '`' . $val[$field][0] . $val[$field][1];
$changeConditions[] = '(`' . $field . '` IS NOT NULL)';
}
} else {
// Only update
$finalField = $raw ? Common::mysql_escape($val[$field]) : "'" . Common::mysql_escape($val[$field]) . "'";
$value = (is_null($val[$field]) ? 'NULL' : $finalField);

// Build condition to check if value actually changes
if (is_null($val[$field])) {
if (Common::disableBacktick($driver)) {
$changeConditions[] = '(' . $field . ' IS NOT NULL)';
} else {
$changeConditions[] = '(`' . $field . '` IS NOT NULL)';
}
} else {
if (Common::disableBacktick($driver)) {
$changeConditions[] = '(' . $field . ' != ' . $finalField . ')';
} else {
$changeConditions[] = '(`' . $field . '` != ' . $finalField . ')';
}
}
}

if (Common::disableBacktick($driver))
Expand All @@ -113,6 +135,34 @@ public function update(Model $table, array $values, string|null $index = null, b
$final[$field][] = 'WHEN `' . $index . '` = \'' . $val[$index] . '\' THEN ' . $value . ' ';
}
}

// Handle explicit updated_at in values
if (isset($val[$updatedAtColumn])) {
$timestampFieldValue = $raw ? Common::mysql_escape($val[$updatedAtColumn]) : "'" . Common::mysql_escape($val[$updatedAtColumn]) . "'";
$timestampFieldValue = (is_null($val[$updatedAtColumn]) ? 'NULL' : $timestampFieldValue);

if (Common::disableBacktick($driver))
$final[$updatedAtColumn][] = 'WHEN ' . $index . ' = \'' . $val[$index] . '\' THEN ' . $timestampFieldValue . ' ';
else
$final[$updatedAtColumn][] = 'WHEN `' . $index . '` = \'' . $val[$index] . '\' THEN ' . $timestampFieldValue . ' ';
} elseif ($hasChanges && $updatedAtColumn && count($changeConditions) > 0) {
// Only add timestamp update if there are actual field changes
$indexCondition = Common::disableBacktick($driver) ?
$index . ' = \'' . $val[$index] . '\'' :
'`' . $index . '` = \'' . $val[$index] . '\'';

$combinedCondition = $indexCondition . ' AND (' . implode(' OR ', $changeConditions) . ')';

if (Common::disableBacktick($driver))
$timestampConditions[] = 'WHEN ' . $combinedCondition . ' THEN \'' . $timestampValue . '\' ';
else
$timestampConditions[] = 'WHEN ' . $combinedCondition . ' THEN \'' . $timestampValue . '\' ';
}
}

// Add timestamp conditions to final array if we have any
if (!empty($timestampConditions) && $updatedAtColumn) {
$final[$updatedAtColumn] = $timestampConditions;
}

if (Common::disableBacktick($driver)) {
Expand Down Expand Up @@ -174,8 +224,9 @@ public function updateWithTwoIndex(Model $table, array $values, string|null $ind
{
$final = [];
$ids = [];
$driver = $table->getConnection()->getDriverName();

$ids2 = [];
$timestampConditions = []; // Track conditions for timestamp updates

if (!count($values)) {
return false;
}
Expand All @@ -184,13 +235,68 @@ public function updateWithTwoIndex(Model $table, array $values, string|null $ind
$index = $table->getKeyName();
}

$driver = $table->getConnection()->getDriverName();
$updatedAtColumn = null;
$timestampValue = null;

if ($table->usesTimestamps()) {
$updatedAtColumn = $table->getUpdatedAtColumn();
$timestampValue = Carbon::now()->format($table->getDateFormat());
}

foreach ($values as $key => $val) {
$ids[] = $val[$index];
$ids2[] = $val[$index2];
$hasChanges = false;
$changeConditions = [];

foreach (array_keys($val) as $field) {
if ($field !== $index || $field !== $index2) {
$finalField = $raw ? Common::mysql_escape($val[$field]) : "'" . Common::mysql_escape($val[$field]) . "'";
$value = (is_null($val[$field]) ? 'NULL' : $finalField);
if ($field !== $index && $field !== $index2 && $field !== $updatedAtColumn) {
$hasChanges = true;

// If increment / decrement
if (gettype($val[$field]) == 'array') {
// If array has two values
if (!array_key_exists(0, $val[$field]) || !array_key_exists(1, $val[$field])) {
throw new \ArgumentCountError('Increment/Decrement array needs to have 2 values, a math operator (+, -, *, /, %) and a number');
}
// Check first value
if (gettype($val[$field][0]) != 'string' || !in_array($val[$field][0], ['+', '-', '*', '/', '%'])) {
throw new \TypeError('First value in Increment/Decrement array needs to be a string and a math operator (+, -, *, /, %)');
}
// Check second value
if (!is_numeric($val[$field][1])) {
throw new \TypeError('Second value in Increment/Decrement array needs to be numeric');
}
// Increment / decrement
// For increment/decrement, always consider it a change
if (Common::disableBacktick($driver)) {
$value = $field . $val[$field][0] . $val[$field][1];
$changeConditions[] = '(' . $field . ' IS NOT NULL)';
} else {
$value = '`' . $field . '`' . $val[$field][0] . $val[$field][1];
$changeConditions[] = '(`' . $field . '` IS NOT NULL)';
}
} else {
// Only update
$finalField = $raw ? Common::mysql_escape($val[$field]) : "'" . Common::mysql_escape($val[$field]) . "'";
$value = (is_null($val[$field]) ? 'NULL' : $finalField);

// Build condition to check if value actually changes
if (is_null($val[$field])) {
if (Common::disableBacktick($driver)) {
$changeConditions[] = '(' . $field . ' IS NOT NULL)';
} else {
$changeConditions[] = '(`' . $field . '` IS NOT NULL)';
}
} else {
if (Common::disableBacktick($driver)) {
$changeConditions[] = '(' . $field . ' != ' . $finalField . ')';
} else {
$changeConditions[] = '(`' . $field . '` != ' . $finalField . ')';
}
}
}

if (Common::disableBacktick($driver)) {
$final[$field][] = 'WHEN (' . $index . ' = \'' . Common::mysql_escape($val[$index]) . '\' AND ' . $index2 . ' = \'' . $val[$index2] . '\') THEN ' . $value . ' ';
Expand All @@ -199,6 +305,35 @@ public function updateWithTwoIndex(Model $table, array $values, string|null $ind
}
}
}

// Handle explicit updated_at in values
if (isset($val[$updatedAtColumn])) {
$timestampFieldValue = $raw ? Common::mysql_escape($val[$updatedAtColumn]) : "'" . Common::mysql_escape($val[$updatedAtColumn]) . "'";
$timestampFieldValue = (is_null($val[$updatedAtColumn]) ? 'NULL' : $timestampFieldValue);

if (Common::disableBacktick($driver)) {
$final[$updatedAtColumn][] = 'WHEN (' . $index . ' = \'' . Common::mysql_escape($val[$index]) . '\' AND ' . $index2 . ' = \'' . $val[$index2] . '\') THEN ' . $timestampFieldValue . ' ';
} else {
$final[$updatedAtColumn][] = 'WHEN (`' . $index . '` = "' . Common::mysql_escape($val[$index]) . '" AND `' . $index2 . '` = "' . $val[$index2] . '") THEN ' . $timestampFieldValue . ' ';
}
} elseif ($hasChanges && $updatedAtColumn && count($changeConditions) > 0) {
// Only add timestamp update if there are actual field changes
$indexCondition = Common::disableBacktick($driver) ?
'(' . $index . ' = \'' . Common::mysql_escape($val[$index]) . '\' AND ' . $index2 . ' = \'' . $val[$index2] . '\')' :
'(`' . $index . '` = "' . Common::mysql_escape($val[$index]) . '" AND `' . $index2 . '` = "' . $val[$index2] . '")';

$combinedCondition = $indexCondition . ' AND (' . implode(' OR ', $changeConditions) . ')';

if (Common::disableBacktick($driver))
$timestampConditions[] = 'WHEN ' . $combinedCondition . ' THEN \'' . $timestampValue . '\' ';
else
$timestampConditions[] = 'WHEN ' . $combinedCondition . ' THEN \'' . $timestampValue . '\' ';
}
}

// Add timestamp conditions to final array if we have any
if (!empty($timestampConditions) && $updatedAtColumn) {
$final[$updatedAtColumn] = $timestampConditions;
}


Expand Down Expand Up @@ -296,8 +431,18 @@ public function updateMultipleCondition(Model $table, array $arrays, string|null
}
}

$updatedAtColumn = null;
$timestampValue = null;

if ($timestamp) {
$updatedAtColumn = $table->getUpdatedAtColumn();
$timestampValue = Carbon::now()->format($table->getDateFormat());
}

$arraysNew = [];
$timestampConditions = []; // Track conditions for timestamp updates
$keys = array_keys($columns);

foreach ($keys as $key) {
$arraysMixed = collect($arrays)->filter(function ($rows) use ($key) {
return in_array($key, array_keys($rows['columns']));
Expand All @@ -308,15 +453,63 @@ public function updateMultipleCondition(Model $table, array $arrays, string|null
$arraysNew[$key][] = [
'conditions' => $item['conditions'],
'value' => is_null($item['columns'][$key]) ? "NULL" : $value,
'originalValue' => $item['columns'][$key], // Store original value for change detection
];
}
}

if ($timestamp) {
$arraysNew['updated_at'][] = [
'conditions' => $item['conditions'],
'value' => "'".(now())."'",
// Handle timestamp updates with change detection
if ($timestamp && $updatedAtColumn) {
foreach ($arrays as $item) {
$hasChanges = false;
$changeConditions = [];

// Check if this item has any actual field changes
foreach ($item['columns'] as $fieldName => $newValue) {
if ($fieldName !== $updatedAtColumn) {
$hasChanges = true;

// Build condition to check if value actually changes
if (is_null($newValue)) {
$changeConditions[] = " {$backtick}{$fieldName}{$backtick} IS NOT NULL ";
} else {
$escapedValue = $raw ? Common::mysql_escape($newValue) : "'" . Common::mysql_escape($newValue) . "'";
$changeConditions[] = " {$backtick}{$fieldName}{$backtick} != {$escapedValue} ";
}
}
}

// Handle explicit updated_at in columns
if (isset($item['columns'][$updatedAtColumn])) {
$timestampFieldValue = $raw ? Common::mysql_escape($item['columns'][$updatedAtColumn]) : "'" . Common::mysql_escape($item['columns'][$updatedAtColumn]) . "'";
$timestampFieldValue = (is_null($item['columns'][$updatedAtColumn]) ? 'NULL' : $timestampFieldValue);

$arraysNew[$updatedAtColumn][] = [
'conditions' => $item['conditions'],
'value' => $timestampFieldValue,
'originalValue' => $item['columns'][$updatedAtColumn],
];
} elseif ($hasChanges && count($changeConditions) > 0) {
// Only add timestamp update if there are actual field changes
$conditionsWithChanges = $item['conditions'];
$timestampConditions[] = [
'conditions' => $conditionsWithChanges,
'value' => "'" . $timestampValue . "'",
'changeConditions' => $changeConditions, // Store change conditions for SQL generation
];
}
}

// Add timestamp conditions with change detection to arraysNew
if (!empty($timestampConditions)) {
if (!isset($arraysNew[$updatedAtColumn])) {
$arraysNew[$updatedAtColumn] = [];
}

foreach ($timestampConditions as $timestampCondition) {
$arraysNew[$updatedAtColumn][] = $timestampCondition;
}
}
}

$cases = [];
Expand All @@ -332,7 +525,15 @@ public function updateMultipleCondition(Model $table, array $arrays, string|null
}

$conditionContext = join(' and ', $conditionContext);
$caseSql .= " WHEN $conditionContext THEN {$value} ";

// Handle timestamp update with change conditions
if ($key === $updatedAtColumn && isset($item['changeConditions']) && !empty($item['changeConditions'])) {
// Add change detection conditions to only update timestamp when fields actually change
$changeConditionsSql = implode(' OR ', $item['changeConditions']);
$caseSql .= " WHEN ($conditionContext) AND ($changeConditionsSql) THEN {$value} ";
} else {
$caseSql .= " WHEN $conditionContext THEN {$value} ";
}
}
$caseSql .= " ELSE {$backtick}{$key}{$backtick} END)";
$cases[] = $caseSql;
Expand Down