Completed
Pull Request — master (#153)
by Deven
02:39
created

getIncidentsWithDifferentStacktrace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 0
crap 1
1
<?php
2
3
/* vim: set expandtab sw=4 ts=4 sts=4: */
4
/**
5
 * Report model representing a group of incidents.
6
 *
7
 * phpMyAdmin Error reporting server
8
 * Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
9
 *
10
 * Licensed under The MIT License
11
 * For full copyright and license information, please see the LICENSE.txt
12
 * Redistributions of files must retain the above copyright notice.
13
 *
14
 * @copyright Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
15
 * @license   https://opensource.org/licenses/mit-license.php MIT License
16
 *
17
 * @see      https://www.phpmyadmin.net/
18
 */
19
20
namespace App\Model\Table;
21
22
use Cake\Model\Model;
23
use Cake\ORM\Table;
24
use Cake\ORM\TableRegistry;
25
use Cake\Routing\Router;
26
27
/**
28
 * A report a representing a group of incidents.
29
 */
30
class ReportsTable extends Table
31
{
32
    /**
33
     * @var array
34
     *
35
     * @see http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany
36
     * @see Cake::Model::$hasMany
37
     */
38
    public $hasMany = array(
39
        'Incidents' => array(
40
            'dependant' => true,
41
        ),
42
    );
43
44
    /**
45
     * @var array
46
     *
47
     * @see http://book.cakephp.org/2.0/en/models/model-attributes.html#validate
48
     * @see http://book.cakephp.org/2.0/en/models/data-validation.html
49
     * @see Model::$validate
50
     */
51
    public $validate = array(
52
        'error_message' => array(
53
            'rule' => 'notEmpty',
54
            'required' => true,
55
        ),
56
    );
57
58
    /**
59
     * List of valid finder method options, supplied as the first parameter to find().
60
     *
61
     * @var array
62
     *
63
     * @see Model::$findMethods
64
     */
65
    public $findMethods = array(
66
        'allDataTable' => true,
67
        'arrayList' => true,
68
    );
69
70
    /**
71
     * List of valid finder method options, supplied as the first parameter to find().
72
     *
73
     * @var array
74
     */
75
    public $status = array(
76
        'new' => 'New',
77
        'invalid' => 'Invalid',
78
        'resolved' => 'Resolved'
79
    );
80
81 15
    public function initialize(array $config)
82
    {
83 15
        $this->hasMany('Incidents', array(
84 15
            'dependent' => true,
85
        ));
86 15
    }
87
88
    /**
89
     * Retrieves the incident records that are related to the current report.
90
     *
91
     * @return array the list of incidents ordered by creation date desc
92
     */
93 4
    public function getIncidents()
94
    {
95 4
        $incidents = TableRegistry::get('Incidents')->find('all', array(
96 4
            'limit' => 50,
97 4
            'conditions' => $this->_relatedIncidentsConditions(),
98 4
            'order' => 'Incidents.created desc',
99
        ));
100
101 4
        return $incidents;
102
    }
103
104
    /**
105
     * Retrieves the report records that are related to the current report.
106
     *
107
     * @return array the list of related reports
108
     */
109 2
    public function getRelatedReports()
110
    {
111 2
        return $this->find('all', array(
112 2
            'conditions' => $this->_relatedReportsConditions(),
113
        ));
114
    }
115
116
    /**
117
     * Retrieves the incident records that are related to the current report that
118
     * also have a description.
119
     *
120
     * @return array the list of incidents ordered by description lenght desc
121
     */
122 3
    public function getIncidentsWithDescription()
123
    {
124 3
        return TableRegistry::get('Incidents')->find('all', array(
125 3
            'conditions' => array(
126
                'NOT' => array(
127
                    'Incidents.steps is null',
128
                ),
129 3
                $this->_relatedIncidentsConditions(),
130
            ),
131 3
            'order' => 'Incidents.steps desc',
132
        ));
133
    }
134
135
    /**
136
     * Retrieves the incident records that are related to the current report that
137
     * that have a different stacktrace.
138
     *
139
     * @return array the list of incidents
140
     */
141 3
    public function getIncidentsWithDifferentStacktrace()
142
    {
143 3
        return TableRegistry::get('Incidents')->find('all', array(
144 3
            'fields' => array('DISTINCT Incidents.stackhash', 'Incidents.stacktrace',
145
                    'Incidents.full_report', 'Incidents.exception_type', ),
146 3
            'conditions' => $this->_relatedIncidentsConditions(),
147 3
            'group' => 'Incidents.stackhash',
148
        ));
149
    }
150
151
    /**
152
     * Removes a report from a group of related reports.
153
     *
154
     * @param mixed $report
155
     */
156 1
    public function removeFromRelatedGroup($report)
157
    {
158 1
        $report->related_to = null;
159 1
        $this->save($report);
160
161 1
        $rel_report = $this->findByRelatedTo($report->id)->first();
0 ignored issues
show
Documentation Bug introduced by
The method findByRelatedTo does not exist on object<App\Model\Table\ReportsTable>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
162 1
        if ($rel_report) {
163
            $this->updateAll(
164
                array('related_to' => $rel_report->id),
165
                array('related_to' => $report->id)
166
            );
167
        }
168
169
        // remove all redundant self-groupings
170 1
        $this->updateAll(
171 1
            array('related_to' => null),
172 1
            array('reports.related_to = reports.id')
173
        );
174 1
    }
175
176
    /**
177
     * Adds a report to a group of related reports.
178
     *
179
     * @param mixed $report
180
     * @param mixed $related_to
181
     */
182 1
    public function addToRelatedGroup($report, $related_to)
183
    {
184 1
        $dup_report = $this->get($related_to);
185
186 1
        if ($dup_report && $dup_report->related_to) {
0 ignored issues
show
Bug introduced by
Accessing related_to on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
187
            $report->related_to = $dup_report->related_to;
0 ignored issues
show
Bug introduced by
Accessing related_to on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
188
        } else {
189 1
            $report->related_to = $related_to;
190
        }
191 1
        $report->status = $dup_report->status;
0 ignored issues
show
Bug introduced by
Accessing status on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
192
193 1
        $this->save($report);
194 1
    }
195
196
    /**
197
     * Returns the full url to the current report.
198
     *
199
     * @return string url
200
     */
201 1
    public function getUrl()
202
    {
203 1
        return Router::url(array('controller' => 'reports', 'action' => 'view',
204 1
                $this->id, ), true);
205
    }
206
207
    /**
208
     * groups related incidents by distinct values of a field. It may also provide
209
     * the number of groups, whether to only include incidents that are related
210
     * to the current report and also to only limit the search to incidents
211
     * submited after a certain date.
212
     *
213
     * @param string $fieldName the name of the field to group by
214
     * @param int    $limit     the max number of groups to return
215
     * @param bool   $count     whether to return the number of distinct groups
216
     * @param bool   $related   whether to limit the search to only related incidents
217
     * @param Date   $timeLimit the date at which to start the search
218
     *
219
     * @return array the groups with the count of each group and possibly the number
220
     *               of groups. Ex: array('Apache' => 2) or array(array('Apache' => 2), 1)
221
     */
222 3
    public function getRelatedByField($fieldName, $limit = 10, $count = false,
223
        $related = true, $timeLimit = null)
224
    {
225 3
        $fieldAlias = "Incidents__$fieldName";
226
        $queryDetails = array(
227
            'conditions' => array(
228
                'NOT' => array(
229 3
                    "Incidents.$fieldName is null",
230
                ),
231
            ),
232 3
            'limit' => $limit,
233
        );
234
235 3
        if ($related) {
236 3
            $queryDetails['conditions'][] = $this->_relatedIncidentsConditions();
237
        }
238
239 3
        if ($timeLimit) {
240 1
            $queryDetails['conditions'][] = array(
241 1
                'Incidents.created >=' => $timeLimit,
242
            );
243
        }
244
245 3
        $groupedCount = TableRegistry::get('Incidents')->find('all', $queryDetails);
246
247
        /* Ommit version number in case of browser and server_software fields.
248
         * In case of browser field, version number is seperated by space,
249
         * for example,'FIREFOX 47', hence split the value using space.
250
         * In case of server_software field, version number is seperated by /
251
         * for example, 'nginx/1.7', hence split the value using /.
252
         * See http://book.cakephp.org/3.0/en/orm/query-builder.html#using-sql-functionsp://book.cakephp.org/3.0/en/orm/query-builder.html#using-sql-functions
253
         * for how to use Sql functions with cake
254
         */
255
        switch ($fieldName) {
256 3 View Code Duplication
            case 'browser':
257
                // SUBSTRING(browser, 1, LOCATE(' ', Incidents.browser)-1))
258 2
                $field = $groupedCount->func()->substring(array(
259 2
                    $fieldName => 'literal',
260 2
                    '1' => 'literal',
261 2
                    "Locate(' ', Incidents.browser)-1" => 'literal',
262
                    ));
263 2
                break;
264 3 View Code Duplication
            case 'server_software':
265
                // SUBSTRING(server_software, 1, LOCATE('/', Incidents.server_software)-1))
266 2
                $field = $groupedCount->func()->substring(array(
267 2
                    $fieldName => 'literal', '1' => 'literal',
268 2
                    "Locate('/', Incidents.server_software)-1" => 'literal',
269
                    ));
270 2
                break;
271
            default:
272 3
                $field = $fieldName;
273
        }
274 3
        $groupedCount->select(array(
275 3
            'count' => $groupedCount->func()->count('*'),
276 3
            $fieldAlias => $field,
277 3
        ))->group($fieldAlias)->distinct(array("$fieldAlias"))
278 3
          ->order('count')->toArray();
279
280 3
        if ($count) {
281 3
            $queryDetails['fields'] = array("$fieldName");
282 3
            $queryDetails['limit'] = null;
283 3
            $queryDetails['group'] = "Incidents.$fieldName";
284 3
            $totalCount = TableRegistry::get('Incidents')->find('all', $queryDetails)->count();
285
286 3
            return array($groupedCount, $totalCount);
287
        }
288
289 1
        return $groupedCount;
290
    }
291
292
    /**
293
     * returns an array of conditions that would return all related incidents to the
294
     * current report.
295
     *
296
     * @return array the related incidents conditions
297
     */
298 7
    protected function _relatedIncidentsConditions()
299
    {
300
        $conditions = array(
301 7
            array('Incidents.report_id = ' . $this->id),
302
        );
303 7
        $report = $this->get($this->id);
304 7
        if ($report->related_to) { //TODO: fix when fix related reports
0 ignored issues
show
Bug introduced by
Accessing related_to on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
305 1
            $conditions[] = array('Incidents.report_id = ' . $report->related_to);
0 ignored issues
show
Bug introduced by
Accessing related_to on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
306
        }
307
308 7
        return array('OR' => $conditions);
309
    }
310
311
    /**
312
     * returns an array of conditions that would return all related reports to the
313
     * current report.
314
     *
315
     * @return array the related reports conditions
316
     */
317 2
    protected function _relatedReportsConditions()
318
    {
319 2
        $conditions = array(array('related_to' => $this->id));
320 2
        $report = $this->get($this->id);
321 2
        if ($report->related_to) { //TODO: fix related to
0 ignored issues
show
Bug introduced by
Accessing related_to on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
322
            $conditions[] = array('related_to' => $report->related_to);
0 ignored issues
show
Bug introduced by
Accessing related_to on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
323
            $conditions[] = array('id' => $report->related_to);
0 ignored issues
show
Bug introduced by
Accessing related_to on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
324
        }
325 2
        $conditions = array(array('OR' => $conditions));
326 2
        $conditions[] = array('Reports.id !=' => $this->id);
327
328 2
        return $conditions;
329
    }
330
}
331