Completed
Push — master ( beb1f2...8ae23c )
by Matthew
06:13
created

AlertStatisticsLoader::readAlertHistory()   C

Complexity

Conditions 10
Paths 144

Size

Total Lines 67
Code Lines 37

Duplication

Lines 6
Ratio 8.96 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
c 3
b 0
f 2
dl 6
loc 67
rs 5.7576
cc 10
eloc 37
nc 144
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Ps2alerts\Api\Loader\Statistics;
4
5
use Ps2alerts\Api\Loader\Statistics\AbstractStatisticsLoader;
6
use Ps2alerts\Api\QueryObjects\QueryObject;
7
use Ps2alerts\Api\Repository\AlertRepository;
8
use Ps2alerts\Api\Loader\Metrics\MapMetricsLoader;
9
use Ps2alerts\Api\Validator\AlertInputValidator;
10
use Ps2alerts\Api\Helper\DataFormatterHelper;
11
12
class AlertStatisticsLoader extends AbstractStatisticsLoader
13
{
14
    /**
15
     * @var \Ps2alerts\Api\Repository\AlertRepository
16
     */
17
    protected $repository;
18
19
    /**
20
     * @var \Ps2alerts\Api\Loader\Metrics\MapMetricsLoader
21
     */
22
    protected $mapLoader;
23
24
    /**
25
     * @var \Ps2alerts\Api\Helper\DataFormatterHelper
26
     */
27
    protected $dataFormatter;
28
29
    /**
30
     * Construct
31
     *
32
     * @param \Ps2alerts\Api\Repository\AlertRepository       $repository
33
     * @param \Ps2alerts\Api\Loader\Metrics\MapMetricsLoader  $mapLoader
34
     * @param \Ps2alerts\Api\Helper\DataFormatter             $dataFormatter
35
     */
36
    public function __construct(
37
        AlertRepository     $repository,
38
        MapMetricsLoader    $mapLoader,
39
        DataFormatterHelper $dataFormatter
40
    ) {
41
        $this->repository    = $repository;
42
        $this->mapLoader     = $mapLoader;
43
        $this->dataFormatter = $dataFormatter;
44
45
        $this->setCacheNamespace('Statistics');
46
        $this->setType('Alerts');
47
    }
48
49
    /**
50
     * Read total counts for alerts
51
     *
52
     * @param  array $post
53
     *
54
     * @return array
55
     */
56
    public function readTotals(array $post)
57
    {
58
        $redisKey = "{$this->getCacheNamespace()}:{$this->getType()}:Totals";
59
        $redisKey = $this->appendRedisKey($post, $redisKey);
60
        $post     = $this->processPostVars($post);
61
62
        if ($this->checkRedis($redisKey)) {
63
            return $this->getFromRedis($redisKey);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getFromRedis($redisKey); (string) is incompatible with the return type documented by Ps2alerts\Api\Loader\Sta...sticsLoader::readTotals of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
64
        }
65
66
        $queryObject = new QueryObject;
67
        $queryObject = $this->setupQueryObject($queryObject, $post);
68
69
        $queryObject->addSelect('COUNT(ResultID) AS COUNT');
70
71
        if ($this->checkRedis($redisKey)) {
72
            return $this->getFromRedis($redisKey);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getFromRedis($redisKey); (string) is incompatible with the return type documented by Ps2alerts\Api\Loader\Sta...sticsLoader::readTotals of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
73
        }
74
75
        $this->setCacheExpireTime(900); // 15 mins
76
77
        return $this->cacheAndReturn(
78
            $this->repository->read($queryObject),
79
            $redisKey
80
        );
81
    }
82
83
    /**
84
     * Retrieves all zone totals and caches as required
85
     *
86
     * @return array
87
     */
88
    public function readZoneTotals()
89
    {
90
        $masterRedisKey = "{$this->getCacheNamespace()}:{$this->getType()}:Totals:Zones";
91
92
        if ($this->checkRedis($masterRedisKey)) {
93
            return $this->getFromRedis($masterRedisKey);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getFromRedis($masterRedisKey); (string) is incompatible with the return type documented by Ps2alerts\Api\Loader\Sta...sLoader::readZoneTotals of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
94
        }
95
96
        $servers  = [1,10,13,17,25,1000,2000];
97
        $zones    = [2,4,6,8];
98
        $factions = ['vs','nc','tr','draw'];
99
100
        $results = [];
101
        $this->setCacheExpireTime(3600); // 1 Hour
102
103
        // Dat loop yo
104
        foreach ($servers as $server) {
105
            foreach ($zones as $zone) {
106
                foreach ($factions as $faction) {
107
                    $results[$server][$zone][$faction] = $this->getZoneStats($server, $zone, $faction);
108
                }
109
            }
110
        }
111
112
        // Commit to Redis
113
        return $this->cacheAndReturn(
114
            $results,
115
            $masterRedisKey
116
        );
117
    }
118
119
    /**
120
     * Gets all information regarding zone victories out of the DB and caches as
121
     * required
122
     *
123
     * @see readZoneTotals()
124
     *
125
     * @param  integer $server
126
     * @param  integer $zone
127
     * @param  integer $faction
128
     *
129
     * @return array
130
     */
131
    public function getZoneStats($server, $zone, $faction)
132
    {
133
        $redisKey = "{$this->getCacheNamespace()}:{$this->getType()}:Totals:Zones";
134
        $redisKey .= ":{$server}:{$zone}:{$faction}";
135
136
        if ($this->checkRedis($redisKey)) {
137
            return $this->getFromRedis($redisKey);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getFromRedis($redisKey); (string) is incompatible with the return type documented by Ps2alerts\Api\Loader\Sta...icsLoader::getZoneStats of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
138
        }
139
140
        // Fire a set of queries to build the object required
141
        $queryObject = new QueryObject;
142
        $queryObject->addSelect('COUNT(ResultID) AS COUNT');
143
        $queryObject->addWhere([
144
            'col'   => 'ResultServer',
145
            'value' => $server
146
        ]);
147
        $queryObject->addWhere([
148
            'col'   => 'ResultAlertCont',
149
            'value' => $zone
150
        ]);
151
        $queryObject->addWhere([
152
            'col'   => 'ResultWinner',
153
            'value' => $faction
154
        ]);
155
156
        // Commit to Redis
157
        return $this->cacheAndReturn(
158
            $this->repository->read($queryObject)[0]["COUNT"],
159
            $redisKey
160
        );
161
    }
162
163
    /**
164
     * Generates the data required for History Summaries
165
     *
166
     * @param  array $post
167
     *
168
     * @return array
169
     */
170
    public function readHistorySummary(array $post)
171
    {
172
        $redisKey = "{$this->getCacheNamespace()}:{$this->getType()}:HistorySummary";
173
        $redisKey = $this->appendRedisKey($post, $redisKey);
174
        $post     = $this->processPostVars($post);
175
176
        $queryObject = new QueryObject;
177
        $queryObject = $this->setupQueryObject($queryObject, $post);
178
        $queryObject->addSelect('FROM_UNIXTIME(ResultEndTime) AS ResultEndTime');
179
        $queryObject->addSelect('ResultWinner');
180
        $queryObject->addWhere([
181
            'col'   => 'InProgress',
182
            'value' => 0
183
        ]);
184
        $queryObject->setLimit('unlimited');
185
186
        $minDate = '2014-10-29'; // Beginning of tracking
187
        $maxDate = date('Y-m-d'); // Today unless set
188
189
        // If there is a minimum date set
190 View Code Duplication
        if (! empty($post['wheres']['morethan']['ResultEndTime'])) {
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
            if (is_integer($post['wheres']['morethan']['ResultEndTime'])) {
192
                $minDate = date('Y-m-d', $post['wheres']['morethan']['ResultEndTime']);
193
            } else {
194
                $minDate = date('Y-m-d', strtotime($post['wheres']['morethan']['ResultEndTime']));
195
            }
196
        }
197
198
        // If there is a maximum date set
199 View Code Duplication
        if (! empty($post['wheres']['lessthan']['ResultEndTime'])) {
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...
200
            if (is_integer($post['wheres']['lessthan']['ResultEndTime'])) {
201
                $maxDate = date('Y-m-d', $post['wheres']['lessthan']['ResultEndTime']);
202
            } else {
203
                $maxDate = date('Y-m-d', strtotime($post['wheres']['lessthan']['ResultEndTime']));
204
            }
205
        }
206
207
        $redisKey .= "/min-{$minDate}/max-{$maxDate}";
208
209
        if ($this->checkRedis($redisKey)) {
210
            return $this->getFromRedis($redisKey);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->getFromRedis($redisKey); (string) is incompatible with the return type documented by Ps2alerts\Api\Loader\Sta...der::readHistorySummary of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
211
        }
212
213
        // Get the data to parse
214
        $alerts = $this->repository->read($queryObject);
215
216
        // Generate the range of dates
217
        $dates = $this->dataFormatter->createDateRangeArray($minDate, $maxDate);
218
        $dateRange = [];
219
220
        // Generates the victory totals for each date
221
        foreach ($dates as $date) {
222
            $dateRange[$date] = [
223
                'vs'   => 0,
224
                'nc'   => 0,
225
                'tr'   => 0,
226
                'draw' => 0
227
            ];
228
        }
229
230
        // Calculate metrics
231
        foreach ($alerts as $alert) {
232
            $date = date('Y-m-d', strtotime($alert['ResultEndTime']));
233
            $winner = strtolower($alert['ResultWinner']);
234
235
            $dateRange[$date][$winner]++;
236
        }
237
238
        // Commit to Redis
239
        return $this->cacheAndReturn(
240
            $dateRange,
241
            $redisKey
242
        );
243
    }
244
245
    /**
246
     * Reds the alert history based off two dates and other filters
247
     *
248
     * @param  array  $post [description]
249
     *
250
     * @return array
251
     */
252
    public function readAlertHistory(array $post)
253
    {
254
        $minDate = date('U', strtotime("-24 hours"));
255
        $maxDate = date('U');
256
257
        if (! empty($post['minDate'])) {
258
            $minDate = date('U', $post['minDate']);
259
        }
260
261
        if (! empty($post['maxDate'])) {
262
            $maxDate = date('U', $post['maxDate']);
263
        }
264
265
        $queryObject = new QueryObject;
266
        $queryObject->addWhere([
267
            'col'   => 'ResultEndTime',
268
            'op'    => '>',
269
            'value' => $minDate
270
        ]);
271
        $queryObject->addWhere([
272
            'col'   => 'ResultEndTime',
273
            'op'    => '<',
274
            'value' => $maxDate
275
        ]);
276
277 View Code Duplication
        if (! empty($post['server'])) {
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...
278
            $queryObject->addWhere([
279
                'col'   => 'ResultServer',
280
                'value' => $post['server']
281
            ]);
282
        }
283
284
        if (! empty($post['faction'])) {
285
            $queryObject->addWhere([
286
                'col'   => 'ResultWinner',
287
                'value' => $post['faction']
288
            ]);
289
        }
290
291
        // Enforce a limit of 50 returns to prevent overload
292
        $queryObject->setLimit(50);
293
        $queryObject->setOrderBy('result');
294
        $queryObject->setOrderByDirection('desc');
295
296
        if (! empty($post['orderBy'])) {
297
            if ($post['orderBy'] === 'asc' || $post['orderBy'] === 'desc') {
298
                $queryObject->setOrderByDirection($post['orderBy']);
299
            }
300
        }
301
302
        $alerts = $this->repository->read($queryObject);
303
304
        // Grab the map information for each alert returned
305
        foreach ($alerts as $key => $alert) {
306
            $alertKey = "Alert:{$alert['ResultID']}";
307
308
            // Cache the alert if it's not been seen before
309
            if ($this->checkRedis($alertKey) === false) {
310
                $this->cacheAndReturn($alert, $alertKey);
311
            }
312
313
            // Cache the map result so we don't have to get it every time and set
314
            $alerts[$key]['map'] = $this->mapLoader->readLatest($alert['ResultID']);
315
        }
316
317
        return $this->cacheAndReturn($alerts);
318
    }
319
}
320