Completed
Push — master ( 740d41...a3b506 )
by Michal
07:10 queued 16s
created

ReportsTable::getRelatedReports()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
crap 1
1
<?php
2
/* vim: set noexpandtab sw=2 ts=2 sts=2: */
3
namespace App\Model\Table;
4
5
use Cake\ORM\Table;
6
use App\Model\AppModel;
7
use Cake\Model\Model;
8
use Cake\Routing\Router;
9
use Cake\ORM\TableRegistry;
10
/**
11
 * Report model representing a group of incidents.
12
 *
13
 * phpMyAdmin Error reporting server
14
 * Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
15
 *
16
 * Licensed under The MIT License
17
 * For full copyright and license information, please see the LICENSE.txt
18
 * Redistributions of files must retain the above copyright notice.
19
 *
20
 * @copyright     Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
21
 * @package       Server.Model
22
 * @link          https://www.phpmyadmin.net/
23
 * @license       https://opensource.org/licenses/mit-license.php MIT License
24
 */
25
26
27
/**
28
 * A report a representing a group of incidents
29
 *
30
 * @package       Server.Model
31
 */
32
class ReportsTable extends Table {
33
34
    /**
35
 * @var array
36
 * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany
37
 * @see Cake::Model::$hasMany
38
 */
39
	public $hasMany = array(
40
		'Incidents' => array(
41
			'dependant' => true
42
		)
43
	);
44
45
/**
46
 * @var Array
47
 * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#validate
48
 * @link 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
 * @see Model::$findMethods
63
 */
64
	public $findMethods = array(
65
		'allDataTable' =>	true,
66
		'arrayList' => true,
67
	);
68
69
/**
70
 * List of valid finder method options, supplied as the first parameter to find().
71
 *
72
 * @var array
73
 */
74
	public $status = array(
75
		'new' =>	'New',
76
		'fixed' =>	'Fixed',
77
		'wontfix' =>	"Won't Fix",
78
		'open' => 	"Open",
79
		'pending' =>	"Pending",
80
		'resolved' =>	"Resolved",
81
		'invalid' => "Invalid",
82
		'duplicate' => "Duplicate",
83
		'works-for-me' => "Works for me",
84
		'out-of-date' => "Out of Date"
85
	);
86
87 14
    public function initialize(array $config)
88
    {
89 14
        $this->hasMany('Incidents', [
90
            'dependent' => true
91 14
        ]);
92 14
    }
93
/**
94
 * Retrieves the incident records that are related to the current report
95
 *
96
 * @return Array the list of incidents ordered by creation date desc
97
 */
98 3
	public function getIncidents() {
99 3
		$incidents = TableRegistry::get('Incidents')->find('all', array(
100 3
			'limit' => 50,
101 3
			'conditions' => $this->_relatedIncidentsConditions(),
102 3
			'order' => 'Incidents.created desc'
103
		));
104 3
        return $incidents;
105
	}
106
107
/**
108
 * Retrieves the report records that are related to the current report
109
 *
110
 * @return Array the list of related reports
111
 */
112 1
	public function getRelatedReports() {
113 1
		return $this->find("all", array(
114 1
			'conditions' => $this->_relatedReportsConditions(),
115
		));
116
	}
117
118
/**
119
 * Retrieves the incident records that are related to the current report that
120
 * also have a description
121
 *
122
 * @return Array the list of incidents ordered by description lenght desc
123
 */
124 2
	public function getIncidentsWithDescription() {
125 2
		return TableRegistry::get('Incidents')->find('all', array(
126
			'conditions' => array(
127
				'NOT' => array(
128
					'Incidents.steps is null'
129
				),
130 2
				$this->_relatedIncidentsConditions(),
131 2
			),
132 2
			'order' => 'Incidents.steps desc'
133
		));
134
	}
135
136
/**
137
 * Retrieves the incident records that are related to the current report that
138
 * that have a different stacktrace
139
 *
140
 * @return Array the list of incidents
141
 */
142 2
	public function getIncidentsWithDifferentStacktrace() {
143 2
		return TableRegistry::get('Incidents')->find('all', array(
144
			'fields' => array('DISTINCT Incidents.stackhash', 'Incidents.stacktrace',
145 2
					'Incidents.full_report', 'Incidents.exception_type'),
146 2
			'conditions' => $this->_relatedIncidentsConditions(),
147 2
			'group' => "Incidents.stackhash",
148
		));
149
	}
150
151
/**
152
 * Removes a report from a group of related reports
153
 *
154
 * @return void
155
 */
156
	public function removeFromRelatedGroup($report) {
157
		$report->related_to = null;
158
		$this->save($report);
159
160
		$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...
161
		if ($rel_report) {
162
			$this->updateAll(
163
				array("related_to" => $rel_report->id),
164
				array("related_to" => $report->id)
165
			);
166
		}
167
168
		// remove all redundant self-groupings
169
		$this->updateAll(
170
			array("related_to" => null),
171
			array("reports.related_to = reports.id")
172
		);
173
	}
174
175
/**
176
 * Adds a report to a group of related reports
177
 *
178
 * @return void
179
 */
180
	public function addToRelatedGroup($report, $related_to) {
181
		$dup_report = $this->get($related_to);
182
183
		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...
184
			$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...
185
		} else {
186
			$report->related_to = $related_to;
187
		}
188
		$this->save($report);
189
	}
190
191
/**
192
 * Returns the full url to the current report
193
 *
194
 * @return String url
195
 */
196 1
	public function getUrl() {
197 1
		return Router::url(array("controller" => "reports", "action" => "view",
198 1
				$this->id), true);
199
	}
200
201
/**
202
 * groups related incidents by distinct values of a field. It may also provide
203
 * the number of groups, whether to only include incidents that are related
204
 * to the current report and also to only limit the search to incidents
205
 * submited after a certain date
206
 *
207
 * @param String $fieldName the name of the field to group by
208
 * @param Integer $limit the max number of groups to return
209
 * @param Boolean $count whether to return the number of distinct groups
210
 * @param Boolean $related whether to limit the search to only related incidents
211
 * @param Date $timeLimit the date at which to start the search
212
 * @return Array the groups with the count of each group and possibly the number
213
 *		of groups. Ex: array('Apache' => 2) or array(array('Apache' => 2), 1)
214
 */
215 2
	public function getRelatedByField($fieldName, $limit = 10, $count = false,
216
		$related = true, $timeLimit = null) {
217 2
		$fieldAlias = "Incidents__$fieldName";
218
		$queryDetails = [
219
			'conditions' => [
220
				'NOT' => [
221 2
					"Incidents.$fieldName is null"
222
				]
223
			],
224 2
			'limit' => $limit
225
		];
226
227 2
		if ($related) {
228 2
			$queryDetails["conditions"][] = $this->_relatedIncidentsConditions();
229
		}
230
231 2
		if ($timeLimit) {
232 1
			$queryDetails["conditions"][] = [
233 1
				'Incidents.created >=' => $timeLimit
234
			];
235
		}
236
237 2
		$groupedCount = TableRegistry::get('Incidents')->find("all", $queryDetails);
238
239
		/* Ommit version number in case of browser and server_software fields.
240
		 * In case of browser field, version number is seperated by space,
241
		 * for example,'FIREFOX 47', hence split the value using space.
242
		 * In case of server_software field, version number is seperated by /
243
		 * for example, 'nginx/1.7', hence split the value using /.
244
		 * 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
245
		 * for how to use Sql functions with cake
246
		 */
247
		switch ($fieldName) {
248 2 View Code Duplication
			case 'browser':
249
				// SUBSTRING(browser, 1, LOCATE(' ', Incidents.browser)-1))
250 1
				$field = $groupedCount->func()->substring([
251 1
					$fieldName=>'literal',
252 1
					"1" => 'literal',
253 1
					"Locate(' ', Incidents.browser)-1" => 'literal'
254
					]);
255 1
				break;
256 2 View Code Duplication
			case 'server_software':
257
				// SUBSTRING(server_software, 1, LOCATE('/', Incidents.server_software)-1))
258 1
				$field = $groupedCount->func()->substring([
259 1
					$fieldName=>'literal', "1" => 'literal',
260 1
					"Locate('/', Incidents.server_software)-1" => 'literal'
261
					]);
262 1
				break;
263
			default:
264 2
				$field = $fieldName;
265
		}
266 2
		$groupedCount->select([
267 2
			'count' => $groupedCount->func()->count('*'),
268 2
			$fieldAlias => $field
269
		])
270 2
		->group($fieldAlias)
271 2
		->distinct(["$fieldAlias"])
272 2
		->order('count')
273 2
		->toArray();		
274
275 2
		if ($count) {
276 2
			$queryDetails['fields'] = ["$fieldName"];
277 2
			$queryDetails['limit'] = null;
278 2
			$queryDetails['group'] = "Incidents.$fieldName";
279 2
			$totalCount = TableRegistry::get('Incidents')->find("all", $queryDetails)->count();
280 2
			return array($groupedCount, $totalCount);
281
		} else {
282 1
			return $groupedCount;
283
		}
284
	}
285
286
/**
287
 * returns an array of conditions that would return all related incidents to the
288
 * current report
289
 *
290
 * @return Array the related incidents conditions
291
 */
292 6
	protected function _relatedIncidentsConditions() {
293 6
		$conditions = array(array('Incidents.report_id = '.$this->id));
294 6
        $report = $this->get($this->id);
295 6
		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...
296
			$conditions[] = array('Incidents.report_id = '.
297
					$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...
298
		}
299 6
		return array('OR' => $conditions);
300
	}
301
302
/**
303
 * returns an array of conditions that would return all related reports to the
304
 * current report
305
 *
306
 * @return Array the related reports conditions
307
 */
308 1
	protected function _relatedReportsConditions() {
309 1
		$conditions = array(array('related_to' => $this->id));
310 1
        $report = $this->get($this->id);
311 1
		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...
312
			$conditions[] = array('related_to' =>
313
					$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...
314
			$conditions[] = array('id' =>
315
					$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...
316
		}
317 1
		$conditions = array(array('OR' => $conditions));
318 1
		$conditions[] = array("Reports.id !=" => $this->id);
319 1
		return $conditions;
320
	}
321
}
322