Skip to content
Open
Show file tree
Hide file tree
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
80 changes: 65 additions & 15 deletions src/InsertOnDuplicateKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) . ')';
}
Expand Down Expand Up @@ -215,32 +219,78 @@ 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.
*
* @param $data
*
* @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;
}

/**
* Build the INSERT ON DUPLICATE KEY sql statement.
*
* @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)) {
Expand Down
28 changes: 28 additions & 0 deletions tests/MainTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,21 @@ public function testBuildInsertOnDuplicateSqlSimple()
$this->assertEquals($expected, $result);
}

public function testBuildInsertOnDuplicateSqlWithExclusionSimple()
{
$data = [
['id' => 1, 'email' => '[email protected]', '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();
Expand All @@ -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();
Expand Down