public function create(array $args, array $options = array())
{
if (empty($args) || $args === null) {
return $this->reportError('Empty arguments');
}
$validationResults = array();
$validationError = false;
$schema = $this->getSchema();
// save $args for afterCreate trigger method
$origArgs = $args;
$k = static::PRIMARY_KEY;
$sql = $vars = null;
$this->_data = array();
$stm = null;
static $cacheable;
$cacheable = extension_loaded('xarray');
$conn = $this->getWriteConnection();
$driver = $this->getWriteQueryDriver();
// Just a note: Exceptions should be used for exceptional conditions; things you
// don't expect to happen. Validating input isn't very exceptional.
$args = $this->beforeCreate($args);
if ($args === false) {
return $this->reportError(_('Create failed'), array('args' => $args));
}
// first, filter the array, arguments for inserting data.
$args = array_intersect_key($args, array_flip($schema->columnNames));
// @codegenBlock currentUserCan
if (!$this->currentUserCan($this->getCurrentUser(), 'create', $args)) {
return $this->reportError(_('Permission denied. Can not create record.'), array('args' => $args));
}
// @codegenBlockEnd
// arguments that are will Bind
$insertArgs = array();
foreach ($schema->columns as $n => $c) {
// if column is required (can not be empty)
// and default is defined.
if (!$c->primary && (!isset($args[$n]) || !$args[$n])) {
if ($val = $c->getDefaultValue($this, $args)) {
$args[$n] = $val;
}
}
// if type constraint is on, check type,
// if not, we should try to cast the type of value,
// if type casting is fail, we should throw an exception.
// short alias for argument value.
$val = isset($args[$n]) ? $args[$n] : null;
// if column is required (can not be empty) // and default is defined.
// @codegenBlock validateRequire
if ($c->required && array_key_exists($n, $args) && $args[$n] === null) {
return $this->reportError("Value of {$n} is required.");
}
// @codegenBlockEnd
// @codegenBlock typeConstraint
if ($c->typeConstraint && ($val !== null && !is_array($val) && !$val instanceof Raw)) {
if (false === $c->checkTypeConstraint($val)) {
return $this->reportError("{$val} is not " . $c->isa . ' type');
}
} elseif ($val !== null && !is_array($val) && !$val instanceof Raw) {
$val = $c->typeCasting($val);
}
// @codegenBlockEnd
// @codegenBlock filterColumn
if ($c->filter || $c->canonicalizer) {
$val = $c->canonicalizeValue($val, $this, $args);
}
// @codegenBlockEnd
// @codegenBlock validateColumn
if ($validationResult = $this->_validateColumn($c, $val, $args)) {
$validationResults[$n] = $validationResult;
if (!$validationResult['valid']) {
$validationError = true;
}
}
// @codegenBlockEnd
if ($val !== null) {
// Update filtered value back to args
// Note that we don't deflate a scalar value, this is to prevent the overhead of data reload from database
// We should try to keep all variables just like the row result we query from database.
if (is_object($val) || is_array($val)) {
$args[$n] = $c->deflate($val, $driver);
} else {
$args[$n] = $val;
}
if (is_scalar($val) || is_null($val)) {
$insertArgs[$n] = new Bind($n, $driver->cast($val));
} elseif ($val instanceof Raw) {
$insertArgs[$n] = $val;
$cacheable = false;
} else {
// deflate objects into string
$insertArgs[$n] = new Bind($n, $c->deflate($val, $driver));
}
}
}
// @codegenBlock handleValidationError
if ($validationError) {
return $this->reportError('Validation failed.', array('validations' => $validationResults));
}
// @codegenBlockEnd
$arguments = new ArgumentArray();
$cacheKey = null;
if ($cacheable) {
$cacheKey = array_keys_join($insertArgs);
if (isset($this->_preparedCreateStms[$cacheKey])) {
$stm = $this->_preparedCreateStms[$cacheKey];
foreach ($insertArgs as $name => $bind) {
$arguments->bind($bind);
}
}
}
if (!$stm) {
$query = new InsertQuery();
$query->into($this->table);
$query->insert($insertArgs);
$query->returning($k);
$sql = $query->toSql($driver, $arguments);
$stm = $conn->prepare($sql);
if ($cacheable) {
$this->_preparedCreateStms[$cacheKey] = $stm;
}
}
if (false === $stm->execute($arguments->toArray())) {
return $this->reportError('Record create failed.', array('validations' => $validationResults, 'args' => $args, 'sql' => $sql));
}
$pkId = null;
if ($driver instanceof PDOPgSQLDriver) {
$this->_data[$k] = $args[$k] = $pkId = intval($stm->fetchColumn());
} else {
$this->_data[$k] = $args[$k] = $pkId = intval($conn->lastInsertId());
}
if ($pkId && (isset($options['reload']) && $options['reload'] || $this->autoReload)) {
$this->load($pkId);
} else {
$this->_data = $args;
}
$this->afterCreate($origArgs);
$stm->closeCursor();
// collect debug info
return $this->reportSuccess('Record created.', array('id' => $pkId, 'sql' => $sql, 'args' => $args, 'binds' => $arguments, 'validations' => $validationResults, 'type' => Result::TYPE_CREATE));
}