/** * Performs a batch of changes wrapped in a database transaction. * The batch `$task` can be an array of items to insert at once, * or a closure function that executes a series of tasks and * performs whatever logic necessary. If any insert fails, or if * the function returns false, the transaction will be rolled * back, otherwise it will be committed. For databases that support * it, records will be inserted using a single SQL insert statement * for greater efficiency. */ public static function batch ($tasks) { DB::execute ('begin'); if ($tasks instanceof Closure) { if ($tasks () === false) { self::$batch_error = DB::error (); DB::execute ('rollback'); return false; } } elseif (is_array ($tasks)) { // Check the driver type, because SQLite doesn't support // multiple row inserts $db = DB::get_connection (1); if (! $db) { self::$batch_error = 'No database connection'; return false; } if ($db->getAttribute (PDO::ATTR_DRIVER_NAME) === 'sqlite') { $class = get_called_class (); foreach ($tasks as $task) { $o = new $class ($task); if (! $o->put ()) { self::$batch_error = $o->error; DB::execute ('rollback'); return false; } } return DB::execute ('commit'); } // Build the multi-row insert statement $class = get_called_class (); $o = new $class; $sql = 'insert into `' . $o->table . '` ('; $data = array (); // Figure out how many placeholders are needed per record $ins = array (); $len = count ($tasks[0]); for ($i = 0; $i < $len; $i++) { $ins[] = '?'; } // Add fields to statement $sql .= join (', ', Model::backticks (array_keys ($tasks[0]))) . ') values '; $sep = ''; // Add each record to the statement foreach ($tasks as $task) { $data = array_merge ($data, array_values ($task)); $sql .= $sep . '(' . join (', ', $ins) . ')'; $sep = ', '; } if (! DB::execute ($sql, $data)) { self::$batch_error = DB::error (); DB::execute ('rollback'); return false; } } return DB::execute ('commit'); }