Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
66.67% covered (warning)
66.67%
6 / 9
CRAP
92.50% covered (success)
92.50%
37 / 40
Builder
0.00% covered (danger)
0.00%
0 / 1
66.67% covered (warning)
66.67%
6 / 9
21.19
92.50% covered (success)
92.50%
37 / 40
 pushFilter
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 setKeyFilter
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 setKey
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 pullFilter
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 createRequest
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
9 / 9
 performUpdate
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 addUpdatedAtColumn
0.00% covered (danger)
0.00%
0 / 1
3.02
87.50% covered (success)
87.50%
7 / 8
 freshTimestamp
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 createArrayFilter
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
15 / 15
1<?php
2
3namespace Qmp\Laravel\MongoLow\Query;
4
5use Qmp\Laravel\MongoLow\Exceptions\ArrayFilterMismatchException;
6use Jenssegers\Mongodb\Query\Builder as BaseBuilder;
7use MongoDB\BSON\UTCDateTime;
8
9class Builder extends BaseBuilder
10{
11    /**
12     * Defaulty column name for the "updated_at" field
13     *
14     * @var string
15     */
16    protected $defaultColumnUpdatedAt = 'updated_at';
17
18    /**
19     * Append one or more values to an array with arrayFilter.
20     * @param mixed $column
21     * @param mixed $value
22     * @param array $options
23     * @param bool $unique
24     * @return int
25     */
26    public function pushFilter($column, $value = null, $options = [], $unique = false)
27    {
28        return $this->createRequest($column, '$push', $value, $options, $unique);
29    }
30
31    /**
32     * Add or set a key at position with arrayFilter.
33     * @param mixed $column
34     * @param mixed $value
35     * @param array $options
36     * @param bool $unique
37     * @return int
38     */
39    public function setKeyFilter($column, $key, $value = null, $options = [], $unique = false)
40    {
41        return $this->createRequest($column . '.' . $key, '$set', $value, $options, $unique, false);
42    }
43
44    /**
45     * Set key at position
46     *
47     * @param mixed $column
48     * @param mixed $key
49     * @param mixed $value
50     * @return int
51     */
52    public function setKey($key, $value) {
53        return $this->performUpdate(['$set' => [ $key => $value]]);
54    }
55
56    /**
57     * Remove one or more values from an array with arrayFilter.
58     * @param mixed $column
59     * @param mixed $value
60     * @param array $options
61     * @return int
62     */
63    public function pullFilter($column, $value = null, $options = [])
64    {
65        return $this->createRequest($column, '$pull', $value, $options);
66    }
67
68    /**
69     * Create Request
70     * @param mixed $column
71     * @param string $method
72     * @param mixed $value
73     * @param array $options
74     * @param bool $unique
75     * @return int
76     */
77    protected function createRequest($column, $method, $value = null, $options = [], $unique = false, $allowBatch=true)
78    {
79        [$options, $column] = $this->createArrayFilter($column, $options);
80
81        // Use the addToSet operator in case we only want unique items.
82        $operator = $unique ? '$addToSet' : $method;
83
84        // Check if we are pushing multiple values.
85        $batch = $allowBatch ? (is_array($value) && array_keys($value) === range(0, count($value) - 1)) : false;
86
87        if (is_array($column)) {
88            $query = [$operator => $column];
89        } elseif ($batch) {
90            $query = [$operator => [$column => ['$each' => $value]]];
91        } else {
92            $query = [$operator => [$column => $value]];
93        }
94
95        return $this->performUpdate($query, ['arrayFilters' => $options], true);
96    }
97
98    /**
99     * Perform an update query with update at.
100     *
101     * @param array $query
102     * @param array $options
103     * @param boolean $addUpdatedAt
104     * @return int
105     */
106    protected function performUpdate($query, array $options = [], $addUpdatedAt = false)
107    {
108        if ($addUpdatedAt) {
109            $query = $this->addUpdatedAtColumn($query);
110        }
111
112        return parent::performUpdate($query, $options);
113    }
114
115    /**
116     * Add the "updated at" column to an array of values
117     *
118     * @param array $values
119     * @param mixed $columnName
120     * @param boolean $set
121     * @return array
122     */
123    protected function addUpdatedAtColumn(array $values, $columnName = null, $set = true)
124    {
125        if (is_bool($columnName)) {
126            $set = $columnName;
127        }
128
129        $columnName = $columnName ?? $this->defaultColumnUpdatedAt;
130
131        $date = [$columnName => $this->freshTimestamp()];
132        $updatedAt = $set ? ['$set' => $date] : $date;
133
134        $values = array_merge(
135            $updatedAt,
136            $values
137        );
138
139        return $values;
140    }
141
142    /**
143     * Return a new datetime object
144     *
145     * @return MongoDB\BSON\UTCDateTime
146     */
147    protected function freshTimestamp()
148    {
149        return new UTCDateTime(time() * 1000);
150    }
151
152    /**
153     * Format path and values for ArrayFilter
154     *
155     * @param string $path
156     * @param array $values
157     * @return array
158     */
159    protected function createArrayFilter($column, array $options)
160    {
161        $path = is_array($column) ? key($column) : $column;
162
163        $return = [];
164
165        $countPath = substr_count($path, '$');
166        $countValues = count($options);
167
168        if ($countPath !== $countValues) {
169            throw new ArrayFilterMismatchException($countPath, $countValues);
170        }
171
172        $key = implode('.', array_map(function ($pathSegment) use (&$return, &$options) {
173            if (substr($pathSegment, 0, 1) === '$') {
174                $Uniqsegment = 'a' . uniqid();
175                $return['options'][] = [$Uniqsegment . str_replace('$', '.', $pathSegment) => array_shift($options)];
176                $pathSegment = "$[$Uniqsegment]";
177            }
178            return $pathSegment;
179        }, explode('.', $path)));
180
181
182        $return['column'] = is_array($column) ? [$key => $column[$path]] : $key;
183
184        return array_values($return);
185    }
186}