Completed
Pull Request — master (#41)
by
unknown
03:50
created

MongoWriteBatch::validate()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 22
Code Lines 14

Duplication

Lines 16
Ratio 72.73 %
Metric Value
dl 16
loc 22
rs 6.9811
cc 7
eloc 14
nc 7
nop 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 */
15
16
use Alcaeus\MongoDbAdapter\TypeConverter;
17
use Alcaeus\MongoDbAdapter\Helper\WriteConcernConverter;
18
19
/**
20
 * MongoWriteBatch allows you to "batch up" multiple operations (of same type)
21
 * and shipping them all to MongoDB at the same time. This can be especially
22
 * useful when operating on many documents at the same time to reduce roundtrips.
23
 *
24
 * @see http://php.net/manual/en/class.mongowritebatch.php
25
 */
26
class MongoWriteBatch
27
{
28
    use WriteConcernConverter;
29
30
    const COMMAND_INSERT = 1;
31
    const COMMAND_UPDATE = 2;
32
    const COMMAND_DELETE = 3;
33
34
    /**
35
     * @var MongoCollection
36
     */
37
    private $collection;
38
39
    /**
40
     * @var int
41
     */
42
    private $batchType;
43
44
    /**
45
     * @var array
46
     */
47
    private $writeOptions;
48
49
    /**
50
     * @var array
51
     */
52
    private $items = [];
53
54
    /**
55
     * Creates a new batch of write operations
56
     *
57
     * @see http://php.net/manual/en/mongowritebatch.construct.php
58
     * @param MongoCollection $collection
59
     * @param int $batchType
60
     * @param array $writeOptions
61
     */
62
    protected function __construct(MongoCollection $collection, $batchType, $writeOptions)
63
    {
64
        $this->collection = $collection;
65
        $this->batchType = $batchType;
66
        $this->writeOptions = $writeOptions;
67
    }
68
69
    /**
70
     * Adds a write operation to a batch
71
     *
72
     * @see http://php.net/manual/en/mongowritebatch.add.php
73
     * @param array|object $item
74
     * @return boolean
75
     */
76
    public function add($item)
77
    {
78
        if (is_object($item)) {
79
            $item = (array)$item;
80
        }
81
82
        $this->validate($item);
83
        $this->addItem($item);
84
85
        return true;
86
    }
87
88
    /**
89
     * Executes a batch of write operations
90
     *
91
     * @see http://php.net/manual/en/mongowritebatch.execute.php
92
     * @param array $writeOptions
93
     * @return array
94
     */
95
    final public function execute(array $writeOptions = [])
96
    {
97
        $writeOptions += $this->writeOptions;
98
        if (! count($this->items)) {
99
            return ['ok' => true];
100
        }
101
102
        if (isset($writeOptions['j'])) {
103
            trigger_error('j parameter is not supported', E_WARNING);
104
        }
105
        if (isset($writeOptions['fsync'])) {
106
            trigger_error('fsync parameter is not supported', E_WARNING);
107
        }
108
109
        $options['writeConcern'] = $this->createWriteConcernFromArray($writeOptions);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
110
        if (isset($writeOptions['ordered'])) {
111
            $options['ordered'] = $writeOptions['ordered'];
112
        }
113
114
        $collection = $this->collection->getCollection();
115
116
        try {
117
            $result = $collection->BulkWrite($this->items, $options);
118
            $ok = true;
119
        } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
1 ignored issue
show
Bug introduced by
The class MongoDB\Driver\Exception\BulkWriteException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
120
            $result = $e->getWriteResult();
121
            $ok = false;
122
        }
123
124
        if ($ok === true) {
125
            $this->items = [];
126
        }
127
128
        switch ($this->batchType) {
129
            case self::COMMAND_UPDATE:
130
                return [
131
                    'nMatched' => $result->getMatchedCount(),
132
                    'nModified' => $result->getModifiedCount(),
133
                    'nUpserted' => $result->getUpsertedCount(),
134
                    'ok' => $ok,
135
                ];
136
137
            case self::COMMAND_DELETE:
138
                return [
139
                    'nRemoved' => $result->getDeletedCount(),
140
                    'ok' => $ok,
141
                ];
142
143
            case self::COMMAND_INSERT:
144
                return [
145
                    'nInserted' => $result->getInsertedCount(),
146
                    'ok' => $ok,
147
                ];
148
        }
149
    }
150
151
    private function validate(array $item)
152
    {
153
        switch ($this->batchType) {
154 View Code Duplication
            case self::COMMAND_UPDATE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
                if (! isset($item['q'])) {
156
                    throw new Exception("Expected $item to contain 'q' key");
157
                }
158
                if (! isset($item['u'])) {
159
                    throw new Exception("Expected $item to contain 'u' key");
160
                }
161
                break;
162
163 View Code Duplication
            case self::COMMAND_DELETE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
                if (! isset($item['q'])) {
165
                    throw new Exception("Expected $item to contain 'q' key");
166
                }
167
                if (! isset($item['limit'])) {
168
                    throw new Exception("Expected $item to contain 'limit' key");
169
                }
170
                break;
171
        }
172
    }
173
174
    private function addItem(array $item)
175
    {
176
        switch ($this->batchType) {
177
            case self::COMMAND_UPDATE:
178
                $method = isset($item['multi']) ? 'updateMany' : 'updateOne';
179
180
                $options = [];
181
                if (isset($item['upsert']) && $item['upsert']) {
182
                    $options['upsert'] = true;
183
                }
184
185
                $this->items[] = [$method => [TypeConverter::fromLegacy($item['q']), TypeConverter::fromLegacy($item['u']), $options]];
186
                break;
187
188
            case self::COMMAND_INSERT:
189
                $this->items[] = ['insertOne' => [TypeConverter::fromLegacy($item)]];
190
                break;
191
192
            case self::COMMAND_DELETE:
193
                $method = $item['limit'] === 0 ? 'deleteMany' : 'deleteOne';
194
195
                $this->items[] = [$method => [TypeConverter::fromLegacy($item['q'])]];
196
                break;
197
        }
198
    }
199
}
200