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

CRUD_helpers   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 98.4%

Importance

Changes 0
Metric Value
dl 0
loc 273
ccs 123
cts 125
cp 0.984
rs 8.2857
c 0
b 0
f 0
wmc 39
lcom 1
cbo 3

8 Methods

Rating   Name   Duplication   Size   Complexity  
A search() 0 16 3
A search_process_parameter() 0 15 4
C search_conditions() 0 27 8
A search_conditions_multilingual() 0 11 2
A search_order_by() 0 8 3
B search_do() 0 31 3
C search_conditions_join_table() 0 37 7
C search_group_by() 0 49 9
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