silverstripe-framework/src/ORM/Queries/SQLAssignmentRow.php

240 lines
6.9 KiB
PHP

<?php
namespace SilverStripe\ORM\Queries;
use InvalidArgumentException;
/**
* Represents a list of updates / inserts made to a single row in a table
*/
class SQLAssignmentRow
{
/**
* List of field values to store for this query
*
* Each item in this array will be in the form of a single-length array
* in the format ['sql' => [$parameters]].
* The field name is stored as the key
*
* E.g.
*
* <code>$assignments['ID'] = ['?' => [1]];</code>
*
* This allows for complex, parameterised updates, or explict field values set
* without any prameters
*
* @var array
*/
protected $assignments = [];
/**
* Instantiate a new SQLAssignmentRow object with the given values
*
* @param array $values
*/
function __construct(array $values = [])
{
$this->setAssignments($values);
}
/**
* Given a key / value pair, extract the predicate and any potential paramaters
* in a format suitable for storing internally as a list of paramaterised conditions.
*
* @param mixed $value Either a literal field value, or an array with
* placeholder => parameter(s) as a pair
* @return array A single item array in the format [$sql => [$parameters]]
*/
protected function parseAssignment($value)
{
// Assume string values (or values saved as customised array objects)
// represent simple assignment
if (!is_array($value) || isset($value['type'])) {
return ['?' => [$value]];
}
// If given as array then extract and check both the SQL as well as the parameter(s)
// Note that there could be multiple parameters, e.g.
// ['MAX(?,?)' => [1,2]] although the container should
// have a single item
if (count($value) == 1) {
foreach ($value as $sql => $parameters) {
if (!is_string($sql)) {
continue;
}
if (!is_array($parameters)) {
$parameters = [$parameters];
}
// @todo Some input sanitisation checking the key contains the
// correct number of ? placeholders as the number of parameters
return [$sql => $parameters];
}
}
throw new InvalidArgumentException(
"Nested field assignments should be given as a single parameterised item array in "
. "['?' => ['value']] format)"
);
}
/**
* Given a list of assignments in any user-acceptible format, normalise the
* value to a common ['SQL' => [parameters]] format
*
* @param array $assignments List of assignments.
* The key of this array should be the field name, and the value the assigned
* literal value, or an array with parameterised information.
* @return array List of normalised assignments
*/
protected function normaliseAssignments(array $assignments)
{
$normalised = [];
foreach ($assignments as $field => $value) {
$normalised[$field] = $this->parseAssignment($value);
}
return $normalised;
}
/**
* Adds assignments for a list of several fields
*
* Note that field values must not be escaped, as these will be internally
* parameterised by the database engine.
*
* <code>
*
* // Basic assignments
* $query->addAssignments([
* '"Object"."Title"' => 'Bob',
* '"Object"."Description"' => 'Bob was here'
* ])
*
* // Parameterised assignments
* $query->addAssignments([
* '"Object"."Title"' => ['?' => 'Bob'],
* '"Object"."Description"' => ['?' => null]
* ])
*
* // Complex parameters
* $query->addAssignments([
* '"Object"."Score"' => ['MAX(?,?)' => [1, 3]]
* ]);
*
* // Assignment of literal SQL for a field. The empty array is
* // important to denote the zero-number paramater list
* $query->addAssignments([
* '"Object"."Score"' => ['NOW()' => []]
* ]);
*
* </code>
*
* @param array $assignments The list of fields to assign
* @return $this The self reference to this row
*/
public function addAssignments(array $assignments)
{
$assignments = $this->normaliseAssignments($assignments);
$this->assignments = array_merge($this->assignments, $assignments);
return $this;
}
/**
* Sets the list of assignments to the given list
*
* @see SQLWriteExpression::addAssignments() for syntax examples
*
* @param array $assignments
* @return $this The self reference to this row
*/
public function setAssignments(array $assignments)
{
return $this->clear()->addAssignments($assignments);
}
/**
* Retrieves the list of assignments in parameterised format
*
* @return array List of assigments. The key of this array will be the
* column to assign, and the value a parameterised array in the format
* ['SQL' => [parameters]];
*/
public function getAssignments()
{
return $this->assignments;
}
/**
* Set the value for a single field
*
* E.g.
* <code>
*
* // Literal assignment
* $query->assign('"Object"."Description"', 'lorum ipsum');
*
* // Single parameter
* $query->assign('"Object"."Title"', ['?' => 'Bob']);
*
* // Complex parameters
* $query->assign('"Object"."Score"', ['MAX(?,?)' => [1, 3]]);
* </code>
*
* @param string $field The field name to update
* @param mixed $value The value to assign to this field. This could be an
* array containing a parameterised SQL query of any number of parameters,
* or a single literal value.
* @return $this The self reference to this row
*/
public function assign($field, $value)
{
return $this->addAssignments([$field => $value]);
}
/**
* Assigns a value to a field using the literal SQL expression, rather than
* a value to be escaped
*
* @param string $field The field name to update
* @param string $sql The SQL to use for this update. E.g. "NOW()"
* @return $this The self reference to this row
*/
public function assignSQL($field, $sql)
{
return $this->assign($field, [$sql => []]);
}
/**
* Determine if this assignment is empty
*
* @return boolean Flag indicating that this assignment is empty
*/
public function isEmpty()
{
return empty($this->assignments);
}
/**
* Retrieves the list of columns updated
*
* @return array
*/
public function getColumns()
{
return array_keys($this->assignments);
}
/**
* Clears all assignment values
*
* @return $this The self reference to this row
*/
public function clear()
{
$this->assignments = [];
return $this;
}
}