Passed
Push — master ( 7d9c3d...f4aea8 )
by Alexander
01:26
created

MongoDB::idReplaceConditions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace alkemann\h2l\data;
4
5
use alkemann\h2l\exceptions\ConnectionError;
6
use alkemann\h2l\interfaces\Source;
7
use alkemann\h2l\Log;
8
use MongoDB\BSON\ObjectId;
9
use MongoDB\Client;
10
use MongoDB\Collection;
11
use MongoDB\Driver\Exception\RuntimeException;
12
use MongoDB\Model\BSONDocument;
13
14
/**
15
 * Class MongoDb
16
 *
17
 * @package alkemann\h2l\data
18
 */
19
class MongoDB implements Source
20
{
21
    public static $operators = [
22
        '$all',
23
        '$gt',
24
        '$gte',
25
        '$in',
26
        '$lt',
27
        '$lte',
28
        '$ne',
29
        '$nin',
30
        '$or',
31
        '$and',
32
        '$not',
33
        '$nor',
34
        '$exists',
35
        '$mod',
36
        'type',
37
        '$elemMatch',
38
        '$size'
39
    ];
40
41
    protected $config = [];
42
    protected $client = null;
43
44
    /**
45
     * @param array $config
46
     */
47
    public function __construct(array $config = [])
48
    {
49
        $defaults = [
50
            'host' => 'localhost',
51
            'port' => 27017,
52
            'db' => 'default'
53
        ];
54
        $this->config = $config + $defaults;
55
    }
56
57
    /**
58
     * @param string $collection
59
     * @return Collection
60
     * @throws ConnectionError un unable to connect
61
     */
62
    private function collection(string $collection): Collection
63
    {
64
        if ($this->client == null) {
65
            $host = $this->config['host'];
66
            $port = $this->config['port'];
67
            $options = $this->config;
68
            unset($options['host']);
69
            unset($options['port']);
70
            unset($options['db']);
71
            try {
72
                $this->client = new Client("mongodb://{$host}:{$port}", $options);
73
                $this->client->listDatabases(); // @TODO skip this extra call to trigger connection error fast?
74
            } catch (RuntimeException $e) {
75
                throw new ConnectionError("Unable to connect to {$host}:{$port} : " . $e->getMessage());
76
            }
77
        }
78
        $db = $this->config['db'];
79
        return $this->client->selectCollection($db, $collection);
80
    }
81
82
    /**
83
     * @param string $id
84
     * @return ObjectId
85
     */
86
    public static function id(string $id): ObjectId
87
    {
88
        return new ObjectId($id);
89
    }
90
91
    /**
92
     * @param mixed $query
93
     * @param array $params
94
     * @throws \Exception if called as not implemented
95
     */
96
    public function query($query, array $params = [])
97
    {
98
        throw new \Exception("Query method is not implemented for MongDB");
99
    }
100
101
    /**
102
     * @param string $collection_name
103
     * @param array $conditions
104
     * @param array $options
105
     * @return array|null
106
     */
107
    public function one(string $collection_name, array $conditions, array $options = []): ?array
108
    {
109
        $collection = $this->collection($collection_name);
110
        $conditions = $this->idReplaceConditions($conditions);
111
        $result = $collection->findOne($conditions, $options);
112
        if ($result === null) {
113
            return null;
114
        }
115
        return $result instanceof BSONDocument ? $this->out($result) : $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result instanceo...>out($result) : $result could return the type object which is incompatible with the type-hinted return array|null. Consider adding an additional type-check to rule them out.
Loading history...
116
    }
117
118
    /**
119
     * @param array $conditions
120
     * @return array
121
     */
122
    private function idReplaceConditions(array $conditions): array
123
    {
124
        if (array_key_exists('id', $conditions)) {
125
            $id = new ObjectId($conditions['id']);
126
            unset($conditions['id']);
127
            $conditions['_id'] = $id;
128
        }
129
        return $conditions;
130
    }
131
132
    /**
133
     * @TODO keep the BSON object?
134
     * @param BSONDocument $document
135
     * @return array
136
     */
137
    private function out(BSONDocument $document): array
138
    {
139
        $a = $document->getArrayCopy();
140
        $a['id'] = "{$document->_id}";
0 ignored issues
show
Bug introduced by
The property _id does not seem to exist on MongoDB\Model\BSONDocument.
Loading history...
141
        unset($a['_id']);
142
        return $a;
143
    }
144
145
    /**
146
     * @param string $collection_name
147
     * @param array $conditions
148
     * @param array $options
149
     * @return \Generator
150
     */
151
    public function find(string $collection_name, array $conditions, array $options = []): \Generator
152
    {
153
        $collection = $this->collection($collection_name);
154
        $conditions = $this->idReplaceConditions($conditions);
155
        $cursor = $collection->find($conditions, $options);
156
        foreach ($cursor as $document) {
157
            yield $this->out($document);
158
        }
159
    }
160
161
    /**
162
     * @param string $collection_name
163
     * @param array $conditions
164
     * @param array $data
165
     * @param array $options
166
     * @return int
167
     */
168
    public function update(string $collection_name, array $conditions, array $data, array $options = []): int
169
    {
170
        $collection = $this->collection($collection_name);
171
        $result = $collection->updateMany($conditions, $data, $options);
172
        if ($result->isAcknowledged() === false) {
173
            // Throw exception or error?
174
            return 0;
175
        }
176
        return $result->getModifiedCount() ?? 0;
177
    }
178
179
    /**
180
     * @param string $collection_name
181
     * @param array $data
182
     * @param array $options
183
     * @return null|ObjectId
184
     */
185
    public function insert(string $collection_name, array $data, array $options = []): ?ObjectId
186
    {
187
        $collection_name = $this->collection($collection_name);
188
        $result = $collection_name->insertOne($data, $options);
189
        if ($result->isAcknowledged() === false) {
190
            // Throw exception or error?
191
            return null;
192
        }
193
        if ($result->getInsertedCount() != 1) {
194
            Log::error("Failed to insert!");
195
            return null;
196
        }
197
        $id = $result->getInsertedId();
198
        return $id instanceof ObjectId ? $id : null;
199
    }
200
201
    /**
202
     * @param string $collection_name
203
     * @param array $conditions
204
     * @param array $options
205
     * @return int
206
     */
207
    public function delete(string $collection_name, array $conditions, array $options = []): int
208
    {
209
        $collection_name = $this->collection($collection_name);
210
        $result = $collection_name->deleteMany($conditions, $options);
211
        if ($result->isAcknowledged() === false) {
212
            // Throw exception or error?
213
            return 0;
214
        }
215
        return $result->getDeletedCount();
216
    }
217
}
218