/** * Insert a new record in the db. * * Calls preInsert() and postInsert(), if they are defined in the child * class. */ public function insert() { $this->preInsert(); $dbh = DB::getInstance(); // build the list of things we actually care about inserting - those differing // from defaults, specifically: $insert_values = []; foreach ($this->_record as $insert_key => $insert_val) { if ($insert_val !== static::$_defaults[$insert_key]) { $insert_values[$insert_key] = $insert_val; } } if (!isset($insert_values[static::$_tableKey])) { $insert_values[static::$_tableKey] = DB::DefaultValue(); } $numcols = count($insert_values); $q = 'INSERT INTO "' . static::$_tableName . '"'; $cols = ' ('; $vals = ' VALUES ('; $insert_data = []; $i = 0; foreach ($insert_values as $col => $val) { $i++; $end = $i < $numcols ? ", " : ") "; // quote column names in case we run into a reserved word $cols .= '"' . $col . '"' . $end; $val = $this->typeJuggle($col, $val); if ($val instanceof DB\Literal) { $vals .= $val->literal(); } else { $vals .= ':' . $col; // tack on to array for later execution of prep'd statement: $insert_data[$col] = $val; } $vals .= $end; } $q .= $cols . $vals; if ('pgsql' === constant('\\DB_SERVER_TYPE')) { $q .= ' RETURNING *'; } try { $sth = $dbh->prepare($q); foreach ($insert_data as $col => $val) { $sth->bindValue(":{$col}", $val); } $sth->execute(); if ('pgsql' === constant('\\DB_SERVER_TYPE')) { $insert_result = $sth->fetch(\PDO::FETCH_ASSOC); $this->loadFromRow($insert_result); } else { $this->_tableId = $dbh->lastInsertId(); } } catch (Exception $e) { $failure_data_string = ''; foreach ($insert_values as $failed_insert_column => $failed_insert_value) { if ($failed_insert_value instanceof DB\Literal) { $failed_insert_value = $failed_insert_value->literal(); } $failure_data_string .= "{$failed_insert_column}: {$failed_insert_value}; "; } throw new SparkRecordException('Insert failed: ' . $e->getMessage() . "\n[Query] " . $q . "\n[Values] {$failure_data_string}\n"); } // We have an id now, so we could do a delete operation. $this->_canDelete = true; // try and keep this in synch $this->_record[static::$_tableKey] = $this->_tableId; if ($this->_logModifications || count($this->modificationInfo()) > 0) { $this->logModifications('INSERT', $this->_record); } $this->logInsert(); $this->postInsert(); // Reset the changes array and the _changed flag so that // any update() calls using the same dinosaur don't rewrite // those fields, and so that postUpdate() can behave sanely // in that case. $this->markUnchanged(); return $this->_tableId; }