-
Notifications
You must be signed in to change notification settings - Fork 2
/
Connection.php
149 lines (126 loc) · 3.95 KB
/
Connection.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
namespace Ijanki\DBAL;
class Connection extends \Doctrine\DBAL\Connection
{
/**
* Insert using mysql ON DUPLICATE KEY UPDATE.
* @link http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html
*
* INSPIRED BY https://github.com/yadakhov/insert-on-duplicate-key
*
* Example: $data = [
* ['id' => 1, 'name' => 'John'],
* ['id' => 2, 'name' => 'Mike'],
* ];
*
* @param string $tableExpression The expression of the table to insert data into, quoted or unquoted.
* @param array $data is an array of array.
* @param array $updateColumns NULL or [] means update all columns
*
* @return int 0 if row is not changed, 1 if row is inserted, 2 if row is updated
*/
public function insertOnDuplicateKey($tableExpression, array $data, array $updateColumns = null)
{
if (empty($data)) {
return $this->executeUpdate('INSERT INTO ' . $tableExpression . ' ()' . ' VALUES ()');
}
// Case where $data is not an array of arrays.
if (!isset($data[0])) {
$data = [$data];
}
$sql = $this->buildInsertOnDuplicateSql($tableExpression, $data, $updateColumns);
$stmt = $this->prepare($sql);
$data = $this->inLineArray($data);
return $stmt->execute($data);
}
/**
* Inline a multiple dimensions array.
*
* @param $data
*
* @return array
*/
protected static function inLineArray(array $data)
{
return call_user_func_array('array_merge', array_map('array_values', $data));
}
private function buildInsertOnDuplicateSql($tableExpression, array $data, array $updateColumns = null)
{
$first = $this->getFirstRow($data);
$columns = '`' . implode('`,`', $this->getColumns($first)) . '`';
$sql = 'INSERT INTO `' . $tableExpression . '`(' . $columns . ') VALUES ';
$sql .= $this->buildQuestionMarks($data);
$sql .= ' ON DUPLICATE KEY UPDATE ';
if (empty($updateColumns)) {
$sql .= $this->buildValuesList(array_keys($first));
} else {
$sql .= $this->buildValuesList($updateColumns);
}
return $sql;
}
public function getColumns($row)
{
$columns = array_keys($row);
return array_map(function($v) {
return str_replace("`", "``", $v);
}, $columns);
}
/**
* Get the first row of the $data array.
*
* @param array $data
*
* @return mixed
*/
protected function getFirstRow(array $data)
{
if (empty($data)) {
throw new \InvalidArgumentException('Empty data.');
}
list($first) = $data;
if (!is_array($first)) {
throw new \InvalidArgumentException('$data is not an array of array.');
}
return $first;
}
/**
* Build the question mark placeholder. Helper function for insertOnDuplicateKeyUpdate().
* Helper function for insertOnDuplicateKeyUpdate().
*
* @param $data
*
* @return string
*/
protected function buildQuestionMarks($data)
{
$lines = [];
foreach ($data as $row) {
$count = count($row);
$questions = [];
for ($i = 0; $i < $count; ++$i) {
$questions[] = '?';
}
$lines[] = '(' . implode(',', $questions) . ')';
}
return implode(', ', $lines);
}
/**
* Build a value list.
*
* @param array $updatedColumns
*
* @return string
*/
protected static function buildValuesList(array $updatedColumns)
{
$out = [];
foreach ($updatedColumns as $key => $value) {
if (is_numeric($key)) {
$out[] = sprintf('`%s` = VALUES(`%s`)', $value, $value);
} else {
$out[] = sprintf('%s = %s', $key, $value);
}
}
return implode(', ', $out);
}
}