Completed
Branch feature/split-orm (60a911)
by Anton
03:15
created

DocumentSelector::__debugInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\ODM\Entities;
8
9
use MongoDB\BSON\UTCDateTime;
10
use MongoDB\Collection;
11
use MongoDB\Driver\Cursor;
12
use Spiral\Core\Component;
13
use Spiral\ODM\CompositableInterface;
14
use Spiral\ODM\ODMInterface;
15
use Spiral\Pagination\PaginatorAwareInterface;
16
use Spiral\Pagination\Traits\LimitsTrait;
17
use Spiral\Pagination\Traits\PaginatorTrait;
18
19
/**
20
 * Provides fluent interface to build document selections.
21
 */
22
class DocumentSelector extends Component implements
23
    \Countable,
24
    \IteratorAggregate,
25
    \JsonSerializable,
26
    PaginatorAwareInterface
27
{
28
    use LimitsTrait, PaginatorTrait;
29
30
    /**
31
     * Sort orders.
32
     */
33
    const ASCENDING  = 1;
34
    const DESCENDING = -1;
35
36
    /**
37
     * @var Collection
38
     */
39
    private $collection;
40
41
    /**
42
     * Document class being selected.
43
     *
44
     * @var string
45
     */
46
    private $class;
47
48
    /**
49
     * @var ODMInterface
50
     */
51
    private $odm;
52
53
    /**
54
     * Fields and conditions to query by.
55
     *
56
     * @link http://docs.mongodb.org/manual/tutorial/query-documents/
57
     *
58
     * @var array
59
     */
60
    private $query = [];
61
62
    /**
63
     * Fields to sort.
64
     *
65
     * @var array
66
     */
67
    private $sort = [];
68
69
    /**
70
     * @param Collection   $collection
71
     * @param string       $class
72
     * @param ODMInterface $odm
73
     */
74
    public function __construct(Collection $collection, string $class, ODMInterface $odm)
75
    {
76
        $this->collection = $collection;
77
        $this->class = $class;
78
        $this->odm = $odm;
79
    }
80
81
    /**
82
     * Set additional query, fields will be merged to currently existed request using array_merge.
83
     * Alias for query.
84
     *
85
     * @link http://docs.mongodb.org/manual/tutorial/query-documents/
86
     *
87
     * @see  query()
88
     *
89
     * @param array $query          Fields and conditions to query by.
90
     * @param bool  $normalizeDates When true (default) all DateTime objects will be converted into
91
     *                              MongoDate.
92
     *
93
     * @return self|$this
94
     */
95
    public function find(array $query = [], bool $normalizeDates = true): DocumentSelector
96
    {
97
        return $this->where($query, $normalizeDates);
98
    }
99
100
    /**
101
     * Set additional query, fields will be merged to currently existed request using array_merge.
102
     * Alias for query.
103
     *
104
     * @link http://docs.mongodb.org/manual/tutorial/query-documents/
105
     *
106
     * @see  query()
107
     *
108
     * @param array $query          Fields and conditions to query by.
109
     * @param bool  $normalizeDates When true (default) all DateTime objects will be converted into
110
     *                              MongoDate.
111
     *
112
     * @return self|$this
113
     */
114
    public function where(array $query = [], bool $normalizeDates = true): DocumentSelector
115
    {
116
        if ($normalizeDates) {
117
            $query = $this->normalizeDates($query);
118
        }
119
120
        $this->query = array_merge($this->query, $query);
121
122
        return $this;
123
    }
124
125
    /**
126
     * Sorts the results by given fields.
127
     *
128
     * @link http://www.php.net/manual/en/mongocursor.sort.php
129
     *
130
     * @param array $fields An array of fields by which to sort. Each element in the array has as
131
     *                      key the field name, and as value either 1 for ascending sort, or -1 for
132
     *                      descending sort.
133
     *
134
     * @return self|$this
135
     */
136
    public function sortBy(array $fields): DocumentSelector
137
    {
138
        $this->sort = $fields;
139
140
        return $this;
141
    }
142
143
    /**
144
     * Alias for sortBy.
145
     *
146
     * @param string $field
147
     * @param int    $direction
148
     *
149
     * @return self|$this
150
     */
151
    public function orderBy(string $field, int $direction = self::ASCENDING): DocumentSelector
152
    {
153
        return $this->sortBy($this->sort + [$field => $direction]);
154
    }
155
156
    /**
157
     * Select one document or it's fields from collection.
158
     *
159
     * @param array $query          Fields and conditions to query by. Query will not be added to an
160
     *                              existed query array.
161
     * @param bool  $normalizeDates When true (default) all DateTime objects will be converted into
162
     *                              MongoDate.
163
     *
164
     * @return CompositableInterface|null
165
     */
166
    public function findOne(array $query = [], bool $normalizeDates = true)
167
    {
168
        if ($normalizeDates) {
169
            $query = $this->normalizeDates($query);
170
        }
171
172
        $result = $this->collection->findOne(
173
            array_merge($this->query, $query),
174
            $this->createOptions()
175
        );
176
177
        if (!is_null($result)) {
178
            $result = $this->odm->instantiate($this->class, $result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->odm->instantiate($this->class, $result) on line 178 can also be of type object; however, Spiral\ODM\ODMInterface::instantiate() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
179
        }
180
181
        return $result;
182
    }
183
184
    /**
185
     * Count collection. Attention, this method depends on current values set for limit and offset!
186
     *
187
     * @return int
188
     */
189
    public function count(): int
190
    {
191
        //Create options?
192
        return $this->collection->count(
193
            $this->query,
194
            ['skip' => $this->offset, 'limit' => $this->limit]
195
        );
196
    }
197
198
    /**
199
     * Create cursor with partial selection.
200
     *
201
     * Example: $selector->fetchFields(['name', ...])->toArray();
202
     *
203
     * @param array $fields
204
     *
205
     * @return Cursor
206
     */
207
    public function fetchFields(array $fields): Cursor
208
    {
209
        return $this->createCursor($fields);
210
    }
211
212
    /**
213
     * Fetch all documents.
214
     *
215
     * @return CompositableInterface[]
216
     */
217
    public function fetchAll(): array
218
    {
219
        return $this->getIterator()->fetchAll();
220
    }
221
222
    /**
223
     * Create selection and wrap it using DocumentCursor.
224
     *
225
     * @return DocumentCursor
226
     */
227
    public function getIterator(): DocumentCursor
228
    {
229
        return new DocumentCursor(
230
            $this->createCursor(),
231
            $this->class,
232
            $this->odm
233
        );
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239
    public function jsonSerialize()
240
    {
241
        return $this->fetchAll();
242
    }
243
244
    /**
245
     * @return array
246
     */
247
    public function __debugInfo()
248
    {
249
        return [
250
            'database'   => $this->collection->getDatabaseName(),
251
            'collection' => $this->collection->getCollectionName(),
252
            'query'      => $this->query,
253
            'limit'      => $this->limit,
254
            'offset'     => $this->offset,
255
            'sort'       => $this->sort
256
        ];
257
    }
258
259
    /**
260
     * Destructing.
261
     */
262
    public function __destruct()
263
    {
264
        $this->collection = null;
265
        $this->odm = null;
266
        $this->paginator = null;
267
        $this->query = [];
268
        $this->sort = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $sort.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
269
    }
270
271
    /**
272
     * @param array $fields Fields to be selected (keep empty to select all).
273
     *
274
     * @return Cursor
275
     */
276
    protected function createCursor(array $fields = [])
277
    {
278
        $cursor = $this->collection->find($this->query, $this->createOptions());
279
280
        //todo: this is important place
281
282
        return $cursor;
283
    }
284
285
    /**
286
     * Options to be send to find() method of MongoDB\Collection. Options are based on how selector
287
     * was configured.
288
     **
289
     *
290
     * @return array
291
     */
292
    protected function createOptions(): array
293
    {
294
        return [
295
            'skip'    => $this->offset,
296
            'limit'   => $this->limit,
297
            'sort'    => $this->sort,
298
            'typeMap' => [
299
                'root'     => 'array',
300
                'document' => 'array',
301
                'array'    => 'array'
302
            ]
303
        ];
304
    }
305
306
    /**
307
     * Converts DateTime objects into MongoDatetime.
308
     *
309
     * @param array $query
310
     *
311
     * @return array
312
     */
313
    protected function normalizeDates(array $query): array
314
    {
315
        array_walk_recursive($query, function (&$value) {
316
            if ($value instanceof \DateTime) {
317
                //MongoDate is always UTC, which is good :)
318
                $value = new UTCDateTime($value);
319
            }
320
        });
321
322
        return $query;
323
    }
324
325
    /**
326
     * @return \Interop\Container\ContainerInterface|null
327
     */
328
    protected function iocContainer()
329
    {
330
        if ($this->odm instanceof Component) {
331
            //Forwarding container scope
332
            return $this->odm->iocContainer();
333
        }
334
335
        return parent::iocContainer();
336
    }
337
}