Completed
Push — master ( c155dc...2cbc2d )
by Ash
02:51
created

APAnalytics::canViewAnalytic()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 16
c 0
b 0
f 0
nc 7
nop 3
dl 0
loc 31
rs 8.4444
1
<?php
2
3
namespace AshPowell\APAnalytics;
4
5
use App\User;
0 ignored issues
show
Bug introduced by
The type App\User was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use AshPowell\APAnalytics\Jobs\Track;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Str;
9
use InvalidArgumentException;
10
use MongoDB\Driver\Cursor;
11
use MongoDB\Model\BSONDocument;
12
13
class APAnalytics
14
{
15
    protected $connection;
16
    protected $namespace;
17
18
    /**
19
     * Instantiate a new instance.
20
     *
21
     * @return void
22
     */
23
    public function __construct()
24
    {
25
        $this->connection = config('apanalytic.db_connection', 'mongodb');
26
        $this->namespace  = config('apanalytic.namespace', '\App\\');
27
    }
28
29
    /**
30
     * Track the Analytic.
31
     *
32
     * @return void
33
     * @param  mixed      $collection
34
     * @param  mixed      $items
35
     * @param  null|mixed $userId
36
     * @param  mixed      $params
37
     */
38
    public function track($collection, $items, $userId = null, $params = [])
39
    {
40
        Track::dispatch($collection, $items, $userId, $params);
41
42
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type void.
Loading history...
43
    }
44
45
    public function update($collection, $item, $params)
46
    {
47
        Track::dispatch($collection, $item, null, $params, 'update');
48
49
        return true;
50
    }
51
52
    /**
53
     * Get the Analytics.
54
     *
55
     * @param mixed      $collection
56
     * @param null|mixed $timeframe
57
     * @param null|mixed $filters
58
     * @param mixed      $interval
59
     * @param mixed      $groupBy
60
     */
61
    public function show($collection, $interval = 'count', $timeframe = null, $filters = null, $groupBy = null)
62
    {
63
        $start          = $timeframe ? Arr::get($timeframe, 'start') : null;
64
        $end            = $timeframe ? Arr::get($timeframe, 'end') : null;
65
        $matchArray     = [];
66
        $filters        = valid_json($filters) ? json_decode($filters) : $filters;
67
        $intervalFormat = '%Y-%m-%dT%H';
68
        $aggregate      = [];
69
        $model          = $this->namespace.Str::studly(Str::singular($collection)).'Analytic';
70
71
        if (! class_exists($model)) {
72
            throw new InvalidArgumentException("Model {$model} does not exist.");
73
        }
74
75
        if ($filters) {
76
            foreach ($filters as $filter) {
77
                if (is_array($filter)) {
78
                    $matchArray = array_merge($matchArray, $filter);
79
                } else {
80
                    $propertyValue = $filter->property_value;
81
82
                    if (is_numeric($propertyValue)) {
83
                        $propertyValue = (int) $propertyValue;
84
                    }
85
86
                    $matchArray = array_merge($matchArray, [$filter->property_name => $propertyValue]);
87
                }
88
            }
89
        }
90
91
        abort_unless(auth()->check() && $this->canViewAnalytic($model, $matchArray, auth()->user()), 403, 'You dont have permission to view these analytics');
92
93
        if ($start) {
94
            $matchArray['created_at']['$gte'] = mongoTime($start);
95
        }
96
97
        if ($end) {
98
            $matchArray['created_at']['$lt'] = mongoTime($end);
99
        }
100
101
        if ($matchArray) {
102
            $aggregate[] = ['$match' => $matchArray];
103
        }
104
105
        if ($interval != 'count') {
106
            if ($interval == 'daily') {
107
                $intervalFormat = '%Y-%m-%d';
108
            }
109
110
            if ($interval == 'weekly') {
111
                $intervalFormat = '%Y-%U';
112
            }
113
114
            if ($interval == 'monthly' || $interval == 'growth') {
115
                $intervalFormat = '%Y-%m';
116
            }
117
118
            $aggregate[] =  [
119
                '$group' => [
120
                    '_id' => [
121
                        '$dateToString' => ['date' => '$created_at', 'format' => $intervalFormat],
122
                    ],
123
                    'count' => [
124
                        '$sum' => 1,
125
                    ],
126
                    'created_at' => [
127
                        '$last' => '$created_at',
128
                    ],
129
                ],
130
            ];
131
132
            $aggregate[] = ['$sort' => ['created_at' => 1]];
133
134
            $aggregate[] = [
135
                '$project' => [
136
                    '_id'        => 0,
137
                    'created_at' => 1,
138
                    'count'      => 1,
139
                ],
140
            ];
141
        }
142
143
        if ($interval == 'count' && $groupBy != null) {
144
            $nested = Str::contains($groupBy, '.');
145
146
            if ($nested) {
147
                $aggregate[] = ['$unwind' => '$'.Str::before($groupBy, '.')];
148
            }
149
150
            $aggregate[] =  [
151
                '$group' => [
152
                    '_id'   => '$'.$groupBy,
153
                    'count' => [
154
                        '$sum' => 1,
155
                    ],
156
                ],
157
            ];
158
159
            $aggregate[] = ['$sort' => ['count' => 1]];
160
161
            $aggregate[] = [
162
                '$project' => [
163
                    '_id'   => 0,
164
                    Str::after($groupBy, '.')  => '$_id',
165
                    'count' => 1,
166
                ],
167
            ];
168
        }
169
170
        $data = $model::raw(function ($collection) use ($matchArray, $interval, $aggregate, $groupBy) {
171
            if ($interval == 'count' && ! $groupBy) {
172
                return $collection->count($matchArray);
173
            }
174
175
            if ($aggregate) {
176
                return $collection->aggregate($aggregate, ['allowDiskUse' => true]);
177
            }
178
        });
179
180
        return $data;
181
    }
182
183
    /**
184
     * Convert the Cursor to Laravel Models.
185
     *
186
     * @return void
187
     * @param  mixed      $data
188
     * @param  null|mixed $model
189
     */
190
    private function toModels($data, $model = null)
0 ignored issues
show
Unused Code introduced by
The method toModels() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
191
    {
192
        if (! $model) {
193
            $model = '\Jenssegers\Mongodb\Eloquent\Model';
194
        }
195
196
        if (! class_exists($model)) {
197
            throw new InvalidArgumentException("Model {$model} does not exist.");
198
        }
199
200
        if ($data instanceof Cursor) {
201
            // Convert MongoCursor results to a collection of models.
202
            $data = iterator_to_array($data, false);
203
204
            return $model::hydrate($data);
205
        } elseif ($data instanceof BSONDocument) {
206
            // Convert Mongo BSONDocument to a single object.
207
            $data = $data::getArrayCopy();
0 ignored issues
show
Bug Best Practice introduced by
The method ArrayObject::getArrayCopy() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

207
            /** @scrutinizer ignore-call */ 
208
            $data = $data::getArrayCopy();
Loading history...
208
209
            return $model::newFromBuilder((array) $data);
210
        } elseif (is_array($data) && array_key_exists('_id', $data)) {
211
            // The result is a single object.
212
            return $model::newFromBuilder((array) $data);
213
        }
214
215
        return $data;
216
    }
217
218
    /**
219
     * Check specified user has permission to see these analytics.
220
     *
221
     * @param array $filterArray
222
     * @param User  $user
223
     * @param mixed $analyticModel
224
     *
225
     * @return bool
226
     */
227
    private function canViewAnalytic($analyticModel, $filterArray, User $user = null)
228
    {
229
        if (app()->runningInConsole()) {
0 ignored issues
show
introduced by
The method runningInConsole() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

229
        if (app()->/** @scrutinizer ignore-call */ runningInConsole()) {
Loading history...
230
            return true;
231
        }
232
233
        if (! $user) {
234
            return false;
235
        }
236
237
        $modelsToCheck = config('apanalytics.models_require_ownership');
238
239
        if (count($modelsToCheck)) {
240
            foreach ($modelsToCheck as $model) {
241
                $modelName  = Str::studly(Str::singular($model));
242
                $modelId    = Arr::get($filterArray, strtolower($modelName).'.id');
243
                $modelClass = $this->namespace.$modelName;
244
245
                if ($modelId) {
246
                    $model = $modelClass::find($modelId);
247
248
                    if ($model && ! $model->canViewAnalytic($user)) {
249
                        return false;
250
                    }
251
                }
252
            }
253
254
            return true;
255
        }
256
257
        return (new $analyticModel)->canViewCollection($user);
258
    }
259
}
260