diff --git a/src/Db.php b/src/Db.php index 790f5c9..a9e6f0a 100644 --- a/src/Db.php +++ b/src/Db.php @@ -1,11 +1,11 @@ - * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) - * @license http://www.popphp.org/license New BSD License + * @author Nick Sagona, III + * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. + * @license https://www.popphp.org/license New BSD License */ /** @@ -18,9 +18,9 @@ * * @category Pop * @package Pop\Db - * @author Nick Sagona, III - * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) - * @license http://www.popphp.org/license New BSD License + * @author Nick Sagona, III + * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. + * @license https://www.popphp.org/license New BSD License * @version 6.5.0 */ class Db @@ -150,6 +150,7 @@ public static function check(string $adapter, array $options, string $prefix = ' } error_reporting((int)$error); + return $result; } @@ -161,10 +162,14 @@ public static function check(string $adapter, array $options, string $prefix = ' * @param array $options * @param string $prefix * @throws Exception - * @return void + * @return int */ - public static function executeSql(string $sql, mixed $adapter, array $options = [], string $prefix = '\Pop\Db\Adapter\\'): void + public static function executeSql( + string $sql, mixed $adapter, array $options = [], string $prefix = '\Pop\Db\Adapter\\' + ): int { + $affectedRows = 0; + if (is_string($adapter)) { $adapter = ucfirst(strtolower($adapter)); $class = $prefix . $adapter; @@ -242,10 +247,13 @@ public static function executeSql(string $sql, mixed $adapter, array $options = foreach ($statements as $statement) { if (!empty($statement)) { $db->query($statement); + $affectedRows += $db->getNumberOfAffectedRows(); } } } } + + return $affectedRows; } /** @@ -256,15 +264,17 @@ public static function executeSql(string $sql, mixed $adapter, array $options = * @param array $options * @param string $prefix * @throws Exception - * @return void + * @return int */ - public static function executeSqlFile(string $sqlFile, mixed $adapter, array $options = [], string $prefix = '\Pop\Db\Adapter\\'): void + public static function executeSqlFile( + string $sqlFile, mixed $adapter, array $options = [], string $prefix = '\Pop\Db\Adapter\\' + ): int { if (!file_exists($sqlFile)) { throw new Exception("Error: The SQL file '" . $sqlFile . "' does not exist."); } - self::executeSql(file_get_contents($sqlFile), $adapter, $options, $prefix); + return self::executeSql(file_get_contents($sqlFile), $adapter, $options, $prefix); } /** diff --git a/src/Sql/Data.php b/src/Sql/Data.php index 043b9f0..f5463f0 100644 --- a/src/Sql/Data.php +++ b/src/Sql/Data.php @@ -1,11 +1,11 @@ - * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) - * @license http://www.popphp.org/license New BSD License + * @author Nick Sagona, III + * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. + * @license https://www.popphp.org/license New BSD License */ /** @@ -20,9 +20,9 @@ * * @category Pop * @package Pop\Db - * @author Nick Sagona, III - * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) - * @license http://www.popphp.org/license New BSD License + * @author Nick Sagona, III + * @copyright Copyright (c) 2009-2025 NOLA Interactive, LLC. + * @license https://www.popphp.org/license New BSD License * @version 6.5.0 */ class Data extends AbstractSql @@ -52,6 +52,12 @@ class Data extends AbstractSql */ protected array $conflictColumns = []; + /** + * Force UPDATE instead of UPSERT if conflict keys/columns are provided + * @var bool + */ + protected bool $forceUpdate = false; + /** * SQL string * @var ?string @@ -96,6 +102,30 @@ public function getDivide(): int return $this->divide; } + /** + * Set force update + * + * @param bool $forceUpdate + * @param string $conflictKey + * @return Data + */ + public function setForceUpdate(bool $forceUpdate = true, string $conflictKey = 'id'): Data + { + $this->forceUpdate = $forceUpdate; + $this->conflictKey = $conflictKey; + return $this; + } + + /** + * Is force update + * + * @return bool + */ + public function isForceUpdate(): bool + { + return $this->forceUpdate; + } + /** * Set the database table * @@ -191,45 +221,76 @@ public function serialize(array $data, mixed $omit = null, bool $nullEmpty = fal } } - $columns = array_map([$this, 'quoteId'], $columns); - $insert = "INSERT INTO " . $table . " (" . implode(', ', $columns) . ") VALUES" . PHP_EOL; - $onUpdate = $this->formatConflicts(); - - foreach ($data as $i => $row) { - if (!empty($omit)) { - foreach ($omit as $o) { - if (isset($row[$o])) { - unset($row[$o]); + // Force UPDATE SQL + if (($this->forceUpdate) && !empty($this->conflictKey)) { + foreach ($data as $i => $row) { + $primaryValue = $row[$this->conflictKey]; + unset($row[$this->conflictKey]); + + $update = "UPDATE " . $table . " SET "; + if (!empty($omit)) { + foreach ($omit as $o) { + if (isset($row[$o])) { + unset($row[$o]); + } } } - } - $value = "(" . implode(', ', array_map(function($value) use ($forceQuote) { - return $this->quote($value, $forceQuote); - }, $row)) . ")"; - if ($nullEmpty) { - $value = str_replace(["('',", " '', ", ", '')"], ["(NULL,", ' NULL, ', ', NULL)'], $value); - } - switch ($this->divide) { - case 0: - if ($i == 0) { - $this->sql .= $insert; + $values = []; + foreach ($row as $key => $value) { + $values[] = $this->quoteId($key) . ' = ' . $this->quote($value, $forceQuote); + } + $values = implode(', ', $values); + + if ($nullEmpty) { + $values = str_replace(["('',", " '', ", ", '')"], ["(NULL,", ' NULL, ', ', NULL)'], $values); + } + + $update .= $values . " WHERE " . $this->quoteId($this->conflictKey) . ' = ' . $primaryValue . ';'; + $this->sql .= $update . PHP_EOL; + } + // Else, INSERT/UPSERT SQL + } else { + $columns = array_map([$this, 'quoteId'], $columns); + $insert = "INSERT INTO " . $table . " (" . implode(', ', $columns) . ") VALUES" . PHP_EOL; + $onUpdate = $this->formatConflicts(); + + foreach ($data as $i => $row) { + if (!empty($omit)) { + foreach ($omit as $o) { + if (isset($row[$o])) { + unset($row[$o]); + } } - $this->sql .= $value; - $this->sql .= ($i == (count($data) - 1)) ? $onUpdate . ';' : ','; - $this->sql .= PHP_EOL; - break; - case 1: - $this->sql .= $insert . $value . $onUpdate . ';' . PHP_EOL; - break; - default: - if (($i % $this->divide) == 0) { - $this->sql .= $insert . $value . (($i == (count($data) - 1)) ? $onUpdate . ';' : ',') . PHP_EOL; - } else { + } + $value = "(" . implode(', ', array_map(function($value) use ($forceQuote) { + return $this->quote($value, $forceQuote); + }, $row)) . ")"; + if ($nullEmpty) { + $value = str_replace(["('',", " '', ", ", '')"], ["(NULL,", ' NULL, ', ', NULL)'], $value); + } + + switch ($this->divide) { + case 0: + if ($i == 0) { + $this->sql .= $insert; + } $this->sql .= $value; - $this->sql .= (((($i + 1) % $this->divide) == 0) || ($i == (count($data) - 1))) ? $onUpdate . ';' : ','; + $this->sql .= ($i == (count($data) - 1)) ? $onUpdate . ';' : ','; $this->sql .= PHP_EOL; - } + break; + case 1: + $this->sql .= $insert . $value . $onUpdate . ';' . PHP_EOL; + break; + default: + if (($i % $this->divide) == 0) { + $this->sql .= $insert . $value . (($i == (count($data) - 1)) ? $onUpdate . ';' : ',') . PHP_EOL; + } else { + $this->sql .= $value; + $this->sql .= (((($i + 1) % $this->divide) == 0) || ($i == (count($data) - 1))) ? $onUpdate . ';' : ','; + $this->sql .= PHP_EOL; + } + } } } @@ -379,4 +440,4 @@ protected function formatConflicts(): string return $onUpdate; } -} \ No newline at end of file +}