Completed
Push — master ( e5db64...945d9a )
by Schlaefer
05:09 queued 28s
created

PrgComponent::presetForm()   F

Complexity

Conditions 17
Paths 272

Size

Total Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
nc 272
nop 1
dl 0
loc 63
rs 3.4833
c 0
b 0
f 0

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
 * Copyright 2009 - 2013, Cake Development Corporation (http://cakedc.com)
4
 *
5
 * Licensed under The MIT License
6
 * Redistributions of files must retain the above copyright notice.
7
 *
8
 * @copyright Copyright 2009 - 2013, Cake Development Corporation (http://cakedc.com)
9
 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
10
 */
11
12
/**
13
 * Post-Redirect-Get: Transfers POST Requests to GET Requests
14
 *
15
 */
16
App::uses('Component', 'Controller');
17
App::uses('Set', 'Utility');
18
19
class PrgComponent extends Component {
20
21
/**
22
 * Actions used to fetch the post data
23
 *
24
 * Maps the action that takes the post data and processes it by using this
25
 * component and maps it to another action that is accessed by a redirect which
26
 * has the post data attached as get data now
27
 *
28
 * array('search' => 'results');
29
 * array('search' => array('controller' => 'results');
30
 *
31
 * @var array actions
32
 */
33
	public $actions = array();
34
35
/**
36
 * Enables encoding on all presetVar fields
37
 *
38
 * @var boolean
39
 */
40
	public $encode = false;
41
42
/**
43
 * If the current request is an actual search (at least one search value present)
44
 *
45
 * @var boolean
46
 */
47
	public $isSearch = false;
48
49
/**
50
 * Parsed params of current request
51
 *
52
 * @var array
53
 */
54
	protected $_parsedParams = array();
55
56
/**
57
 * Default options
58
 *
59
 * @var array
60
 */
61
	protected $_defaults = array(
62
		'commonProcess' => array(
63
			'formName' => null,
64
			'keepPassed' => true,
65
			'action' => null,
66
			'modelMethod' => 'validateSearch',
67
			'allowedParams' => array(),
68
			'paramType' => 'named',
69
			'filterEmpty' => false
70
		),
71
		'presetForm' => array(
72
			'model' => null,
73
			'paramType' => 'named'
74
		)
75
	);
76
77
/**
78
 * Constructor
79
 *
80
 * @param ComponentCollection $collection
81
 * @param array $settings
82
 */
83
	public function __construct(ComponentCollection $collection, $settings) {
84
		$this->_defaults = Set::merge($this->_defaults, array(
85
			'commonProcess' => (array)Configure::read('Search.Prg.commonProcess'),
86
			'presetForm' => (array)Configure::read('Search.Prg.presetForm'),
87
		), $settings);
88
	}
89
90
/**
91
 * Called before the Controller::beforeFilter().
92
 *
93
 * @param Controller $controller Controller with components to initialize
94
 * @return void
95
 */
96
	public function initialize(Controller $controller) {
97
		$this->controller = $controller;
0 ignored issues
show
Bug introduced by
The property controller does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
98
99
		// fix for not throwing warnings
100
		if (!isset($this->controller->presetVars)) {
101
			$this->controller->presetVars = true;
102
		}
103
104
		$model = $this->controller->modelClass;
105
		if (!empty($settings['model'])) {
0 ignored issues
show
Bug introduced by
The variable $settings seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
106
			$model = $settings['model'];
107
		}
108
109
		if ($this->controller->presetVars === true) {
110
			// auto-set the presetVars based on search definitions in model
111
			$this->controller->presetVars = array();
112
			$filterArgs = array();
113
			if (!empty($this->controller->$model->filterArgs)) {
114
				$filterArgs = $this->controller->$model->filterArgs;
115
			}
116
117
			foreach ($filterArgs as $key => $arg) {
118
				if ($args = $this->_parseFromModel($arg, $key)) {
119
					$this->controller->presetVars[] = $args;
120
				}
121
			}
122
		}
123
		foreach ($this->controller->presetVars as $key => $field) {
124
			if ($field === true) {
125
				if (isset($this->controller->$model->filterArgs[$key])) {
126
					$field = $this->_parseFromModel($this->controller->$model->filterArgs[$key], $key);
127
				} else {
128
					$field = array('type' => 'value');
129
				}
130
			}
131
			if (!isset($field['field'])) {
132
				$field['field'] = $key;
133
			}
134
			$this->controller->presetVars[$key] = $field;
135
		}
136
	}
137
138
/**
139
 * Populates controller->request->data with allowed values from the named/passed get params
140
 *
141
 * Fields in $controller::$presetVars that have a type of 'lookup' the foreignKey value will be inserted
142
 *
143
 * 1) 'lookup'
144
 *    Is used for autocomplete selectors
145
 *    For autocomplete we have hidden field with value and autocomplete text box
146
 *    Component fills text part on id from hidden field
147
 * 2) 'value'
148
 *    The value as it is entered in form
149
 * 3) 'checkbox'
150
 *    Allows to pass several values internaly encoded as string
151
 *
152
 * 1 use field, model, formField, and modelField
153
 * 2, 3 need only field parameter
154
 *
155
 * @param array $options
156
 * @return void
157
 */
158
	public function presetForm($options) {
159
		if (!is_array($options)) {
160
			$options = array('model' => $options);
161
		}
162
		extract(Set::merge($this->_defaults['presetForm'], $options));
0 ignored issues
show
Bug introduced by
\Set::merge($this->_defa...presetForm'], $options) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
163
164
		if ($paramType === 'named') {
165
			$args = $this->controller->passedArgs;
166
		} else {
167
			$args = $this->controller->request->query;
168
		}
169
170
		$parsedParams = array();
171
		$data = array($model => array());
172
		foreach ($this->controller->presetVars as $field) {
173
			if (!isset($args[$field['field']])) {
174
				continue;
175
			}
176
177
			if ($paramType === 'named' && ($this->encode || !empty($field['encode']))) {
178
				// Its important to set it also back to the controllers passed args!
179
				$fieldContent = str_replace(array('-', '_'), array('/', '='), $args[$field['field']]);
180
				$args[$field['field']] = base64_decode($fieldContent);
181
			}
182
183
			if ($field['type'] === 'lookup') {
184
				$searchModel = $field['model'];
185
				$this->controller->loadModel($searchModel);
186
				$this->controller->{$searchModel}->recursive = -1;
187
				$result = $this->controller->{$searchModel}->findById($args[$field['field']]);
188
				$parsedParams[$field['field']] = $args[$field['field']];
189
				$parsedParams[$field['formField']] = $result[$searchModel][$field['modelField']];
190
				$data[$model][$field['field']] = $args[$field['field']];
191
				$data[$model][$field['formField']] = $result[$searchModel][$field['modelField']];
192
193
			} elseif ($field['type'] === 'checkbox') {
194
				$values = explode('|', $args[$field['field']]);
195
				$parsedParams[$field['field']] = $values;
196
				$data[$model][$field['field']] = $values;
197
198
			} elseif ($field['type'] === 'value') {
199
				$parsedParams[$field['field']] = $args[$field['field']];
200
				$data[$model][$field['field']] = $args[$field['field']];
201
			}
202
203
			if (isset($data[$model][$field['field']]) && $data[$model][$field['field']] !== '') {
204
				$this->isSearch = true;
205
			}
206
207
			if (isset($data[$model][$field['field']]) && $data[$model][$field['field']] === '' && isset($field['emptyValue'])) {
208
				$data[$model][$field['field']] = $field['emptyValue'];
209
			}
210
		}
211
212
		$this->controller->request->data = $data;
213
		$this->_parsedParams = $parsedParams;
214
		// deprecated, don't use controller's parsedData or passedArgs anymore.
215
		$this->controller->parsedData = $this->_parsedParams;
216
		foreach ($this->controller->parsedData as $key => $value) {
217
			$this->controller->passedArgs[$key] = $value;
218
		}
219
		$this->controller->set('isSearch', $this->isSearch);
220
	}
221
222
/**
223
 * Return the parsed params of the current search request
224
 *
225
 * @return array Params
226
 */
227
	public function parsedParams() {
228
		return $this->_parsedParams;
229
	}
230
231
/**
232
 * Restores form params for checkboxes and other url encoded params
233
 *
234
 * @param array
235
 * @return array
236
 */
237
	public function serializeParams(array &$data) {
238
		foreach ($this->controller->presetVars as $field) {
239
			if ($field['type'] === 'checkbox') {
240 View Code Duplication
				if (array_key_exists($field['field'], $data)) {
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...
241
					$values = join('|', (array)$data[$field['field']]);
242
				} else {
243
					$values = '';
244
				}
245
				$data[$field['field']] = $values;
246
			}
247
248
			if ($this->_defaults['commonProcess']['paramType'] === 'named' && ($this->encode || !empty($field['encode']))) {
249
				$fieldContent = $data[$field['field']];
250
				$tmp = base64_encode($fieldContent);
251
				// replace chars base64 uses that would mess up the url
252
				$tmp = str_replace(array('/', '='), array('-', '_'), $tmp);
253
				$data[$field['field']] = $tmp;
254
			}
255
			if (!empty($field['empty']) && isset($data[$field['field']]) && $data[$field['field']] === '') {
256
				unset($data[$field['field']]);
257
			}
258
		}
259
		return $data;
260
	}
261
262
/**
263
 * Connect named arguments
264
 *
265
 * @param array $data
266
 * @param array $exclude
267
 * @return void
268
 */
269
	public function connectNamed($data = null, array $exclude = array()) {
270
		if (!isset($data)) {
271
			$data = $this->controller->passedArgs;
272
		}
273
274
		if (!is_array($data)) {
275
			return;
276
		}
277
278
		foreach ($data as $key => $value) {
279
			if (!is_numeric($key) && !in_array($key, $exclude)) {
280
				Router::connectNamed(array($key));
281
			}
282
		}
283
	}
284
285
/**
286
 * Exclude
287
 *
288
 * Removes key/values from $array based on $exclude
289
 *
290
 * @param array Array of data to be filtered
291
 * @param array Array of keys to exclude from other $array
292
 * @return array
293
 */
294
	public function exclude(array $array, array $exclude) {
295
		$data = array();
296
		foreach ($array as $key => $value) {
297
			if (is_numeric($key) || !in_array($key, $exclude)) {
298
				$data[$key] = $value;
299
			}
300
		}
301
		return $data;
302
	}
303
304
/**
305
 * Common search method
306
 *
307
 * Handles processes common to all PRG forms
308
 *
309
 * - Handles validation of post data
310
 * - converting post data into named params
311
 * - Issuing redirect(), and connecting named parameters before redirect
312
 * - Setting named parameter form data to view
313
 *
314
 * @param string $modelName - Name of the model class being used for the prg form
315
 * @param array $options Optional parameters:
316
 *  - string formName - name of the form involved in the prg
317
 *  - string action - The action to redirect to. Defaults to the current action
318
 *  - mixed modelMethod - If not false a string that is the model method that will be used to process the data
319
 *  - array allowedParams - An array of additional top level route params that should be included in the params processed
320
 *  - array excludedParams - An array of named/query params that should be excluded from the redirect url
321
 *  - string paramType - 'named' if you want to used named params or 'querystring' is you want to use query string
322
 * @return void
323
 */
324
	public function commonProcess($modelName = null, array $options = array()) {
325
		$defaults = array(
326
			'excludedParams' => array('page'),
327
		);
328
		$defaults = Set::merge($defaults, $this->_defaults['commonProcess']);
329
		extract(Set::merge($defaults, $options));
0 ignored issues
show
Bug introduced by
\Set::merge($defaults, $options) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
330
331
		$paramType = strtolower($paramType);
332
333
		if (empty($modelName)) {
334
			$modelName = $this->controller->modelClass;
335
		}
336
337
		if (empty($formName)) {
338
			$formName = $modelName;
339
		}
340
341
		if (empty($action)) {
342
			$action = $this->controller->action;
343
		}
344
345
		if (!empty($this->controller->request->data)) {
346
			$this->controller->{$modelName}->set($this->controller->request->data);
347
			$valid = true;
348
			if ($modelMethod !== false) {
349
				$valid = $this->controller->{$modelName}->{$modelMethod}();
350
			}
351
352
			if ($valid) {
353
				$params = $this->controller->request->params['named'];
354
				if ($keepPassed) {
355
					$params = array_merge($this->controller->request->params['pass'], $params);
356
				}
357
358
				$searchParams = $this->controller->request->data[$modelName];
359
				$this->serializeParams($searchParams);
360
361
				if ($paramType === 'named') {
362
					$params = array_merge($params, $searchParams);
363
					$params = $this->exclude($params, $excludedParams);
364
					if ($filterEmpty) {
365
						$params = Set::filter($params);
366
					}
367
					foreach ($this->controller->presetVars as $key => $presetVar) {
368
						$field = $key;
369
						if (!empty($presetVar['name'])) {
370
							$field = $presetVar['name'];
371
						}
372
						if (!isset($params[$field])) {
373
							continue;
374
						}
375
						if (!isset($presetVar['emptyValue']) || $presetVar['emptyValue'] !== $params[$field]) {
376
							continue;
377
						}
378
						$params[$field] = '';
379
					}
380
381
					$this->connectNamed($params, array());
382
				} else {
383
					$searchParams = array_merge($this->controller->request->query, $searchParams);
384
					$searchParams = $this->exclude($searchParams, $excludedParams);
385
					if ($filterEmpty) {
386
						$searchParams = Set::filter($searchParams);
387
					}
388
					$this->connectNamed($params, array());
389
					$params['?'] = $searchParams;
390
				}
391
392
				$params['action'] = $action;
393
394
				foreach ($allowedParams as $key) {
395
					if (isset($this->controller->request->params[$key])) {
396
						$params[$key] = $this->controller->request->params[$key];
397
					}
398
				}
399
400
				$this->controller->redirect($params);
401
			} else {
402
				$this->controller->Session->setFlash(__d('search', 'Please correct the errors below.'));
403
			}
404
		} elseif (($paramType === 'named' && !empty($this->controller->passedArgs)) ||
405
				($paramType === 'querystring' && !empty($this->controller->request->query))
406
			) {
407
			$this->connectNamed($this->controller->passedArgs, array());
408
			$this->presetForm(array('model' => $formName, 'paramType' => $paramType));
409
		}
410
	}
411
412
/**
413
 * Parse the configs from the Model (to keep things dry)
414
 *
415
 * @param array $arg
416
 * @param mixed $key
417
 * @return array
418
 */
419
	protected function _parseFromModel(array $arg, $key = null) {
420
		if (isset($arg['preset']) && !$arg['preset']) {
421
			return array();
422
		}
423
		if (isset($arg['presetType'])) {
424
			$arg['type'] = $arg['presetType'];
425
			unset($arg['presetType']);
426
		} elseif (!isset($arg['type']) || in_array($arg['type'], array('expression', 'query', 'subquery', 'like', 'type'))) {
427
			$arg['type'] = 'value';
428
		}
429
430
		if (isset($arg['name']) || is_numeric($key)) {
431
			$field = $arg['name'];
432
		} else {
433
			$field = $key;
434
		}
435
		$res = array('field' => $field, 'type' => $arg['type']);
436
		if (!empty($arg['encode'])) {
437
			$res['encode'] = $arg['encode'];
438
		}
439
		$res = array_merge($arg, $res);
440
		return $res;
441
	}
442
443
}
444