Completed
Pull Request — master (#118)
by Sascha-Oliver
03:31
created

MongoWriteBatch::execute()   C

Complexity

Conditions 11
Paths 65

Size

Total Lines 65
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
cc 11
eloc 40
c 4
b 0
f 1
nc 65
nop 1
dl 0
loc 65
rs 5.9999

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 17.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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
if (class_exists('MongoWriteBatch', false)) {
17
    return;
18
}
19
20
use Alcaeus\MongoDbAdapter\TypeConverter;
21
use Alcaeus\MongoDbAdapter\ExceptionConverter;
22
use Alcaeus\MongoDbAdapter\Helper\WriteConcernConverter;
23
24
/**
25
 * MongoWriteBatch allows you to "batch up" multiple operations (of same type)
26
 * and shipping them all to MongoDB at the same time. This can be especially
27
 * useful when operating on many documents at the same time to reduce roundtrips.
28
 *
29
 * @see http://php.net/manual/en/class.mongowritebatch.php
30
 */
31
class MongoWriteBatch
32
{
33
    use WriteConcernConverter;
34
35
    const COMMAND_INSERT = 1;
36
    const COMMAND_UPDATE = 2;
37
    const COMMAND_DELETE = 3;
38
39
    /**
40
     * @var MongoCollection
41
     */
42
    private $collection;
43
44
    /**
45
     * @var int
46
     */
47
    private $batchType;
48
49
    /**
50
     * @var array
51
     */
52
    private $writeOptions;
53
54
    /**
55
     * @var array
56
     */
57
    private $items = [];
58
59
    /**
60
     * Creates a new batch of write operations
61
     *
62
     * @see http://php.net/manual/en/mongowritebatch.construct.php
63
     * @param MongoCollection $collection
64
     * @param int $batchType
65
     * @param array $writeOptions
66
     */
67
    protected function __construct(MongoCollection $collection, $batchType, $writeOptions)
68
    {
69
        $this->collection = $collection;
70
        $this->batchType = $batchType;
71
        $this->writeOptions = $writeOptions;
72
    }
73
74
    /**
75
     * Adds a write operation to a batch
76
     *
77
     * @see http://php.net/manual/en/mongowritebatch.add.php
78
     * @param array|object $item
79
     * @return boolean
80
     */
81
    public function add($item)
82
    {
83
        if (is_object($item)) {
84
            $item = (array)$item;
85
        }
86
87
        $this->validate($item);
88
        $this->addItem($item);
89
90
        return true;
91
    }
92
93
    /**
94
     * Executes a batch of write operations
95
     *
96
     * @see http://php.net/manual/en/mongowritebatch.execute.php
97
     * @param array $writeOptions
98
     * @return array
99
     */
100
    final public function execute(array $writeOptions = [])
101
    {
102
        $writeOptions += $this->writeOptions;
103
        if (! count($this->items)) {
104
            return ['ok' => true];
105
        }
106
107
        if (isset($writeOptions['j'])) {
108
            trigger_error('j parameter is not supported', E_WARNING);
109
        }
110
        if (isset($writeOptions['fsync'])) {
111
            trigger_error('fsync parameter is not supported', E_WARNING);
112
        }
113
114
        $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...
115
        if (isset($writeOptions['ordered'])) {
116
            $options['ordered'] = $writeOptions['ordered'];
117
        }
118
119
        $collection = $this->collection->getCollection();
120
121
        try {
122
            $result = $collection->BulkWrite($this->items, $options);
123
        } catch (\MongoDB\Driver\Exception\BulkWriteException $e) {
124
            throw ExceptionConverter::toLegacy($e);
125
        }
126
127
        $this->items = [];
128
129
        switch ($this->batchType) {
130
            case self::COMMAND_UPDATE:
131
                $upsertedIds = [];
132
                foreach ($result->getUpsertedIds() as $index => $id) {
133
                    $upsertedIds[] = [
134
                        'index' => $index,
135
                        '_id' => TypeConverter::toLegacy($id)
136
                    ];
137
                }
138
139
                $result = [
140
                    'nMatched' => $result->getMatchedCount(),
141
                    'nModified' => $result->getModifiedCount(),
142
                    'nUpserted' => $result->getUpsertedCount(),
143
                    'ok' => true,
144
                ];
145
146
                if (count($upsertedIds)) {
147
                    $result['upserted'] = $upsertedIds;
148
                }
149
150
                return $result;
151
152
            case self::COMMAND_DELETE:
153
                return [
154
                    'nRemoved' => $result->getDeletedCount(),
155
                    'ok' => true,
156
                ];
157
158
            case self::COMMAND_INSERT:
159
                return [
160
                    'nInserted' => $result->getInsertedCount(),
161
                    'ok' => true,
162
                ];
163
        }
164
    }
165
166
    private function validate(array $item)
167
    {
168
        switch ($this->batchType) {
169 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...
170
                if (! isset($item['q'])) {
171
                    throw new Exception("Expected \$item to contain 'q' key");
172
                }
173
                if (! isset($item['u'])) {
174
                    throw new Exception("Expected \$item to contain 'u' key");
175
                }
176
                break;
177
178 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...
179
                if (! isset($item['q'])) {
180
                    throw new Exception("Expected \$item to contain 'q' key");
181
                }
182
                if (! isset($item['limit'])) {
183
                    throw new Exception("Expected \$item to contain 'limit' key");
184
                }
185
                break;
186
        }
187
    }
188
189
    private function addItem(array $item)
190
    {
191
        switch ($this->batchType) {
192
            case self::COMMAND_UPDATE:
193
                $method = isset($item['multi']) ? 'updateMany' : 'updateOne';
194
195
                $options = [];
196
                if (isset($item['upsert']) && $item['upsert']) {
197
                    $options['upsert'] = true;
198
                }
199
200
                $this->items[] = [$method => [TypeConverter::fromLegacy($item['q']), TypeConverter::fromLegacy($item['u']), $options]];
201
                break;
202
203
            case self::COMMAND_INSERT:
204
                $this->items[] = ['insertOne' => [TypeConverter::fromLegacy($item)]];
205
                break;
206
207
            case self::COMMAND_DELETE:
208
                $method = $item['limit'] === 0 ? 'deleteMany' : 'deleteOne';
209
210
                $this->items[] = [$method => [TypeConverter::fromLegacy($item['q'])]];
211
                break;
212
        }
213
    }
214
}
215