Completed
Push — master ( 6a5328...43881d )
by Mehmet
02:51
created

MongoDB   B

Complexity

Total Complexity 53

Size/Duplication

Total Lines 273
Duplicated Lines 8.79 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 50%

Importance

Changes 14
Bugs 2 Features 2
Metric Value
wmc 53
c 14
b 2
f 2
lcom 1
cbo 1
dl 24
loc 273
ccs 94
cts 188
cp 0.5
rs 7.4757

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getConnection() 0 4 1
A create() 0 4 1
A drop() 0 4 1
A truncate() 0 5 1
A createIndexes() 0 5 1
A insert() 0 11 2
A get() 0 8 2
A singleGet() 0 13 2
B multiGet() 8 24 4
B update() 4 22 4
A delete() 0 13 2
C find() 12 55 13
A query() 0 4 1
B buildFilter() 0 16 5
A buildFilterForOr() 0 15 3
A mergeFilters() 0 13 4
B buildFilterForKeys() 0 24 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MongoDB often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MongoDB, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Soupmix;
4
/*
5
MongoDB Adapter
6
*/
7
8
class MongoDB implements Base
9
{
10
    protected $conn = null;
11
12
    private $dbName = null;
13
14
    private $database = null;
15
16 2
    public function __construct($config, \MongoDB\Client $client)
17
    {
18 2
        $this->dbName = $config['db_name'];
19 2
        $this->conn = $client;
20 2
        $this->database = $this->conn->{$this->dbName};
21 2
    }
22
23
    public function getConnection()
24
    {
25
        return $this->conn;
26
    }
27
28
    public function create($collection, $fields)
29
    {
30
        return $this->database->createCollection($collection);
31
    }
32
33
    public function drop($collection)
34
    {
35
        return $this->database->dropCollection($collection);
36
    }
37
38
    public function truncate($collection)
39
    {
40
        $this->database->dropCollection($collection);
41
        return $this->database->createCollection($collection);
42
    }
43
44
    public function createIndexes($collection, $indexes)
45
    {
46
        $collection = $this->database->selectCollection($collection);
47
        return $collection->createIndexes($indexes);
48
    }
49
50 2
    public function insert($collection, $values)
51
    {
52 2
        $collection = $this->database->selectCollection($collection);
53 2
        $result = $collection->insertOne($values);
54 2
        $docId = $result->getInsertedId();
55 2
        if (is_object($docId)) {
56 2
            return (string) $docId;
57
        }
58
        return null;
59
        
60
    }
61
62 1
    public function get($collection, $docId)
63
    {
64 1
        $collection = $this->database->selectCollection($collection);
65 1
        if (gettype($docId) == "array") {
66
            return $this->multiGet($collection, $docId);
67
        }
68 1
        return $this->singleGet($collection, $docId);
69
    }
70
71 1
    private function singleGet($collection, $docId)
72
    {
73
        $options = [
74 1
            'typeMap' => ['root' => 'array', 'document' => 'array'],
75 1
        ];
76 1
        $filter = ['_id' => new \MongoDB\BSON\ObjectID($docId)];
77 1
        $result = $collection->findOne($filter, $options);
78 1
        if ($result!==null) {
79 1
            $result['id'] = (string) $result['_id'];
80 1
            unset($result['_id']);
81 1
        }
82 1
        return $result;
83
    }
84
85
    private function multiGet($collection, $docIds)
86
    {
87
        $options = [
88
            'typeMap' => ['root' => 'array', 'document' => 'array'],
89
        ];
90
        $idList = [];
91
        foreach ($docIds as $itemId) {
92
            $idList[]=['_id'=>new \MongoDB\BSON\ObjectID($itemId)];
93
        }
94
        $filter = ['$or'=>$idList];
95
        $cursor = $collection->find($filter, $options);
96
        $iterator = new \IteratorIterator($cursor);
97
        $iterator->rewind();
98
        $results=[];
99 View Code Duplication
        while ($doc = $iterator->current()) {
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...
100
            if (isset($doc['_id'])) {
101
                $doc['id'] = (string) $doc['_id'];
102
                unset($doc['_id']);
103
            }
104
            $results[$doc['id']] = $doc;
105
            $iterator->next();
106
        }
107
        return $results;
108
    }
109
110
    public function update($collection, $filters, $values)
111
    {
112
        $collection = $this->database->selectCollection($collection);
113 View Code Duplication
        if (isset($filters['id'])) {
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...
114
            $filters['_id'] = new \MongoDB\BSON\ObjectID($filters['id']);
115
            unset($filters['id']);
116
        }
117
        $query_filters = [];
118
        if ($filters != null) {
119
            $query_filters = ['$and' => self::buildFilter($filters)];
120
        }
121
        $values_set = ['$set' => $values];
122
        try{
123
            $result = $collection->updateMany($query_filters, $values_set);
124
125
        } catch (\Exception $e){
126
            throw new \Exception($e->getMessage());
127
        }
128
129
130
        return $result->getModifiedCount();
131
    }
132
133 2
    public function delete($collection, $filter)
134
    {
135 2
        $collection = $this->database->selectCollection($collection);
136 2
        $filter = self::buildFilter($filter)[0];
137
138 2
        if (isset($filter['id'])) {
139 2
            $filter['_id'] = new \MongoDB\BSON\ObjectID($filter['id']);
140 2
            unset($filter['id']);
141 2
        }
142 2
        $result = $collection->deleteMany($filter);
143
144 2
        return $result->getDeletedCount();
145
    }
146
147 1
    public function find($collection, $filters, $fields = null, $sort = null, $start = 0, $limit = 25)
148
    {
149 1
        $collection = $this->database->selectCollection($collection);
150 1 View Code Duplication
        if (isset($filters['id'])) {
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...
151
            $filters['_id'] = new \MongoDB\BSON\ObjectID($filters['id']);
152
            unset($filters['id']);
153
        }
154 1
        $query_filters = [];
155 1
        if ($filters != null) {
156 1
            $query_filters = ['$and' => self::buildFilter($filters)];
157 1
        }
158 1
        $count = $collection->count($query_filters);
159 1
        if ($count > 0) {
160 1
            $results = [];
161
            $options = [
162 1
                'limit' => (int) $limit,
163 1
                'skip' => (int) $start,
164 1
                'typeMap' => ['root' => 'array', 'document' => 'array'],
165 1
            ];
166 1
            if ($fields!==null) {
167
                $projection = [];
168
                foreach ($fields as $field) {
169
                    if ($field=='id') {
170
                        $field = '_id';
171
                    }
172
                    $projection[$field] = true;
173
                }
174
                $options['projection'] = $projection;
175
            }
176 1
            if ($sort!==null) {
177
                foreach ($sort as $sort_key => $sort_dir) {
178
                    $sort[$sort_key] = ($sort_dir=='desc') ? -1 : 1;
179
                    if ($sort_key=='id') {
180
                        $sort['_id'] = $sort[$sort_key];
181
                        unset($sort['id']);
182
                    }
183
                }
184
                $options['sort'] = $sort;
185
            }
186
187 1
            $cursor = $collection->find($query_filters, $options);
188 1
            $iterator = new \IteratorIterator($cursor);
189 1
            $iterator->rewind();
190 1 View Code Duplication
            while ($doc = $iterator->current()) {
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...
191 1
                if (isset($doc['_id'])) {
192 1
                    $doc['id'] = (string) $doc['_id'];
193 1
                    unset($doc['_id']);
194 1
                }
195 1
                $results[] = $doc;
196 1
                $iterator->next();
197 1
            }
198 1
            return ['total' => $count, 'data' => $results];
199
        }
200
        return ['total' => 0, 'data' => null];
201
    }
202
203
    public function query($collection)
204
    {
205
        return new MongoDBQueryBuilder($collection, $this);
206
    }
207
208 2
    public static function buildFilter($filter)
209
    {
210 2
        $filters = [];
211 2
        foreach ($filter as $key => $value) {
212
            
213 2
            if (strpos($key, '__')!==false) {
214 1
                $filters[] = self::buildFilterForKeys($key, $value);
215
                //$filters = self::mergeFilters($filters, $tmpFilters);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
216 2
            } elseif (strpos($key, '__') === false && is_array($value)) {
217 1
                $filters[]['$or'] = self::buildFilterForOr($value);
218 1
            } else {
219 2
                $filters[][$key] = $value;
220
            }
221 2
        }
222 2
        return $filters;
223
    }
224
225 1
    public static function buildFilterForOr($orValues)
226
    {
227 1
        $filters = [];
228 1
        foreach ($orValues as $filter) {
229 1
            $subKey = array_keys($filter)[0];
230 1
            $subValue = $filter[$subKey];
231 1
            if (strpos($subKey, '__')!==false) {
232 1
                $filters[] = self::buildFilterForKeys($subKey, $subValue);
233
               // $filters = self::mergeFilters($filters, $tmpFilters);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
234 1
            } else {
235
                $filters[][$subKey] = $subValue;
236
            }
237 1
        }
238 1
        return $filters;
239
    }
240
241
    private static function mergeFilters ($filters, $tmpFilters){
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
242
243
        foreach ($tmpFilters as $fKey => $fVals) {
244
            if (isset($filters[$fKey])) {
245
                foreach ($fVals as $fVal) {
246
                    $filters[$fKey][] = $fVal;
247
                }
248
            } else {
249
                $filters[$fKey] = $fVals;
250
            }
251
        }
252
        return $filters;
253
    }
254
255 1
    private static function buildFilterForKeys($key, $value)
256
    {
257 1
        preg_match('/__(.*?)$/i', $key, $matches);
258 1
        $operator = $matches[1];
259
        switch ($operator) {
260 1
            case '!in':
261
                $operator = 'nin';
262
                break;
263 1
            case 'not':
264 1
                $operator = 'ne';
265
                break;
266 1
            case 'wildcard':
267
                $operator = 'regex';
268
                $value = str_replace(array('?'), array('.'), $value);
269
                break;
270 1
            case 'prefix':
271
                $operator = 'regex';
272
                $value = $value.'*';
273
                break;
274
        }
275 1
        $key = str_replace($matches[0], '', $key);
276 1
        $filters= [$key => ['$'.$operator => $value]];
277 1
        return $filters;
278
    }
279
280
}
281