Completed
Push — master ( fa65af...9eb297 )
by Nazar
04:25
created

CRUD_helpers::search()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 21
rs 8.7624
cc 5
eloc 16
nc 5
nop 5
1
<?php
2
/**
3
 * @package   CleverStyle CMS
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2015-2016, 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
0 ignored issues
show
Documentation introduced by
Should the return type not be false|array[]|integer|integer[]|string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
31
	 */
32
	protected function search ($search_parameters = [], $page = 1, $count = 100, $order_by = 'id', $asc = false) {
33
		if (!isset($this->data_model[$order_by])) {
34
			return false;
35
		}
36
		$joins        = '';
37
		$join_params  = [];
38
		$join_index   = 0;
39
		$where        = [];
40
		$where_params = [];
41
		foreach ($search_parameters as $key => $details) {
42
			if (!isset($this->data_model[$key])) {
43
				continue;
44
			}
45
			if (isset($this->data_model[$key]['data_model'])) {
46
				$this->search_conditions_join_table('t', $key, $details, $joins, $join_params, $join_index);
47
			} else {
48
				$this->search_conditions('t', $key, $details, $where, $where_params);
49
			}
50
		}
51
		return $this->search_do('t', @$search_parameters['total_count'], $where, $where_params, $joins, $join_params, $page, $count, $order_by, $asc);
52
	}
53
	/**
54
	 * @param string   $table_alias
55
	 * @param bool     $total_count
56
	 * @param string[] $where
57
	 * @param array    $where_params
58
	 * @param string   $joins
59
	 * @param array    $join_params
60
	 * @param int      $page
61
	 * @param int      $count
62
	 * @param string   $order_by
63
	 * @param bool     $asc
64
	 *
65
	 * @return false|int|int[]|string[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array[]|false|integer|integer[]|string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
66
	 */
67
	private function search_do ($table_alias, $total_count, $where, $where_params, $joins, $join_params, $page, $count, $order_by, $asc) {
68
		$first_column = array_keys($this->data_model)[0];
69
		$where        = $where ? 'WHERE '.implode(' AND ', $where) : '';
70
		if ($total_count) {
71
			return $this->db()->qfs(
72
				"SELECT COUNT(`$table_alias`.`$first_column`)
73
				FROM `$this->table` AS `$table_alias`
74
				$joins
75
				$where",
76
				array_merge($join_params, $where_params)
77
			);
78
		}
79
		$where_params[] = ($page - 1) * $count;
80
		$where_params[] = $count;
81
		$order_by       = $this->search_order_by($table_alias, $order_by, $joins, $join_index);
82
		$asc            = $asc ? 'ASC' : 'DESC';
83
		return $this->db()->qfas(
84
			"SELECT `$table_alias`.`$first_column`
85
			FROM `$this->table` AS `$table_alias`
86
			$joins
87
			$where
88
			ORDER BY $order_by $asc
89
			LIMIT %d, %d",
90
			array_merge($join_params, $where_params)
91
		);
92
	}
93
	/**
94
	 * @param string   $table_alias
95
	 * @param string   $key
96
	 * @param array    $details
97
	 * @param string[] $where
98
	 * @param array    $where_params
99
	 */
100
	private function search_conditions ($table_alias, $key, $details, &$where, &$where_params) {
101
		if (is_scalar($details)) {
102
			$where[]        = "`$table_alias`.`$key` = '%s'";
103
			$where_params[] = $details;
104
		} elseif (is_array($details) && $details) {
105
			if (is_array_indexed($details)) {
106
				$where_tmp = [];
107
				foreach ($details as $d) {
108
					$where_tmp[]    = "`$table_alias`.`$key` = '%s'";
109
					$where_params[] = $d;
110
				}
111
				$where[] = '('.implode(' OR ', $where_tmp).')';
112
				return;
113
			}
114
			if (isset($details['from'])) {
115
				$where[]        = "`$table_alias`.`$key` => '%s'";
116
				$where_params[] = $details['from'];
117
			}
118
			if (isset($details['to'])) {
119
				$where[]        = "`$table_alias`.`$key` <= '%s'";
120
				$where_params[] = $details['to'];
121
			}
122
		}
123
	}
124
	/**
125
	 * @param string           $table_alias
126
	 * @param string           $table
127
	 * @param array|int|string $details
128
	 * @param string           $joins
129
	 * @param array            $join_params
130
	 * @param int              $join_index
131
	 */
132
	private function search_conditions_join_table ($table_alias, $table, $details, &$joins, &$join_params, &$join_index) {
133
		$data_model        = $this->data_model[$table];
134
		$first_column      = array_keys($this->data_model)[0];
135
		$first_column_join = array_keys($data_model['data_model'])[0];
136
		if (is_scalar($details)) {
137
			$details = [
138
				array_keys($data_model['data_model'])[1] => $details
139
			];
140
		}
141
		/**
142
		 * @var array $details
143
		 */
144
		++$join_index;
145
		$joins .=
146
			"INNER JOIN `{$this->table}_$table` AS `j$join_index`
147
			ON
148
				`$table_alias`.`$first_column` = `j$join_index`.`$first_column_join`";
149
		$language_field = isset($data_model['language_field']) ? $data_model['language_field'] : false;
150
		foreach ($details as $field => $value) {
151
			if ($language_field === $field) {
152
				continue;
153
			}
154
			$where_tmp = [];
155
			$this->search_conditions("j$join_index", $field, $value, $where_tmp, $join_params);
156
			$joins .= " AND ".implode(" AND ", $where_tmp);
157
		}
158
		if ($language_field) {
159
			/** @noinspection OffsetOperationsInspection */
160
			$clang = isset($details[$language_field]) ? $details[$language_field] : Language::instance()->clang;
161
			$joins .=
162
				" AND
163
				(
164
					`j$join_index`.`lang`	= '$clang' OR
165
					`j$join_index`.`lang`	= ''
166
				)";
167
		}
168
	}
169
	/**
170
	 * @param string $table_alias
171
	 * @param string $order_by
172
	 * @param string $joins
173
	 * @param int    $join_index
174
	 *
175
	 * @return string
176
	 */
177
	private function search_order_by ($table_alias, $order_by, &$joins, &$join_index) {
178
		$order_by = explode(':', $order_by);
179
		if (!isset($this->data_model[$order_by[0]])) {
180
			/**
181
			 * Non-existing field
182
			 */
183
			$order_by = ['id'];
184
		} elseif (isset($order_by[1])) {
185
			/**
186
			 * Non-existing field in joined table
187
			 */
188
			if (!isset($this->data_model[$order_by[0]]['data_model'][$order_by[1]])) {
189
				$order_by = ['id'];
190
			}
191
		} elseif (isset($this->data_model[$order_by[0]]['data_model'])) {
192
			/**
193
			 * Default field in joined table
194
			 */
195
			$order_by[1] = array_keys($this->data_model[$order_by[0]]['data_model'])[1];
196
		}
197
		if (isset($order_by[1])) {
198
			++$join_index;
199
			$first_column      = array_keys($this->data_model)[0];
200
			$first_column_join = array_keys($this->data_model[$order_by[0]]['data_model'])[0];
201
			$joins .=
202
				"INNER JOIN `{$this->table}_$order_by[0]` AS `j$join_index`
203
				ON
204
					`$table_alias`.`$first_column`	= `j$join_index`.`$first_column_join`";
205
			$order_by = "`j$join_index`.`$order_by[1]`";
206
		} else {
207
			$order_by = "`$table_alias`.`$order_by[0]`";
208
		}
209
		return $order_by;
210
	}
211
}
212