Update.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. namespace Drupal\Core\Database\Query;
  3. use Drupal\Core\Database\Database;
  4. use Drupal\Core\Database\Connection;
  5. /**
  6. * General class for an abstracted UPDATE operation.
  7. *
  8. * @ingroup database
  9. */
  10. class Update extends Query implements ConditionInterface {
  11. use QueryConditionTrait;
  12. /**
  13. * The table to update.
  14. *
  15. * @var string
  16. */
  17. protected $table;
  18. /**
  19. * An array of fields that will be updated.
  20. *
  21. * @var array
  22. */
  23. protected $fields = [];
  24. /**
  25. * An array of values to update to.
  26. *
  27. * @var array
  28. */
  29. protected $arguments = [];
  30. /**
  31. * Array of fields to update to an expression in case of a duplicate record.
  32. *
  33. * This variable is a nested array in the following format:
  34. * @code
  35. * <some field> => array(
  36. * 'condition' => <condition to execute, as a string>,
  37. * 'arguments' => <array of arguments for condition, or NULL for none>,
  38. * );
  39. * @endcode
  40. *
  41. * @var array
  42. */
  43. protected $expressionFields = [];
  44. /**
  45. * Constructs an Update query object.
  46. *
  47. * @param \Drupal\Core\Database\Connection $connection
  48. * A Connection object.
  49. * @param string $table
  50. * Name of the table to associate with this query.
  51. * @param array $options
  52. * Array of database options.
  53. */
  54. public function __construct(Connection $connection, $table, array $options = []) {
  55. $options['return'] = Database::RETURN_AFFECTED;
  56. parent::__construct($connection, $options);
  57. $this->table = $table;
  58. $this->condition = new Condition('AND');
  59. }
  60. /**
  61. * Adds a set of field->value pairs to be updated.
  62. *
  63. * @param $fields
  64. * An associative array of fields to write into the database. The array keys
  65. * are the field names and the values are the values to which to set them.
  66. *
  67. * @return \Drupal\Core\Database\Query\Update
  68. * The called object.
  69. */
  70. public function fields(array $fields) {
  71. $this->fields = $fields;
  72. return $this;
  73. }
  74. /**
  75. * Specifies fields to be updated as an expression.
  76. *
  77. * Expression fields are cases such as counter=counter+1. This method takes
  78. * precedence over fields().
  79. *
  80. * @param $field
  81. * The field to set.
  82. * @param $expression
  83. * The field will be set to the value of this expression. This parameter
  84. * may include named placeholders.
  85. * @param $arguments
  86. * If specified, this is an array of key/value pairs for named placeholders
  87. * corresponding to the expression.
  88. *
  89. * @return \Drupal\Core\Database\Query\Update
  90. * The called object.
  91. */
  92. public function expression($field, $expression, array $arguments = NULL) {
  93. $this->expressionFields[$field] = [
  94. 'expression' => $expression,
  95. 'arguments' => $arguments,
  96. ];
  97. return $this;
  98. }
  99. /**
  100. * Executes the UPDATE query.
  101. *
  102. * @return
  103. * The number of rows matched by the update query. This includes rows that
  104. * actually didn't have to be updated because the values didn't change.
  105. */
  106. public function execute() {
  107. // Expressions take priority over literal fields, so we process those first
  108. // and remove any literal fields that conflict.
  109. $fields = $this->fields;
  110. $update_values = [];
  111. foreach ($this->expressionFields as $field => $data) {
  112. if (!empty($data['arguments'])) {
  113. $update_values += $data['arguments'];
  114. }
  115. if ($data['expression'] instanceof SelectInterface) {
  116. $data['expression']->compile($this->connection, $this);
  117. $update_values += $data['expression']->arguments();
  118. }
  119. unset($fields[$field]);
  120. }
  121. // Because we filter $fields the same way here and in __toString(), the
  122. // placeholders will all match up properly.
  123. $max_placeholder = 0;
  124. foreach ($fields as $value) {
  125. $update_values[':db_update_placeholder_' . ($max_placeholder++)] = $value;
  126. }
  127. if (count($this->condition)) {
  128. $this->condition->compile($this->connection, $this);
  129. $update_values = array_merge($update_values, $this->condition->arguments());
  130. }
  131. return $this->connection->query((string) $this, $update_values, $this->queryOptions);
  132. }
  133. /**
  134. * Implements PHP magic __toString method to convert the query to a string.
  135. *
  136. * @return string
  137. * The prepared statement.
  138. */
  139. public function __toString() {
  140. // Create a sanitized comment string to prepend to the query.
  141. $comments = $this->connection->makeComment($this->comments);
  142. // Expressions take priority over literal fields, so we process those first
  143. // and remove any literal fields that conflict.
  144. $fields = $this->fields;
  145. $update_fields = [];
  146. foreach ($this->expressionFields as $field => $data) {
  147. if ($data['expression'] instanceof SelectInterface) {
  148. // Compile and cast expression subquery to a string.
  149. $data['expression']->compile($this->connection, $this);
  150. $data['expression'] = ' (' . $data['expression'] . ')';
  151. }
  152. $update_fields[] = $this->connection->escapeField($field) . '=' . $data['expression'];
  153. unset($fields[$field]);
  154. }
  155. $max_placeholder = 0;
  156. foreach ($fields as $field => $value) {
  157. $update_fields[] = $this->connection->escapeField($field) . '=:db_update_placeholder_' . ($max_placeholder++);
  158. }
  159. $query = $comments . 'UPDATE {' . $this->connection->escapeTable($this->table) . '} SET ' . implode(', ', $update_fields);
  160. if (count($this->condition)) {
  161. $this->condition->compile($this->connection, $this);
  162. // There is an implicit string cast on $this->condition.
  163. $query .= "\nWHERE " . $this->condition;
  164. }
  165. return $query;
  166. }
  167. }