Upsert.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. <?php
  2. namespace Drupal\Core\Database\Query;
  3. use Drupal\Core\Database\Connection;
  4. use Drupal\Core\Database\Database;
  5. /**
  6. * General class for an abstracted "Upsert" (UPDATE or INSERT) query operation.
  7. *
  8. * This class can only be used with a table with a single unique index.
  9. * Often, this will be the primary key. On such a table this class works like
  10. * Insert except the rows will be set to the desired values even if the key
  11. * existed before.
  12. */
  13. abstract class Upsert extends Query implements \Countable {
  14. use InsertTrait;
  15. /**
  16. * The unique or primary key of the table.
  17. *
  18. * @var string
  19. */
  20. protected $key;
  21. /**
  22. * Constructs an Upsert object.
  23. *
  24. * @param \Drupal\Core\Database\Connection $connection
  25. * A Connection object.
  26. * @param string $table
  27. * Name of the table to associate with this query.
  28. * @param array $options
  29. * (optional) An array of database options.
  30. */
  31. public function __construct(Connection $connection, $table, array $options = []) {
  32. $options['return'] = Database::RETURN_AFFECTED;
  33. parent::__construct($connection, $options);
  34. $this->table = $table;
  35. }
  36. /**
  37. * Sets the unique / primary key field to be used as condition for this query.
  38. *
  39. * @param string $field
  40. * The name of the field to set.
  41. *
  42. * @return $this
  43. */
  44. public function key($field) {
  45. $this->key = $field;
  46. return $this;
  47. }
  48. /**
  49. * Preprocesses and validates the query.
  50. *
  51. * @return bool
  52. * TRUE if the validation was successful, FALSE otherwise.
  53. *
  54. * @throws \Drupal\Core\Database\Query\NoUniqueFieldException
  55. * @throws \Drupal\Core\Database\Query\FieldsOverlapException
  56. * @throws \Drupal\Core\Database\Query\NoFieldsException
  57. */
  58. protected function preExecute() {
  59. // Confirm that the user set the unique/primary key of the table.
  60. if (!$this->key) {
  61. throw new NoUniqueFieldException('There is no unique field specified.');
  62. }
  63. // Confirm that the user did not try to specify an identical
  64. // field and default field.
  65. if (array_intersect($this->insertFields, $this->defaultFields)) {
  66. throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
  67. }
  68. // Don't execute query without fields.
  69. if (count($this->insertFields) + count($this->defaultFields) == 0) {
  70. throw new NoFieldsException('There are no fields available to insert with.');
  71. }
  72. // If no values have been added, silently ignore this query. This can happen
  73. // if values are added conditionally, so we don't want to throw an
  74. // exception.
  75. return isset($this->insertValues[0]) || $this->insertFields;
  76. }
  77. /**
  78. * {@inheritdoc}
  79. */
  80. public function execute() {
  81. if (!$this->preExecute()) {
  82. return NULL;
  83. }
  84. $max_placeholder = 0;
  85. $values = [];
  86. foreach ($this->insertValues as $insert_values) {
  87. foreach ($insert_values as $value) {
  88. $values[':db_insert_placeholder_' . $max_placeholder++] = $value;
  89. }
  90. }
  91. $last_insert_id = $this->connection->query((string) $this, $values, $this->queryOptions);
  92. // Re-initialize the values array so that we can re-use this query.
  93. $this->insertValues = [];
  94. return $last_insert_id;
  95. }
  96. }