type names */ public function __construct(array $columns, $typeMap) { $this->_columns = $columns; $this->setTypeMap($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->setQuery($data); return; } $this->_values[] = $data; $this->_castedExpressions = false; } /** * Sets the columns to be inserted. * * @param array $cols Array with columns to be inserted. * @return $this */ public function setColumns($cols) { $this->_columns = $cols; $this->_castedExpressions = false; return $this; } /** * Gets the columns to be inserted. * * @return array */ public function getColumns() { return $this->_columns; } /** * Sets the columns to be inserted. If no params are passed, then it returns * the currently stored columns. * * @deprecated 3.4.0 Use setColumns()/getColumns() instead. * @param array|null $cols Array with columns to be inserted. * @return array|$this */ public function columns($cols = null) { deprecationWarning( 'ValuesExpression::columns() is deprecated. ' . 'Use ValuesExpression::setColumns()/getColumns() instead.' ); if ($cols !== null) { return $this->setColumns($cols); } return $this->getColumns(); } /** * Get the bare column names. * * Because column names could be identifier quoted, we * need to strip the identifiers off of the columns. * * @return array */ protected function _columnNames() { $columns = []; foreach ($this->_columns as $col) { if (is_string($col)) { $col = trim($col, '`[]"'); } $columns[] = $col; } return $columns; } /** * Sets the values to be inserted. * * @param array $values Array with values to be inserted. * @return $this */ public function setValues($values) { $this->_values = $values; $this->_castedExpressions = false; return $this; } /** * Gets the values to be inserted. * * @return array */ public function getValues() { if (!$this->_castedExpressions) { $this->_processExpressions(); } return $this->_values; } /** * Sets the values to be inserted. If no params are passed, then it returns * the currently stored values * * @deprecated 3.4.0 Use setValues()/getValues() instead. * @param array|null $values Array with values to be inserted. * @return array|$this */ public function values($values = null) { deprecationWarning( 'ValuesExpression::values() is deprecated. ' . 'Use ValuesExpression::setValues()/getValues() instead.' ); if ($values !== null) { return $this->setValues($values); } return $this->getValues(); } /** * Sets the query object to be used as the values expression to be evaluated * to insert records in the table. * * @param \Cake\Database\Query $query The query to set * @return $this */ public function setQuery(Query $query) { $this->_query = $query; return $this; } /** * Gets the query object to be used as the values expression to be evaluated * to insert records in the table. * * @return \Cake\Database\Query|null */ public function getQuery() { return $this->_query; } /** * 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 * * @deprecated 3.4.0 Use setQuery()/getQuery() instead. * @param \Cake\Database\Query|null $query The query to set * @return \Cake\Database\Query|null|$this */ public function query(Query $query = null) { deprecationWarning( 'ValuesExpression::query() is deprecated. ' . 'Use ValuesExpression::setQuery()/getQuery() instead.' ); if ($query !== null) { return $this->setQuery($query); } return $this->getQuery(); } /** * 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(); } $columns = $this->_columnNames(); $defaults = array_fill_keys($columns, null); $placeholders = []; $types = []; $typeMap = $this->getTypeMap(); foreach ($defaults as $col => $v) { $types[$col] = $typeMap->type($col); } foreach ($this->_values as $row) { $row += $defaults; $rowPlaceholders = []; foreach ($columns as $column) { $value = $row[$column]; if ($value instanceof ExpressionInterface) { $rowPlaceholders[] = '(' . $value->sql($generator) . ')'; continue; } $placeholder = $generator->placeholder('c'); $rowPlaceholders[] = $placeholder; $generator->bind($placeholder, $value, $types[$column]); } $placeholders[] = implode(', ', $rowPlaceholders); } if ($this->getQuery()) { return ' ' . $this->getQuery()->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->getTypeMap(); $columns = $this->_columnNames(); foreach ($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) { /* @var \Cake\Database\Type\ExpressionTypeInterface $type */ $this->_values[$row][$col] = $type->toExpression($values[$col]); } } $this->_castedExpressions = true; } }