type names */ public function __construct(array $columns, $typeMap) { $this->_columns = $columns; $this->typeMap($typeMap); } /** * Add a row of data to be inserted. * * @param array|\Cake\Database\Query $data Array of data to append into the insert, or * a query for doing INSERT INTO .. SELECT style commands * @return void * @throws \Cake\Database\Exception When mixing array + Query data types. */ public function add($data) { if ((count($this->_values) && $data instanceof Query) || ($this->_query && is_array($data)) ) { throw new Exception( 'You cannot mix subqueries and array data in inserts.' ); } if ($data instanceof Query) { $this->query($data); return; } $this->_values[] = $data; $this->_castedExpressions = false; } /** * Sets the columns to be inserted. If no params are passed, then it returns * the currently stored columns * * @param array|null $cols arrays with columns to be inserted * @return array|$this */ public function columns($cols = null) { if ($cols === null) { return $this->_columns; } $this->_columns = $cols; $this->_castedExpressions = false; return $this; } /** * Sets the values to be inserted. If no params are passed, then it returns * the currently stored values * * @param array|null $values arrays with values to be inserted * @return array|$this */ public function values($values = null) { if ($values === null) { if (!$this->_castedExpressions) { $this->_processExpressions(); } return $this->_values; } $this->_values = $values; $this->_castedExpressions = false; return $this; } /** * Sets the query object to be used as the values expression to be evaluated * to insert records in the table. If no params are passed, then it returns * the currently stored query * * @param \Cake\Database\Query|null $query The query to set/get * @return \Cake\Database\Query */ public function query(Query $query = null) { if ($query === null) { return $this->_query; } $this->_query = $query; } /** * Convert the values into a SQL string with placeholders. * * @param \Cake\Database\ValueBinder $generator Placeholder generator object * @return string */ public function sql(ValueBinder $generator) { if (empty($this->_values) && empty($this->_query)) { return ''; } if (!$this->_castedExpressions) { $this->_processExpressions(); } $i = 0; $columns = []; // Remove identifier quoting so column names match keys. foreach ($this->_columns as $col) { $columns[] = trim($col, '`[]"'); } $defaults = array_fill_keys($columns, null); $placeholders = []; $types = []; $typeMap = $this->typeMap(); foreach ($defaults as $col => $v) { $types[$col] = $typeMap->type($col); } foreach ($this->_values as $row) { $row += $defaults; $rowPlaceholders = []; foreach ($this->_columns as $column) { $value = $row[$column]; if ($value instanceof ExpressionInterface) { $rowPlaceholders[] = '(' . $value->sql($generator) . ')'; continue; } $placeholder = $generator->placeholder($i); $rowPlaceholders[] = $placeholder; $generator->bind($placeholder, $value, $types[$column]); } $placeholders[] = implode(', ', $rowPlaceholders); } if ($this->query()) { return ' ' . $this->query()->sql($generator); } return sprintf(' VALUES (%s)', implode('), (', $placeholders)); } /** * Traverse the values expression. * * This method will also traverse any queries that are to be used in the INSERT * values. * * @param callable $visitor The visitor to traverse the expression with. * @return void */ public function traverse(callable $visitor) { if ($this->_query) { return; } if (!$this->_castedExpressions) { $this->_processExpressions(); } foreach ($this->_values as $v) { if ($v instanceof ExpressionInterface) { $v->traverse($visitor); } if (!is_array($v)) { continue; } foreach ($v as $column => $field) { if ($field instanceof ExpressionInterface) { $visitor($field); $field->traverse($visitor); } } } } /** * Converts values that need to be casted to expressions * * @return void */ protected function _processExpressions() { $types = []; $typeMap = $this->typeMap(); foreach ($this->_columns as $c) { if (!is_scalar($c)) { continue; } $types[$c] = $typeMap->type($c); } $types = $this->_requiresToExpressionCasting($types); if (empty($types)) { return; } foreach ($this->_values as $row => $values) { foreach ($types as $col => $type) { $this->_values[$row][$col] = $type->toExpression($values[$col]); } } $this->_castedExpressions = true; } }