Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   1  <?php
   2  /*
   3   * Copyright 2015-present MongoDB, Inc.
   4   *
   5   * Licensed under the Apache License, Version 2.0 (the "License");
   6   * you may not use this file except in compliance with the License.
   7   * You may obtain a copy of the License at
   8   *
   9   *   https://www.apache.org/licenses/LICENSE-2.0
  10   *
  11   * Unless required by applicable law or agreed to in writing, software
  12   * distributed under the License is distributed on an "AS IS" BASIS,
  13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14   * See the License for the specific language governing permissions and
  15   * limitations under the License.
  16   */
  17  
  18  namespace MongoDB\Operation;
  19  
  20  use MongoDB\DeleteResult;
  21  use MongoDB\Driver\BulkWrite as Bulk;
  22  use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
  23  use MongoDB\Driver\Server;
  24  use MongoDB\Driver\Session;
  25  use MongoDB\Driver\WriteConcern;
  26  use MongoDB\Exception\InvalidArgumentException;
  27  use MongoDB\Exception\UnsupportedException;
  28  
  29  use function is_array;
  30  use function is_object;
  31  use function is_string;
  32  use function MongoDB\is_write_concern_acknowledged;
  33  use function MongoDB\server_supports_feature;
  34  
  35  /**
  36   * Operation for the delete command.
  37   *
  38   * This class is used internally by the DeleteMany and DeleteOne operation
  39   * classes.
  40   *
  41   * @internal
  42   * @see https://mongodb.com/docs/manual/reference/command/delete/
  43   */
  44  class Delete implements Executable, Explainable
  45  {
  46      /** @var integer */
  47      private static $wireVersionForHint = 9;
  48  
  49      /** @var string */
  50      private $databaseName;
  51  
  52      /** @var string */
  53      private $collectionName;
  54  
  55      /** @var array|object */
  56      private $filter;
  57  
  58      /** @var integer */
  59      private $limit;
  60  
  61      /** @var array */
  62      private $options;
  63  
  64      /**
  65       * Constructs a delete command.
  66       *
  67       * Supported options:
  68       *
  69       *  * collation (document): Collation specification.
  70       *
  71       *  * comment (mixed): BSON value to attach as a comment to this command.
  72       *
  73       *    This is not supported for servers versions < 4.4.
  74       *
  75       *  * hint (string|document): The index to use. Specify either the index
  76       *    name as a string or the index key pattern as a document. If specified,
  77       *    then the query system will only consider plans using the hinted index.
  78       *
  79       *    This is not supported for server versions < 4.4 and will result in an
  80       *    exception at execution time if used.
  81       *
  82       *  * let (document): Map of parameter names and values. Values must be
  83       *    constant or closed expressions that do not reference document fields.
  84       *    Parameters can then be accessed as variables in an aggregate
  85       *    expression context (e.g. "$$var").
  86       *
  87       *  * session (MongoDB\Driver\Session): Client session.
  88       *
  89       *  * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
  90       *
  91       * @param string       $databaseName   Database name
  92       * @param string       $collectionName Collection name
  93       * @param array|object $filter         Query by which to delete documents
  94       * @param integer      $limit          The number of matching documents to
  95       *                                     delete. Must be 0 or 1, for all or a
  96       *                                     single document, respectively.
  97       * @param array        $options        Command options
  98       * @throws InvalidArgumentException for parameter/option parsing errors
  99       */
 100      public function __construct(string $databaseName, string $collectionName, $filter, int $limit, array $options = [])
 101      {
 102          if (! is_array($filter) && ! is_object($filter)) {
 103              throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
 104          }
 105  
 106          if ($limit !== 0 && $limit !== 1) {
 107              throw new InvalidArgumentException('$limit must be 0 or 1');
 108          }
 109  
 110          if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
 111              throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
 112          }
 113  
 114          if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
 115              throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']);
 116          }
 117  
 118          if (isset($options['session']) && ! $options['session'] instanceof Session) {
 119              throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
 120          }
 121  
 122          if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
 123              throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
 124          }
 125  
 126          if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) {
 127              throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object');
 128          }
 129  
 130          if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) {
 131              unset($options['writeConcern']);
 132          }
 133  
 134          $this->databaseName = $databaseName;
 135          $this->collectionName = $collectionName;
 136          $this->filter = $filter;
 137          $this->limit = $limit;
 138          $this->options = $options;
 139      }
 140  
 141      /**
 142       * Execute the operation.
 143       *
 144       * @see Executable::execute()
 145       * @return DeleteResult
 146       * @throws UnsupportedException if hint or write concern is used and unsupported
 147       * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
 148       */
 149      public function execute(Server $server)
 150      {
 151          /* CRUD spec requires a client-side error when using "hint" with an
 152           * unacknowledged write concern on an unsupported server. */
 153          if (
 154              isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) &&
 155              isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint)
 156          ) {
 157              throw UnsupportedException::hintNotSupported();
 158          }
 159  
 160          $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
 161          if ($inTransaction && isset($this->options['writeConcern'])) {
 162              throw UnsupportedException::writeConcernNotSupportedInTransaction();
 163          }
 164  
 165          $bulk = new Bulk($this->createBulkWriteOptions());
 166          $bulk->delete($this->filter, $this->createDeleteOptions());
 167  
 168          $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions());
 169  
 170          return new DeleteResult($writeResult);
 171      }
 172  
 173      /**
 174       * Returns the command document for this operation.
 175       *
 176       * @see Explainable::getCommandDocument()
 177       * @return array
 178       */
 179      public function getCommandDocument(Server $server)
 180      {
 181          $cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]];
 182  
 183          if (isset($this->options['writeConcern'])) {
 184              $cmd['writeConcern'] = $this->options['writeConcern'];
 185          }
 186  
 187          return $cmd;
 188      }
 189  
 190      /**
 191       * Create options for constructing the bulk write.
 192       *
 193       * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php
 194       */
 195      private function createBulkWriteOptions(): array
 196      {
 197          $options = [];
 198  
 199          if (isset($this->options['comment'])) {
 200              $options['comment'] = $this->options['comment'];
 201          }
 202  
 203          if (isset($this->options['let'])) {
 204              $options['let'] = (object) $this->options['let'];
 205          }
 206  
 207          return $options;
 208      }
 209  
 210      /**
 211       * Create options for the delete command.
 212       *
 213       * Note that these options are different from the bulk write options, which
 214       * are created in createExecuteOptions().
 215       */
 216      private function createDeleteOptions(): array
 217      {
 218          $deleteOptions = ['limit' => $this->limit];
 219  
 220          if (isset($this->options['collation'])) {
 221              $deleteOptions['collation'] = (object) $this->options['collation'];
 222          }
 223  
 224          if (isset($this->options['hint'])) {
 225              $deleteOptions['hint'] = $this->options['hint'];
 226          }
 227  
 228          return $deleteOptions;
 229      }
 230  
 231      /**
 232       * Create options for executing the bulk write.
 233       *
 234       * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php
 235       */
 236      private function createExecuteOptions(): array
 237      {
 238          $options = [];
 239  
 240          if (isset($this->options['session'])) {
 241              $options['session'] = $this->options['session'];
 242          }
 243  
 244          if (isset($this->options['writeConcern'])) {
 245              $options['writeConcern'] = $this->options['writeConcern'];
 246          }
 247  
 248          return $options;
 249      }
 250  }