1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @package CleverStyle Framework |
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 |
|
|
|
|
31
|
|
|
*/ |
32
|
10 |
|
protected function search ($search_parameters = [], $page = 1, $count = 100, $order_by = 'id', $asc = false) { |
33
|
10 |
|
$joins = ''; |
34
|
10 |
|
$join_params = []; |
35
|
10 |
|
$join_index = 0; |
36
|
10 |
|
$where = []; |
37
|
10 |
|
$where_params = []; |
38
|
10 |
|
foreach ($search_parameters as $key => $details) { |
39
|
6 |
|
if (!isset($this->data_model[$key])) { |
40
|
2 |
|
continue; |
41
|
|
|
} |
42
|
6 |
|
if (isset($this->data_model[$key]['data_model'])) { |
43
|
2 |
|
$this->search_conditions_join_table('t', $key, $details, $joins, $join_params, $join_index); |
44
|
|
|
} else { |
45
|
6 |
|
$this->search_conditions('t', $key, $details, $where, $where_params); |
46
|
|
|
} |
47
|
|
|
} |
48
|
10 |
|
return $this->search_do('t', @$search_parameters['total_count'], $where, $where_params, $joins, $join_params, $page, $count, $order_by, $asc); |
49
|
|
|
} |
50
|
|
|
/** |
51
|
|
|
* @param string $table_alias |
52
|
|
|
* @param bool $total_count |
53
|
|
|
* @param string[] $where |
54
|
|
|
* @param array $where_params |
55
|
|
|
* @param string $joins |
56
|
|
|
* @param array $join_params |
57
|
|
|
* @param int $page |
58
|
|
|
* @param int $count |
59
|
|
|
* @param string $order_by |
60
|
|
|
* @param bool $asc |
61
|
|
|
* |
62
|
|
|
* @return false|int|int[]|string[] |
63
|
|
|
*/ |
64
|
10 |
|
private function search_do ($table_alias, $total_count, $where, $where_params, $joins, $join_params, $page, $count, $order_by, $asc) { |
65
|
10 |
|
$first_column = array_keys($this->data_model)[0]; |
66
|
10 |
|
$where = $where ? 'WHERE '.implode(' AND ', $where) : ''; |
67
|
10 |
|
if ($total_count) { |
68
|
2 |
|
return (int)$this->db()->qfs( |
69
|
2 |
|
"SELECT COUNT(`$table_alias`.`$first_column`) |
70
|
2 |
|
FROM `$this->table` AS `$table_alias` |
71
|
2 |
|
$joins |
72
|
2 |
|
$where |
73
|
2 |
|
GROUP BY `$table_alias`.`$first_column`", |
74
|
|
|
array_merge($join_params, $where_params) |
75
|
|
|
); |
76
|
|
|
} |
77
|
10 |
|
$where_params[] = $count; |
78
|
10 |
|
$where_params[] = ($page - 1) * $count; |
79
|
10 |
|
$order_by = $this->search_order_by($table_alias, $order_by, $joins, $join_index); |
80
|
10 |
|
$asc = $asc ? 'ASC' : 'DESC'; |
81
|
10 |
|
$return = $this->db()->qfas( |
82
|
10 |
|
"SELECT `$table_alias`.`$first_column` |
83
|
10 |
|
FROM `$this->table` AS `$table_alias` |
84
|
10 |
|
$joins |
85
|
10 |
|
$where |
86
|
10 |
|
GROUP BY `$table_alias`.`$first_column` |
87
|
10 |
|
ORDER BY $order_by $asc |
88
|
10 |
|
LIMIT %d OFFSET %d", |
89
|
|
|
array_merge($join_params, $where_params) |
90
|
|
|
); |
91
|
10 |
|
return $this->read_field_post_processing($return, array_values($this->data_model)[0]); |
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
|
6 |
|
private function search_conditions ($table_alias, $key, $details, &$where, &$where_params) { |
101
|
6 |
|
if (is_scalar($details)) { |
102
|
6 |
|
$where[] = "`$table_alias`.`$key` = '%s'"; |
103
|
6 |
|
$where_params[] = $details; |
104
|
4 |
|
} elseif (is_array($details) && $details) { |
105
|
2 |
|
if (is_array_indexed($details)) { |
106
|
2 |
|
$where_tmp = []; |
107
|
2 |
|
foreach ($details as $d) { |
108
|
2 |
|
$where_tmp[] = "`$table_alias`.`$key` = '%s'"; |
109
|
2 |
|
$where_params[] = $d; |
110
|
|
|
} |
111
|
2 |
|
$where[] = '('.implode(' OR ', $where_tmp).')'; |
112
|
2 |
|
return; |
113
|
|
|
} |
114
|
2 |
|
if (isset($details['from'])) { |
115
|
2 |
|
$where[] = "`$table_alias`.`$key` >= '%s'"; |
116
|
2 |
|
$where_params[] = $details['from']; |
117
|
|
|
} |
118
|
2 |
|
if (isset($details['to'])) { |
119
|
2 |
|
$where[] = "`$table_alias`.`$key` <= '%s'"; |
120
|
2 |
|
$where_params[] = $details['to']; |
121
|
|
|
} |
122
|
|
|
} |
123
|
6 |
|
} |
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
|
2 |
|
private function search_conditions_join_table ($table_alias, $table, $details, &$joins, &$join_params, &$join_index) { |
133
|
2 |
|
$data_model = $this->data_model[$table]; |
134
|
2 |
|
$first_column = array_keys($this->data_model)[0]; |
135
|
2 |
|
$first_column_join = array_keys($data_model['data_model'])[0]; |
136
|
2 |
|
if (is_scalar($details)) { |
137
|
|
|
$details = [ |
138
|
2 |
|
array_keys($data_model['data_model'])[1] => $details |
139
|
|
|
]; |
140
|
|
|
} |
141
|
|
|
/** |
142
|
|
|
* @var array $details |
143
|
|
|
*/ |
144
|
2 |
|
++$join_index; |
145
|
|
|
$joins .= |
146
|
2 |
|
"INNER JOIN `{$this->table}_$table` AS `j$join_index` |
147
|
|
|
ON |
148
|
2 |
|
`$table_alias`.`$first_column` = `j$join_index`.`$first_column_join`"; |
149
|
2 |
|
$language_field = isset($data_model['language_field']) ? $data_model['language_field'] : false; |
150
|
2 |
|
foreach ($details as $field => $value) { |
151
|
2 |
|
if ($language_field === $field) { |
152
|
|
|
continue; |
153
|
|
|
} |
154
|
2 |
|
$where_tmp = []; |
155
|
2 |
|
$this->search_conditions("j$join_index", $field, $value, $where_tmp, $join_params); |
156
|
2 |
|
$joins .= ' AND '.implode(' AND ', $where_tmp); |
157
|
|
|
} |
158
|
2 |
|
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
|
2 |
|
} |
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
|
10 |
|
private function search_order_by ($table_alias, $order_by, &$joins, &$join_index) { |
178
|
10 |
|
$order_by = explode(':', $order_by); |
179
|
10 |
|
if (!isset($this->data_model[$order_by[0]])) { |
180
|
|
|
/** |
181
|
|
|
* Non-existing field |
182
|
|
|
*/ |
183
|
2 |
|
$order_by = ['id']; |
184
|
10 |
|
} elseif (isset($order_by[1])) { |
185
|
|
|
/** |
186
|
|
|
* Non-existing field in joined table |
187
|
|
|
*/ |
188
|
2 |
|
if (!isset($this->data_model[$order_by[0]]['data_model'][$order_by[1]])) { |
189
|
2 |
|
$order_by = ['id']; |
190
|
|
|
} |
191
|
10 |
|
} elseif (is_array($this->data_model[$order_by[0]]) && isset($this->data_model[$order_by[0]]['data_model'])) { |
192
|
|
|
/** |
193
|
|
|
* Default field in joined table |
194
|
|
|
*/ |
195
|
2 |
|
$order_by[1] = array_keys($this->data_model[$order_by[0]]['data_model'])[1]; |
196
|
|
|
} |
197
|
10 |
|
if (isset($order_by[1])) { |
198
|
2 |
|
++$join_index; |
199
|
2 |
|
$first_column = array_keys($this->data_model)[0]; |
200
|
2 |
|
$first_column_join = array_keys($this->data_model[$order_by[0]]['data_model'])[0]; |
201
|
|
|
$joins .= |
202
|
2 |
|
"INNER JOIN `{$this->table}_$order_by[0]` AS `j$join_index` |
203
|
|
|
ON |
204
|
2 |
|
`$table_alias`.`$first_column` = `j$join_index`.`$first_column_join`"; |
205
|
2 |
|
$order_by = "`j$join_index`.`$order_by[1]`"; |
206
|
|
|
} else { |
207
|
10 |
|
$order_by = "`$table_alias`.`$order_by[0]`"; |
208
|
|
|
} |
209
|
10 |
|
return $order_by; |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
|
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.If the return type contains the type array, this check recommends the use of a more specific type like
String[]
orarray<String>
.