diff --git a/src/InsertOnDuplicateKey.php b/src/InsertOnDuplicateKey.php index c976018..c7e126a 100644 --- a/src/InsertOnDuplicateKey.php +++ b/src/InsertOnDuplicateKey.php @@ -15,10 +15,11 @@ trait InsertOnDuplicateKey * * @param array $data is an array of array. * @param array $updateColumns NULL or [] means update all columns + * @param array $dontEscapeColumns [] esapces all columns, or keys of which columns not to bind * * @return int 0 if row is not changed, 1 if row is inserted, 2 if row is updated */ - public static function insertOnDuplicateKey(array $data, array $updateColumns = null) + public static function insertOnDuplicateKey(array $data, array $updateColumns = null, array $dontEscapeColumns = []) { if (empty($data)) { return false; @@ -29,9 +30,9 @@ public static function insertOnDuplicateKey(array $data, array $updateColumns = $data = [$data]; } - $sql = static::buildInsertOnDuplicateSql($data, $updateColumns); + $sql = static::buildInsertOnDuplicateSql($data, $updateColumns, $dontEscapeColumns); - $data = static::inLineArray($data); + $data = static::inLineArray($data, $dontEscapeColumns); return self::getModelConnectionName()->affectingStatement($sql, $data); } @@ -99,10 +100,10 @@ public static function getTableName() } /** - * Static function for getting connection name - * - * @return string - */ + * Static function for getting connection name + * + * @return string + */ public static function getModelConnectionName() { $class = get_called_class(); @@ -140,14 +141,17 @@ public static function getPrimaryKey() * * @return string */ - protected static function buildQuestionMarks($data) + protected static function buildQuestionMarks($data, $dontEscapeColumns = []) { $lines = []; foreach ($data as $row) { - $count = count($row); $questions = []; - for ($i = 0; $i < $count; ++$i) { - $questions[] = '?'; + foreach ($row as $key => $value) { + if (in_array($key, $dontEscapeColumns)) { + $questions[] = $value; + } else { + $questions[] = '?'; + } } $lines[] = '(' . implode(',', $questions) . ')'; } @@ -215,6 +219,21 @@ protected static function buildValuesList(array $updatedColumns) return implode(', ', $out); } + protected static function buildValuesListSqlite(array $updatedColumns) + { + $out = []; + + foreach ($updatedColumns as $key => $value) { + if (is_numeric($key)) { + $out[] = sprintf('`%s` = excluded.%s', $value, $value); + } else { + $out[] = sprintf('%s = excluded.%s', $key, $value); + } + } + + return implode(', ', $out); + } + /** * Inline a multiple dimensions array. * @@ -222,9 +241,20 @@ protected static function buildValuesList(array $updatedColumns) * * @return array */ - protected static function inLineArray(array $data) + protected static function inLineArray(array $data, array $dontEscapeColumns = []) { - return call_user_func_array('array_merge', array_map('array_values', $data)); + $dataBindings = []; + foreach ($data as $row) { + foreach ($row as $key => $value) { + if (in_array($key, $dontEscapeColumns)) { + continue; + } else { + $dataBindings[] = $value; + } + } + } + + return $dataBindings; } /** @@ -232,15 +262,35 @@ protected static function inLineArray(array $data) * * @param array $data * @param array $updateColumns + * @param array $dontEscapeColumns * * @return string */ - protected static function buildInsertOnDuplicateSql(array $data, array $updateColumns = null) + protected static function buildInsertOnDuplicateSql(array $data, array $updateColumns = null, array $dontEscapeColumns = []) { $first = static::getFirstRow($data); $sql = 'INSERT INTO `' . static::getTablePrefix() . static::getTableName() . '`(' . static::getColumnList($first) . ') VALUES' . PHP_EOL; - $sql .= static::buildQuestionMarks($data) . PHP_EOL; + $sql .= static::buildQuestionMarks($data, $dontEscapeColumns) . PHP_EOL; + + $connection = config('database.default'); + + if ($connection === 'sqlite') { + $sql .= 'ON CONFLICT(ptn) DO UPDATE SET'; + + if (empty($updateColumns)) { + $sql .= static::buildValuesListSqlite(array_keys($first)); + } else { + $sql .= static::buildValuesListSqlite($updateColumns); + } + + return $sql; + } + + if ($connection !== 'mysql') { + return $sql; + } + $sql .= 'ON DUPLICATE KEY UPDATE '; if (empty($updateColumns)) { diff --git a/tests/MainTest.php b/tests/MainTest.php index 9b3d6bd..f8f5326 100644 --- a/tests/MainTest.php +++ b/tests/MainTest.php @@ -144,6 +144,21 @@ public function testBuildInsertOnDuplicateSqlSimple() $this->assertEquals($expected, $result); } + public function testBuildInsertOnDuplicateSqlWithExclusionSimple() + { + $data = [ + ['id' => 1, 'email' => 'user1@email.com', 'name' => 'User One'] + ]; + + $expected = 'INSERT INTO `prefix_test_user_table`(`id`,`email`,`name`) VALUES +(1,?,?) +ON DUPLICATE KEY UPDATE `id` = VALUES(`id`), `email` = VALUES(`email`), `name` = VALUES(`name`)'; + + $result = $this->invokeMethod($this->user, 'buildInsertOnDuplicateSql', [$data,null,['id']]); + + $this->assertEquals($expected, $result); + } + public function testBuildInsertOnDuplicateSqlMultiple() { $data = $this->getDataForInsert(); @@ -157,6 +172,19 @@ public function testBuildInsertOnDuplicateSqlMultiple() $this->assertEquals($expected, $result); } + public function testBuildInsertOnDuplicateSqlWithExclusionMultiple() + { + $data = $this->getDataForInsert(); + + $expected = 'INSERT INTO `prefix_test_user_table`(`id`,`email`,`name`) VALUES +(1,?,?), (2,?,?), (3,?,?) +ON DUPLICATE KEY UPDATE `id` = VALUES(`id`), `email` = VALUES(`email`), `name` = VALUES(`name`)'; + + $result = $this->invokeMethod($this->user, 'buildInsertOnDuplicateSql', [$data,null,['id']]); + + $this->assertEquals($expected, $result); + } + public function testBuildInsertOnDuplicateSqlMultipleWithUpdateColumn() { $data = $this->getDataForInsert();