Update.php 6.22 KB
Newer Older
1
<?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright 2015-2017 MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
17 18 19 20

namespace MongoDB\Operation;

use MongoDB\UpdateResult;
21
use MongoDB\Driver\BulkWrite as Bulk;
22 23
use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern;
24
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
25
use MongoDB\Exception\InvalidArgumentException;
26
use MongoDB\Exception\UnsupportedException;
27 28 29 30 31 32 33 34 35 36 37 38

/**
 * Operation for the update command.
 *
 * This class is used internally by the ReplaceOne, UpdateMany, and UpdateOne
 * operation classes.
 *
 * @internal
 * @see http://docs.mongodb.org/manual/reference/command/update/
 */
class Update implements Executable
{
39
    private static $wireVersionForCollation = 5;
40 41
    private static $wireVersionForDocumentLevelValidation = 4;

42 43 44 45 46 47 48 49 50 51 52
    private $databaseName;
    private $collectionName;
    private $filter;
    private $update;
    private $options;

    /**
     * Constructs a update command.
     *
     * Supported options:
     *
53 54 55
     *  * bypassDocumentValidation (boolean): If true, allows the write to opt
     *    out of document level validation.
     *
56 57 58 59 60
     *  * collation (document): Collation specification.
     *
     *    This is not supported for server versions < 3.4 and will result in an
     *    exception at execution time if used.
     *
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
     *  * multi (boolean): When true, updates all documents matching the query.
     *    This option cannot be true if the $update argument is a replacement
     *    document (i.e. contains no update operators). The default is false.
     *
     *  * upsert (boolean): When true, a new document is created if no document
     *    matches the query. The default is false.
     *
     *  * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
     *
     * @param string       $databaseName   Database name
     * @param string       $collectionName Collection name
     * @param array|object $filter         Query by which to delete documents
     * @param array|object $update         Update to apply to the matched
     *                                     document(s) or a replacement document
     * @param array        $options        Command options
76
     * @throws InvalidArgumentException for parameter/option parsing errors
77
     */
Jeremy Mikola's avatar
Jeremy Mikola committed
78
    public function __construct($databaseName, $collectionName, $filter, $update, array $options = [])
79 80
    {
        if ( ! is_array($filter) && ! is_object($filter)) {
81
            throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
82 83 84
        }

        if ( ! is_array($update) && ! is_object($update)) {
85
            throw InvalidArgumentException::invalidType('$update', $filter, 'array or object');
86 87
        }

Jeremy Mikola's avatar
Jeremy Mikola committed
88
        $options += [
89 90
            'multi' => false,
            'upsert' => false,
Jeremy Mikola's avatar
Jeremy Mikola committed
91
        ];
92

93
        if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
94
            throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
95 96
        }

97 98 99 100
        if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
            throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
        }

101
        if ( ! is_bool($options['multi'])) {
102
            throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean');
103 104 105 106 107 108 109
        }

        if ($options['multi'] && ! \MongoDB\is_first_key_operator($update)) {
            throw new InvalidArgumentException('"multi" option cannot be true if $update is a replacement document');
        }

        if ( ! is_bool($options['upsert'])) {
110
            throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean');
111 112
        }

113
        if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
114
            throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern');
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
        }

        $this->databaseName = (string) $databaseName;
        $this->collectionName = (string) $collectionName;
        $this->filter = $filter;
        $this->update = $update;
        $this->options = $options;
    }

    /**
     * Execute the operation.
     *
     * @see Executable::execute()
     * @param Server $server
     * @return UpdateResult
130
     * @throws UnsupportedException if collation is used and unsupported
131
     * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
132 133 134
     */
    public function execute(Server $server)
    {
135 136 137 138
        if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
            throw UnsupportedException::collationNotSupported();
        }

139
        $updateOptions = [
140 141
            'multi' => $this->options['multi'],
            'upsert' => $this->options['upsert'],
Jeremy Mikola's avatar
Jeremy Mikola committed
142
        ];
143

144 145 146 147
        if (isset($this->options['collation'])) {
            $updateOptions['collation'] = (object) $this->options['collation'];
        }

148 149 150 151 152 153 154 155
        $bulkOptions = [];

        if (isset($this->options['bypassDocumentValidation']) && \MongoDB\server_supports_feature($server, self::$wireVersionForDocumentLevelValidation)) {
            $bulkOptions['bypassDocumentValidation'] = $this->options['bypassDocumentValidation'];
        }

        $bulk = new Bulk($bulkOptions);
        $bulk->update($this->filter, $this->update, $updateOptions);
156 157 158 159 160 161 162

        $writeConcern = isset($this->options['writeConcern']) ? $this->options['writeConcern'] : null;
        $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $writeConcern);

        return new UpdateResult($writeResult);
    }
}