class Upsert

Same name in this branch
  1. main core/modules/sqlite/src/Driver/Database/sqlite/Upsert.php \Drupal\sqlite\Driver\Database\sqlite\Upsert
  2. main core/modules/mysql/src/Driver/Database/mysql/Upsert.php \Drupal\mysql\Driver\Database\mysql\Upsert
  3. main core/modules/pgsql/src/Driver/Database/pgsql/Upsert.php \Drupal\pgsql\Driver\Database\pgsql\Upsert
  4. main core/tests/fixtures/database_drivers/module/core_fake/src/Driver/Database/CoreFakeWithAllCustomClasses/Upsert.php \Drupal\core_fake\Driver\Database\CoreFakeWithAllCustomClasses\Upsert
Same name and namespace in other branches
  1. 11.x core/modules/sqlite/src/Driver/Database/sqlite/Upsert.php \Drupal\sqlite\Driver\Database\sqlite\Upsert
  2. 11.x core/modules/mysql/src/Driver/Database/mysql/Upsert.php \Drupal\mysql\Driver\Database\mysql\Upsert
  3. 11.x core/modules/pgsql/src/Driver/Database/pgsql/Upsert.php \Drupal\pgsql\Driver\Database\pgsql\Upsert
  4. 11.x core/tests/fixtures/database_drivers/module/core_fake/src/Driver/Database/CoreFakeWithAllCustomClasses/Upsert.php \Drupal\core_fake\Driver\Database\CoreFakeWithAllCustomClasses\Upsert
  5. 11.x core/lib/Drupal/Core/Database/Query/Upsert.php \Drupal\Core\Database\Query\Upsert
  6. 10 core/modules/sqlite/src/Driver/Database/sqlite/Upsert.php \Drupal\sqlite\Driver\Database\sqlite\Upsert
  7. 10 core/modules/mysql/src/Driver/Database/mysql/Upsert.php \Drupal\mysql\Driver\Database\mysql\Upsert
  8. 10 core/modules/pgsql/src/Driver/Database/pgsql/Upsert.php \Drupal\pgsql\Driver\Database\pgsql\Upsert
  9. 10 core/tests/fixtures/database_drivers/module/core_fake/src/Driver/Database/CoreFakeWithAllCustomClasses/Upsert.php \Drupal\core_fake\Driver\Database\CoreFakeWithAllCustomClasses\Upsert
  10. 10 core/lib/Drupal/Core/Database/Driver/sqlite/Upsert.php \Drupal\Core\Database\Driver\sqlite\Upsert
  11. 10 core/lib/Drupal/Core/Database/Driver/mysql/Upsert.php \Drupal\Core\Database\Driver\mysql\Upsert
  12. 10 core/lib/Drupal/Core/Database/Driver/pgsql/Upsert.php \Drupal\Core\Database\Driver\pgsql\Upsert
  13. 10 core/lib/Drupal/Core/Database/Query/Upsert.php \Drupal\Core\Database\Query\Upsert
  14. 9 core/modules/sqlite/src/Driver/Database/sqlite/Upsert.php \Drupal\sqlite\Driver\Database\sqlite\Upsert
  15. 9 core/modules/mysql/src/Driver/Database/mysql/Upsert.php \Drupal\mysql\Driver\Database\mysql\Upsert
  16. 9 core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Upsert.php \Drupal\driver_test\Driver\Database\DrivertestMysql\Upsert
  17. 9 core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysqlDeprecatedVersion/Upsert.php \Drupal\driver_test\Driver\Database\DrivertestMysqlDeprecatedVersion\Upsert
  18. 9 core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Upsert.php \Drupal\driver_test\Driver\Database\DrivertestPgsql\Upsert
  19. 9 core/modules/pgsql/src/Driver/Database/pgsql/Upsert.php \Drupal\pgsql\Driver\Database\pgsql\Upsert
  20. 9 core/tests/fixtures/database_drivers/module/corefake/src/Driver/Database/corefakeWithAllCustomClasses/Upsert.php \Drupal\corefake\Driver\Database\corefakeWithAllCustomClasses\Upsert
  21. 9 core/lib/Drupal/Core/Database/Driver/sqlite/Upsert.php \Drupal\Core\Database\Driver\sqlite\Upsert
  22. 9 core/lib/Drupal/Core/Database/Driver/mysql/Upsert.php \Drupal\Core\Database\Driver\mysql\Upsert
  23. 9 core/lib/Drupal/Core/Database/Driver/pgsql/Upsert.php \Drupal\Core\Database\Driver\pgsql\Upsert
  24. 9 core/lib/Drupal/Core/Database/Query/Upsert.php \Drupal\Core\Database\Query\Upsert
  25. 8.9.x core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestMysql/Upsert.php \Drupal\driver_test\Driver\Database\DrivertestMysql\Upsert
  26. 8.9.x core/modules/system/tests/modules/driver_test/src/Driver/Database/DrivertestPgsql/Upsert.php \Drupal\driver_test\Driver\Database\DrivertestPgsql\Upsert
  27. 8.9.x core/lib/Drupal/Core/Database/Driver/sqlite/Upsert.php \Drupal\Core\Database\Driver\sqlite\Upsert
  28. 8.9.x core/lib/Drupal/Core/Database/Driver/mysql/Upsert.php \Drupal\Core\Database\Driver\mysql\Upsert
  29. 8.9.x core/lib/Drupal/Core/Database/Driver/pgsql/Upsert.php \Drupal\Core\Database\Driver\pgsql\Upsert
  30. 8.9.x core/lib/Drupal/Core/Database/Query/Upsert.php \Drupal\Core\Database\Query\Upsert

General class for an abstracted "Upsert" (UPDATE or INSERT) query operation.

This class works like Insert except the rows will be set to the desired values even if the key existed before. It supports both single-field and composite (multi-field) unique or primary key constraints.

Hierarchy

Expanded class hierarchy of Upsert

6 files declare their use of Upsert
Connection.php in core/tests/fixtures/database_drivers/custom/fake/Connection.php
StubUpsert.php in core/tests/Drupal/Tests/Core/Database/Stub/StubUpsert.php
Upsert.php in core/modules/sqlite/src/Driver/Database/sqlite/Upsert.php
Upsert.php in core/modules/mysql/src/Driver/Database/mysql/Upsert.php
Upsert.php in core/modules/pgsql/src/Driver/Database/pgsql/Upsert.php

... See full list

1 string reference to 'Upsert'
ConnectionTest::providerGetDriverClass in core/tests/Drupal/Tests/Core/Database/ConnectionTest.php
Data provider for testGetDriverClass().

File

core/lib/Drupal/Core/Database/Query/Upsert.php, line 14

Namespace

Drupal\Core\Database\Query
View source
abstract class Upsert extends Query implements \Countable {
  use InsertTrait;
  
  /**
   * The unique or primary key column(s) of the table.
   *
   * @var string[]
   */
  protected $key;
  
  /**
   * Explicit updates to be done in case of insert failure.
   *
   * If the insert part of the upsert fails, the default behavior is to update
   * the existing record with the values passed in. However, in some cases you
   * may want to see what is the value of a column of the existing record, and
   * manipulate that - typically, counters or the like. This array contains
   * such overrides.
   *
   * @var array<string,\Drupal\Core\Database\Query\UpsertUpdateExpression>
   */
  protected array $updateExpressions = [];
  
  /**
   * Constructs an Upsert object.
   *
   * @param \Drupal\Core\Database\Connection $connection
   *   A Connection object.
   * @param string $table
   *   Name of the table to associate with this query.
   * @param array $options
   *   (optional) An array of database options.
   */
  public function __construct(Connection $connection, $table, array $options = []) {
    parent::__construct($connection, $options);
    $this->table = $table;
  }
  
  /**
   * Sets the unique / primary key field(s) to be used as condition.
   *
   * @param string|string[] $field
   *   The name of the field, or an array of field names for a composite key.
   *
   * @return $this
   */
  public function key(string|array $field) {
    $this->key = (array) $field;
    return $this;
  }
  
  /**
   * Adds a set of column->value pairs to be inserted.
   *
   * This method may only be called once. Calling it a second time will be
   * ignored. To queue up multiple sets of values to be inserted at once,
   * use the values() method.
   *
   * @param array<string|int,string|\Drupal\Core\Database\Query\UpsertUpdateExpression>|array<string,string|int|float|bool|array> $fields
   *   An array of columns on which to insert. This array may be indexed or
   *   associative. If indexed, the array is taken to be the list of columns.
   *   If associative, the keys of the array are taken to be the columns and
   *   the values are taken to be corresponding values to insert. If a
   *   $values argument is provided, $fields must be indexed.
   *   In the indexed form, an entry may map a column name to an
   *   UpsertUpdateExpression. When the row already exists, that expression is
   *   run for the column in place of overwriting it with the insert value.
   * @param list<string|int|float|bool|array> $values
   *   (optional) An array of values to insert into the database. The values
   *   must be specified in the same order as the $fields array.
   *
   * @return $this
   *   The called object.
   *
   * @throws \InvalidArgumentException
   *   If an update expression is not keyed by its column name, or if a column
   *   name maps to an insert value while the $fields array is not fully
   *   associative.
   */
  public function fields(array $fields, array $values = []) : static {
    if (empty($this->insertFields)) {
      if (empty($values)) {
        $isFieldsAssociative = TRUE;
        array_walk($fields, function (mixed $value, int|string $key) use (&$isFieldsAssociative) : void {
          if (is_numeric($key) || is_string($key) && $value instanceof UpsertUpdateExpression) {
            $isFieldsAssociative = FALSE;
          }
        });
        if ($isFieldsAssociative) {
          $values = array_values($fields);
          $fields = array_keys($fields);
        }
      }
      foreach ($fields as $key => $def) {
        if ($def instanceof UpsertUpdateExpression) {
          if (!is_string($key)) {
            throw new \InvalidArgumentException('An update expression must be keyed by its column name.');
          }
          $this->insertFields[] = $key;
          $this->updateExpressions[$key] = $def;
        }
        elseif (is_string($key)) {
          throw new \InvalidArgumentException(sprintf('The entry for "%s" must be a column name or an update expression keyed by column name. Pass insert values via the $values argument or the values() method.', $key));
        }
        else {
          $this->insertFields[] = $def;
        }
      }
      if (!empty($values)) {
        $this->values($values);
      }
    }
    return $this;
  }
  
  /**
   * Preprocesses and validates the query.
   *
   * @return bool
   *   TRUE if the validation was successful, FALSE otherwise.
   *
   * @throws \Drupal\Core\Database\Query\NoUniqueFieldException
   * @throws \Drupal\Core\Database\Query\FieldsOverlapException
   * @throws \Drupal\Core\Database\Query\NoFieldsException
   */
  protected function preExecute() {
    // Confirm that the user set the unique/primary key of the table.
    if (!$this->key) {
      throw new NoUniqueFieldException('There is no unique field specified.');
    }
    // Confirm that the user did not try to specify an identical
    // field and default field.
    if (array_intersect($this->insertFields, $this->defaultFields)) {
      throw new FieldsOverlapException('You may not specify the same field to have a value and a schema-default value.');
    }
    // Don't execute query without fields.
    if (count($this->insertFields) + count($this->defaultFields) == 0) {
      throw new NoFieldsException('There are no fields available to insert with.');
    }
    // If no values have been added, silently ignore this query. This can happen
    // if values are added conditionally, so we don't want to throw an
    // exception.
    return isset($this->insertValues[0]) || $this->insertFields;
  }
  
  /**
   * Executes the UPSERT operation.
   *
   * @return int
   *   An integer indicating the number of rows affected by the operation. Do
   *   not rely on this value as a precise indication of the actual rows
   *   affected: different database engines return different values.
   */
  public function execute() {
    if (!$this->preExecute()) {
      return NULL;
    }
    $max_placeholder = 0;
    $values = [];
    foreach ($this->insertValues as $insert_values) {
      foreach ($insert_values as $value) {
        $values[':db_insert_placeholder_' . $max_placeholder++] = $value;
      }
    }
    foreach ($this->updateExpressions as $update_expression) {
      foreach ($update_expression->arguments as $argument => $value) {
        $values[$argument] = $value;
      }
    }
    $stmt = $this->connection
      ->prepareStatement((string) $this, $this->queryOptions, TRUE);
    try {
      $stmt->execute($values, $this->queryOptions);
      // Re-initialize the values array so that we can re-use this query.
      $this->insertValues = [];
      return $stmt->rowCount();
    } catch (\Exception $e) {
      $this->connection
        ->exceptionHandler()
        ->handleExecutionException($e, $stmt, $values, $this->queryOptions);
    }
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
InsertTrait::$defaultFields protected property An array of fields that should be set to their database-defined defaults.
InsertTrait::$insertFields protected property An array of fields on which to insert.
InsertTrait::$insertValues protected property A nested array of values to insert.
InsertTrait::$table protected property The table on which to insert.
InsertTrait::count public function
InsertTrait::getInsertPlaceholderFragment protected function Returns the query placeholders for values that will be inserted.
InsertTrait::useDefaults public function Specifies fields for which the database defaults should be used.
InsertTrait::values public function Adds another set of values to the query to be inserted.
Query::$comments protected property An array of comments that can be prepended to a query.
Query::$connection protected property The connection object on which to run this query.
Query::$connectionKey protected property The key of the connection object.
Query::$connectionTarget protected property The target of the connection object.
Query::$nextPlaceholder protected property The placeholder counter.
Query::$queryOptions protected property The query options to pass on to the connection object.
Query::$uniqueIdentifier protected property A unique identifier for this query object.
Query::comment public function Adds a comment to the query.
Query::getComments public function Returns a reference to the comments array for the query.
Query::getConnection public function Gets the database connection to be used for the query.
Query::nextPlaceholder public function Gets the next placeholder value for this query object. Overrides PlaceholderInterface::nextPlaceholder
Query::uniqueIdentifier public function Returns a unique identifier for this object. Overrides PlaceholderInterface::uniqueIdentifier
Query::__clone public function Implements the magic __clone function. 1
Query::__sleep public function Implements the magic __sleep function to disconnect from the database.
Query::__toString abstract public function Implements PHP magic __toString method to convert the query to a string. 11
Query::__wakeup public function Implements the magic __wakeup function to reconnect to the database.
Upsert::$key protected property The unique or primary key column(s) of the table.
Upsert::$updateExpressions protected property Explicit updates to be done in case of insert failure.
Upsert::execute public function Executes the UPSERT operation. 2
Upsert::fields public function Adds a set of column-&gt;value pairs to be inserted. Overrides InsertTrait::fields
Upsert::key public function Sets the unique / primary key field(s) to be used as condition.
Upsert::preExecute protected function Preprocesses and validates the query.
Upsert::__construct public function Constructs an Upsert object. Overrides Query::__construct

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.