Completed
Push — master ( 195cac...2c4aac )
by Nazar
05:02
created

CRUD_helpers::search_do()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 31
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 25
nc 4
nop 10
dl 0
loc 31
ccs 22
cts 22
cp 1
crap 3
rs 8.8571
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2015-2017, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs;
9
/**
10
 * CRUD trait extended with useful additional methods, for example, for searching
11
 *
12
 * Might me used instead of regular CRUD trait
13
 */
14
trait CRUD_helpers {
15
	use
16
		CRUD;
17
	/**
18
	 * Generic search
19
	 *
20
	 * @param mixed[] $search_parameters Array in form [attribute => value];<br>
21
	 *                                   Or [attribute => [value1, value2, value3]];<br>
22
	 *                                   Or [attribute => [from => a, to => b]];<br>
23
	 *                                   Or [attribute => [...]] in case of joined tables, where ... is any of three constructions mentioned above;<br>
24
	 *                                   if `total_count => true` element is present - total number of found rows will be returned instead of rows themselves
25
	 * @param int     $page
26
	 * @param int     $count
27
	 * @param string  $order_by
28
	 * @param bool    $asc
29
	 *
30
	 * @return false|int|int[]|string[] Array of `id` or number of elements
31
	 */
32 12
	protected function search ($search_parameters = [], $page = 1, $count = 100, $order_by = 'id', $asc = false) {
33 12
		$joins       = '';
34 12
		$join_params = [];
35 12
		$join_index  = 0;
36 12
		$where       = [];
37 12
		$params      = [];
38
		/**
39
		 * @var $key string
40
		 */
41 12
		foreach ($search_parameters as $key => $details) {
42 8
			if (isset($this->data_model[$key])) {
43 8
				$this->search_process_parameter($this->data_model[$key], $key, $details, $where, $params, $joins, $join_params, $join_index);
44
			}
45
		}
46 12
		return $this->search_do('t', @$search_parameters['total_count'], $where, $params, $joins, $join_params, $page, $count, $order_by, $asc);
47
	}
48
	/**
49
	 * @param mixed    $data_model
50
	 * @param string   $key
51
	 * @param string   $details
52
	 * @param string[] $where
53
	 * @param array    $params
54
	 * @param string   $joins
55
	 * @param array    $join_params
56
	 * @param int      $join_index
57
	 */
58 8
	private function search_process_parameter ($data_model, $key, $details, &$where, &$params, &$joins, &$join_params, &$join_index) {
59 8
		if (isset($data_model['data_model'])) {
60 4
			$this->search_conditions_join_table('t', $key, $details, $joins, $join_params, $join_index);
61
		} else {
62 8
			if (strpos($data_model, 'ml:') === 0) {
63 2
				list($where_local, $params_local) = $this->search_conditions_multilingual('t', $key, $details);
64
			} else {
65 6
				list($where_local, $params_local) = $this->search_conditions('t', $key, $details);
66
			}
67 8
			if ($where_local) {
68 8
				$where[] = $where_local;
69 8
				array_push($params, ...$params_local);
70
			}
71
		}
72 8
	}
73
	/**
74
	 * @param string   $table_alias
75
	 * @param bool     $total_count
76
	 * @param string[] $where
77
	 * @param array    $params
78
	 * @param string   $joins
79
	 * @param array    $join_params
80
	 * @param int      $page
81
	 * @param int      $count
82
	 * @param string   $order_by
83
	 * @param bool     $asc
84
	 *
85
	 * @return false|int|int[]|string[]
86
	 */
87 12
	private function search_do ($table_alias, $total_count, $where, $params, $joins, $join_params, $page, $count, $order_by, $asc) {
88 12
		$first_column = array_keys($this->data_model)[0];
89 12
		$where        = $where ? 'WHERE '.implode(' AND ', $where) : '';
90 12
		if ($total_count) {
91 2
			return (int)$this->db()->qfs(
92
				"SELECT COUNT(*) FROM (
93 2
					SELECT `$table_alias`.`$first_column`
94 2
					FROM `$this->table` AS `$table_alias`
95 2
					$joins
96 2
					$where
97 2
					GROUP BY `$table_alias`.`$first_column`
98
				) AS `count`",
99
				array_merge($join_params, $params)
100
			);
101
		}
102 12
		$params[] = $count;
103 12
		$params[] = ($page - 1) * $count;
104 12
		$group_by = $this->search_group_by($table_alias, $order_by, $joins, $join_index);
105 12
		$order_by = $this->search_order_by($group_by, $asc);
106 12
		$return   = $this->db()->qfas(
107 12
			"SELECT `$table_alias`.`$first_column`
108 12
			FROM `$this->table` AS `$table_alias`
109 12
			$joins
110 12
			$where
111 12
			GROUP BY `$table_alias`.`$first_column`, $group_by
112 12
			ORDER BY $order_by
113
			LIMIT ? OFFSET ?",
114
			array_merge($join_params, $params)
115
		);
116 12
		return $this->read_field_post_processing($return, array_values($this->data_model)[0]);
117
	}
118
	/**
119
	 * @param string $table_alias
120
	 * @param string $key
121
	 * @param array  $details
122
	 *
123
	 * @return array First element is string `where` clause, second is an array of parameters
124
	 */
125 8
	private function search_conditions ($table_alias, $key, $details) {
126 8
		if (is_scalar($details)) {
127 8
			return ["`$table_alias`.`$key` = ?", [$details]];
128
		}
129 6
		if (is_array($details) && $details) {
130 4
			$where  = [];
131 4
			$params = [];
132 4
			if (is_array_indexed($details)) {
133 4
				foreach ($details as $d) {
134 4
					$where[]  = "`$table_alias`.`$key` = ?";
135 4
					$params[] = $d;
136
				}
137 4
				$where = implode(' OR ', $where);
138 4
				return ["($where)", $params];
139
			}
140 4
			if (isset($details['from'])) {
141 4
				$where[]  = "`$table_alias`.`$key` >= ?";
142 4
				$params[] = $details['from'];
143
			}
144 4
			if (isset($details['to'])) {
145 4
				$where[]  = "`$table_alias`.`$key` <= ?";
146 4
				$params[] = $details['to'];
147
			}
148 4
			return [implode(' AND ', $where), $params];
149
		}
150 2
		return ['', []];
151
	}
152
	/**
153
	 * @param string $table_alias
154
	 * @param string $key
155
	 * @param array  $details
156
	 *
157
	 * @return array First element is string `where` clause, second is an array of parameters
158
	 */
159 2
	private function search_conditions_multilingual ($table_alias, $key, $details) {
160 2
		list($where1, $params1) = $this->search_conditions($table_alias, $key, $details);
161 2
		if ($where1) {
162 2
			list($where2, $params2) = $this->search_conditions('td', 'text', $details);
163
			return [
164 2
				"($where1 OR `$table_alias`.`$key` IN (SELECT `td`.`id_` FROM `[prefix]texts_data` AS `td` WHERE $where2))",
165 2
				array_merge($params1, $params2)
166
			];
167
		}
168
		return ['', []];
169
	}
170
	/**
171
	 * @param string           $table_alias
172
	 * @param string           $key
173
	 * @param array|int|string $details
174
	 * @param string           $joins
175
	 * @param array            $join_params
176
	 * @param int              $join_index
177
	 */
178 4
	private function search_conditions_join_table ($table_alias, $key, $details, &$joins, &$join_params, &$join_index) {
179 4
		$data_model        = $this->data_model[$key];
180 4
		$first_column      = array_keys($this->data_model)[0];
181 4
		$first_column_join = array_keys($data_model['data_model'])[0];
182 4
		if (is_scalar($details)) {
183
			$details = [
184 4
				array_keys($data_model['data_model'])[1] => $details
185
			];
186
		}
187 4
		++$join_index;
188
		$joins .=
189 4
			"INNER JOIN `{$this->table}_$key` AS `j$join_index`
190
			ON
191 4
				`$table_alias`.`$first_column` = `j$join_index`.`$first_column_join`";
192 4
		$language_field = isset($data_model['language_field']) ? $data_model['language_field'] : false;
193
		/** @noinspection ForeachSourceInspection */
194 4
		foreach ($details as $field => $value) {
195 4
			if ($language_field === $field) {
196
				continue;
197
			}
198 4
			list($where, $params) = $this->search_conditions("j$join_index", $field, $value);
199 4
			if ($where) {
200 4
				$joins .= " AND $where";
201 4
				array_push($join_params, ...$params);
202
			}
203
		}
204 4
		if ($language_field) {
205 2
			$clang = Language::instance()->clang;
206
			$joins .=
207
				" AND
208
				(
209 2
					`j$join_index`.`lang`	= '$clang' OR
210 2
					`j$join_index`.`lang`	= ''
211
				)";
212
		}
213 4
		$joins .= "\n";
214 4
	}
215
	/**
216
	 * @param string $table_alias
217
	 * @param string $order_by
218
	 * @param string $joins
219
	 * @param int    $join_index
220
	 *
221
	 * @return string
222
	 */
223 12
	private function search_group_by ($table_alias, $order_by, &$joins, &$join_index) {
224 12
		$order_by     = explode(':', $order_by);
225 12
		$first_column = array_keys($this->data_model)[0];
226 12
		if (!isset($this->data_model[$order_by[0]])) {
227
			/**
228
			 * Non-existing field
229
			 */
230 2
			$order_by = [$first_column];
231
		}
232 12
		$model = $this->data_model[$order_by[0]];
233 12
		if (isset($order_by[1])) {
234
			/**
235
			 * Non-existing field in joined table
236
			 */
237 2
			if (!isset($model['data_model'][$order_by[1]])) {
238 2
				$order_by = [$first_column];
239 2
				$model    = $this->data_model[$order_by[0]];
240
			}
241 12
		} elseif (is_array($model) && isset($model['data_model'])) {
242
			/**
243
			 * Default field in joined table
244
			 */
245 2
			$order_by[1] = array_keys($model['data_model'])[1];
246
		}
247 12
		if (isset($order_by[1])) {
248 2
			++$join_index;
249 2
			$first_column_join = array_keys($model['data_model'])[0];
250
			$joins .=
251 2
				"INNER JOIN `{$this->table}_$order_by[0]` AS `j$join_index`
252
				ON
253 2
					`$table_alias`.`$first_column`	= `j$join_index`.`$first_column_join`";
254 2
			return "`j$join_index`.`$order_by[1]`";
255
		}
256 12
		if (is_string($model) && strpos($model, 'ml:') === 0) {
257 2
			$clang = Language::instance()->clang;
258 2
			++$join_index;
259
			$joins .=
260 2
				"JOIN `[prefix]texts_data` AS `j$join_index`
261
				ON
262 2
					`$table_alias`.`$order_by[0]`	= `j$join_index`.`id_` AND
263
					(
264 2
						`j$join_index`.`lang`	= '$clang' OR
265 2
						`j$join_index`.`lang`	= ''
266
					)";
267 2
			return "`j$join_index`.`text`, `$table_alias`.`$order_by[0]`";
268
		} else {
269 12
			return "`$table_alias`.`$order_by[0]`";
270
		}
271
	}
272
	/**
273
	 * @param string $group_by
274
	 * @param bool   $asc
275
	 *
276
	 * @return string
277
	 */
278 12
	private function search_order_by ($group_by, $asc) {
279 12
		$direction = $asc ? 'ASC' : 'DESC';
280 12
		$order_by  = explode(', ', $group_by);
281 12
		foreach ($order_by as &$o) {
282 12
			$o = "$o $direction";
283
		}
284 12
		return implode(', ', $order_by);
285
	}
286
}
287