public function testIterating() { $args = new ArgumentArray(); $args->add(new Bind('name', 'John')); $args->add(new Bind('foo', 'FooBar')); foreach ($args as $n => $v) { ok(is_string($n)); ok(in_array($v, array('John', 'FooBar'))); } }
public function toSql(BaseDriver $driver, ArgumentArray $args) { foreach ($this->args as $k => $a) { if ($a instanceof Bind) { $args->add($a); } else { $args->add(new Bind($k, $a)); } } return $this->str; }
/** * For variable placeholder like PDO, we need 1 or 0 for boolean type, * * For pgsql and mysql sql statement, * we use TRUE or FALSE for boolean type. * * FOr sqlite sql statement: * we use 1 or 0 for boolean type. */ public function deflate($value, ArgumentArray $args = NULL) { if ($this->alwaysBindValues) { if ($value instanceof Raw) { return $value->__toString(); } elseif ($value instanceof Bind) { if ($args) { $args->bind($value); } return $value->getMarker(); } elseif ($value instanceof ParamMarker) { if ($args) { $args->bind(new Bind($value->getMarker(), NULL)); } return $value->getMarker(); } else { $bind = $this->allocateBind($value); if ($args) { $args->bind($bind); } return $bind->getMarker(); } } if ($value === NULL) { return 'NULL'; } elseif ($value === true) { return 'TRUE'; } elseif ($value === false) { return 'FALSE'; } elseif (is_integer($value)) { return intval($value); } elseif (is_float($value)) { return floatval($value); } elseif (is_string($value)) { return $this->quote($value); } elseif (is_callable($value)) { return call_user_func($value); } elseif (is_object($value)) { if ($value instanceof Bind) { if ($args) { $args->bind($value); } if ($this->paramMarkerType === self::QMARK_PARAM_MARKER) { return '?'; } elseif ($this->paramMarkerType === self::NAMED_PARAM_MARKER) { return $value->getMarker(); } else { return $value->getMarker(); } } elseif ($value instanceof ParamMarker) { if ($args) { $args->bind(new Bind($value->getMarker(), NULL)); } if ($this->paramMarkerType === self::QMARK_PARAM_MARKER) { return '?'; } elseif ($this->paramMarkerType === self::NAMED_PARAM_MARKER) { return $value->getMarker(); } else { return $value->getMarker(); } return $value->getMarker(); } elseif ($value instanceof Unknown) { return 'UNKNOWN'; } else { if ($value instanceof DateTime) { // convert DateTime object into string // return $this->quote($value->format(DateTime::ISO8601)); return $this->quote($value->format(DateTime::ATOM)); // sqlite use ATOM format } elseif ($value instanceof ToSqlInterface) { return $value->toSql($this, $args); } elseif ($value instanceof Raw) { return $value->__toString(); } else { throw new LogicException('Unsupported class: ' . get_class($value)); } } } elseif (is_array($value)) { // error_log("LazyRecord: deflating array type value", 0); return $value[0]; } else { throw new LogicException('BaseDriver::deflate: Unsupported variable type'); } return $value; }
/** * Update current record * * @param array $args * * @return Result operation result (success or error) */ public function update(array $args, $options = array()) { $schema = $this->getSchema(); // check if the record is loaded. $k = static::PRIMARY_KEY; if ($k && !isset($args[$k]) && !isset($this->_data[$k])) { throw new Exception('Record is not loaded, Can not update record.', array('args' => $args)); } if (!$this->currentUserCan($this->getCurrentUser(), 'update', $args)) { return $this->reportError('Permission denied. Can not update record.', array('args' => $args)); } // check if we get primary key value // here we allow users to specifty primary key value from arguments if the record is not loaded. $kVal = null; if (isset($args[$k]) && is_scalar($args[$k])) { $kVal = intval($args[$k]); } else { if (isset($this->_data[$k])) { $kVal = intval($this->_data[$k]); } } if (!$kVal) { throw new Exception("Primary key value is undefined."); } $origArgs = $args; $dsId = $this->writeSourceId; $conn = $this->getWriteConnection(); $driver = $this->getWriteQueryDriver(); $sql = null; $vars = null; $arguments = new ArgumentArray(); $query = new UpdateQuery(); $validationError = false; $validationResults = array(); $updateArgs = array(); $schema = $this->getSchema(); try { $args = $this->beforeUpdate($args); // foreach mixin schema, run their beforeUpdate method, $args = array_intersect_key($args, array_flip($schema->columnNames)); foreach ($schema->columns as $n => $c) { if (isset($args[$n]) && !$args[$n] && !$c->primary) { if ($val = $c->getDefaultValue($this, $args)) { $args[$n] = $val; } } // column validate (value is set.) if (!array_key_exists($n, $args)) { continue; } // if column is required (can not be empty) // and default is defined. if ($c->required && array_key_exists($n, $args) && $args[$n] === null) { return $this->reportError("Value of {$n} is required."); } // TODO: Do not render immutable field in ActionKit if ($c->immutable) { // TODO: render as a validation results? // unset($args[$n]); // continue; return $this->reportError("You can not update {$n} column, which is immutable.", array('args' => $args)); } if ($args[$n] !== null && !is_array($args[$n]) && !$args[$n] instanceof Raw) { $args[$n] = $c->typeCasting($args[$n]); } // The is_array function here is for checking raw sql value. if ($args[$n] !== null && !is_array($args[$n]) && !$args[$n] instanceof Raw) { if (false === $c->checkTypeConstraint($args[$n])) { return $this->reportError($args[$n] . " is not " . $c->isa . " type"); } } if ($c->filter || $c->canonicalizer) { $args[$n] = $c->canonicalizeValue($args[$n], $this, $args); } if ($validationResult = $this->_validateColumn($c, $args[$n], $args)) { $validationResults[$n] = $validationResult; if (!$validationResult['valid']) { $validationError = true; } } // deflate the values into query /* if ($args[$n] instanceof Raw) { $updateArgs[$n] = $args[$n]; } else { $updateArgs[$n] = $c->deflate($args[$n], $driver); } */ // use parameter binding for binding $val = $args[$n]; if (is_scalar($args[$n]) || is_null($args[$n])) { $updateArgs[$n] = $bind = new Bind($n, $driver->cast($args[$n])); $arguments->bind($bind); } else { if ($args[$n] instanceof Raw) { $updateArgs[$n] = $args[$n]; } else { $updateArgs[$n] = $bind = new Bind($n, $c->deflate($args[$n], $driver)); $arguments->bind($bind); } } } if ($validationError) { return $this->reportError("Validation failed.", array('validations' => $validationResults)); } // TODO: optimized to built cache $query->set($updateArgs); $query->update($this->table); $query->where()->equal($k, $kVal); $sql = $query->toSql($driver, $arguments); $stm = $conn->prepare($sql); $stm->execute($arguments->toArray()); // Merge updated data. // // if $args contains a raw SQL string, // we should reload data from database if (isset($options['reload'])) { $this->reload(); } else { $this->_data = array_merge($this->_data, $args); } $this->afterUpdate($origArgs); } catch (PDOException $e) { throw new QueryException("Record update failed", $this, $e, array('driver' => get_class($driver), 'args' => $args, 'sql' => $sql, 'validations' => $validationResults)); } return $this->reportSuccess('Updated', array('id' => $kVal, 'sql' => $sql, 'args' => $args, 'type' => Result::TYPE_UPDATE)); }