--- /dev/null
+<?php
+
+/**
+ * Dear God Why Do I Have To Write This (Dumb SQL Builder)
+ *
+ * Usage:
+ * $insert = CRM_Utils_SQL_Insert::into('mytable')
+ * ->row(array('col1' => '1', 'col2' => '2' ))
+ * ->row(array('col1' => '2b', 'col2' => '1b'));
+ * echo $insert->toSQL();
+ *
+ * Note: In MySQL, numeric values may be escaped. Except for NULL values,
+ * it's reasonable for us to simply escape all values by default -- without
+ * any knowledge of the underlying schema.
+ *
+ * Design principles:
+ * - Portable
+ * - No knowledge of the underlying SQL API (except for escaping -- CRM_Core_DAO::escapeString)
+ * - No knowledge of the underlying data model
+ * - Single file
+ * - SQL clauses correspond to PHP functions ($select->where("foo_id=123"))
+ */
+class CRM_Utils_SQL_Insert {
+
+ /**
+ * @var string
+ */
+ private $table;
+
+ /**
+ * @var array
+ */
+ private $rows;
+
+ /**
+ * array<string> list of column names
+ */
+ private $columns;
+
+ /**
+ * Create a new INSERT query
+ *
+ * @param string $table table-name and optional alias
+ * @return CRM_Utils_SQL_Insert
+ */
+ public static function into($table) {
+ return new self($table);
+ }
+
+ /**
+ * Create a new SELECT query
+ *
+ * @param string $from table-name and optional alias
+ */
+ public function __construct($table) {
+ $this->table = $table;
+ $this->rows = array();
+ }
+
+ /**
+ * @param array $rows
+ * @return CRM_Utils_SQL_Insert
+ */
+ public function rows($rows) {
+ foreach ($rows as $row) {
+ $this->row($row);
+ }
+ return $this;
+ }
+
+ /**
+ * @param array $row
+ * @return CRM_Utils_SQL_Insert
+ * @throws CRM_Core_Exception
+ */
+ public function row($row) {
+ $columns = array_keys($row);
+ sort($columns);
+
+ if ($this->columns === NULL) {
+ $this->columns = $columns;
+ }
+ elseif ($this->columns != $columns) {
+ throw new CRM_Core_Exception("Inconsistent column names");
+ }
+
+ $escapedRow = array();
+ foreach ($columns as $column) {
+ $escapedRow[$column] = $this->escapeString($row[$column]);
+ }
+ $this->rows[] = $escapedRow;
+
+ return $this;
+ }
+
+ /**
+ * @param string|NULL $value
+ * @return string SQL expression, e.g. "it\'s great" (with-quotes) or NULL (without-quotes)
+ */
+ protected function escapeString($value) {
+ return $value === NULL ? 'NULL' : '"' . CRM_Core_DAO::escapeString($value) . '"';
+ }
+
+ /**
+ * @return string SQL statement
+ */
+ public function toSQL() {
+ $columns = "`" . implode('`,`', $this->columns) . "`";
+ $sql = "INSERT INTO {$this->table} ({$columns}) VALUES";
+
+ $nextDelim = '';
+ foreach ($this->rows as $row) {
+ $sql .= "{$nextDelim}\n(" . implode(',', $row) . ")";
+ $nextDelim = ',';
+ }
+ $sql .= "\n";
+
+ return $sql;
+ }
+}
--- /dev/null
+<?php
+ require_once 'CiviTest/CiviUnitTestCase.php';
+
+ /**
+ * Class CRM_Utils_SQL_SelectTest
+ */
+class CRM_Utils_SQL_InsertTest extends CiviUnitTestCase {
+ function testRow_twice() {
+ $insert = CRM_Utils_SQL_Insert::into('foo')
+ ->row(array('first' => '1', 'second' => '2' ))
+ ->row(array('second' => '2b', 'first' => '1b'))
+ ;
+ $expected = '
+ INSERT INTO foo (`first`,`second`) VALUES
+ ("1","2"),
+ ("1b","2b")
+ ';
+ $this->assertLike($expected, $insert->toSQL());
+ }
+
+ function testRows() {
+ $insert = CRM_Utils_SQL_Insert::into('foo')
+ ->row(array('first' => '1', 'second' => '2' ))
+ ->rows(array(
+ array('second' => '2b', 'first' => '1b'),
+ array('first' => '1c', 'second' => '2c')
+ ))
+ ->row(array('second' => '2d', 'first' => '1d'))
+ ;
+ $expected = '
+ INSERT INTO foo (`first`,`second`) VALUES
+ ("1","2"),
+ ("1b","2b"),
+ ("1c","2c"),
+ ("1d","2d")
+ ';
+ $this->assertLike($expected, $insert->toSQL());
+ }
+
+ /**
+ * @param $expected
+ * @param $actual
+ * @param string $message
+ */
+ function assertLike($expected, $actual, $message = '') {
+ $expected = trim((preg_replace('/[ \r\n\t]+/', ' ', $expected)));
+ $actual = trim((preg_replace('/[ \r\n\t]+/', ' ', $actual)));
+ $this->assertEquals($expected, $actual, $message);
+ }
+}
\ No newline at end of file