PluginProbe
The Innovative Form Builder – IvyForms / 0.3
The Innovative Form Builder – IvyForms v0.3
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Repository / AbstractRepository.php

AbstractRepository.php in The Innovative Form Builder – IvyForms 0.3, at backend/src/Repository/AbstractRepository.php

306 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © TMS-Plugins. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace IvyForms\Repository;
9
10 // phpcs:disable PSR1.Files.SideEffects
11 if (!defined('ABSPATH')) {
12 exit; // Exit if accessed directly
13 }
14
15 use IvyForms\Common\Exceptions\InvalidArgumentException;
16
17 /**
18 * Class AbstractRepository
19 *
20 * @package IvyForms\Repository
21 */
22 class AbstractRepository
23 {
24 public const FACTORY = '';
25
26 /** @var string */
27 protected string $table;
28
29 /** @var mixed */
30 protected $wpdb;
31
32 public function __construct(string $table)
33 {
34 global $wpdb;
35 $this->wpdb = $wpdb;
36 $this->table = $table;
37 }
38
39 /**
40 * @param int $id
41 * @return object|null
42 *
43 */
44 public function getById(int $id): ?object
45 {
46 $row = $this->wpdb->get_row(
47 $this->wpdb->prepare(
48 $this->selectQuery() . " WHERE $this->table.id = %d",
49 $id
50 ),
51 ARRAY_A
52 );
53
54 if (!$row) {
55 return null;
56 }
57
58 return call_user_func([static::FACTORY, 'create'], $row);
59 }
60
61 /**
62 * @return mixed[]
63 * @throws InvalidArgumentException
64 */
65 public function getAll(): array
66 {
67 $rows = $this->wpdb->get_results(
68 $this->wpdb->prepare($this->selectQuery()),
69 ARRAY_A
70 );
71
72 $result = [];
73
74 foreach ($rows as $row) {
75 $result[] = call_user_func([static::FACTORY, 'create'], $row);
76 }
77
78 return $result;
79 }
80
81 public function delete(int $id): int
82 {
83 return $this->wpdb->query(
84 $this->wpdb->prepare(
85 "DELETE FROM $this->table WHERE id = %d",
86 $id
87 )
88 );
89 }
90
91 /**
92 * @param int[] $ids
93 * @return int
94 */
95 public function deleteMany(array $ids): int
96 {
97 if (empty($ids)) {
98 return 0;
99 }
100 $placeholders = implode(',', array_fill(0, count($ids), '%d'));
101 $query = "DELETE FROM $this->table WHERE id IN ($placeholders)";
102 return $this->wpdb->query($this->wpdb->prepare($query, ...$ids));
103 }
104
105 /**
106 * @param array<string, mixed>|null $params
107 * @return array<string, mixed>
108 */
109 public function search(?array $params): array
110 {
111 $queryParams = [];
112 $whereClauses = $this->buildWhereClauses($params, $queryParams);
113
114 $query = $this->buildSearchQuery($whereClauses, $params, $queryParams);
115 $results = $this->wpdb->get_results($this->wpdb->prepare($query, $queryParams));
116
117 $total = $this->getTotalCount($whereClauses, $queryParams);
118
119 return [
120 'data' => $results,
121 'meta' => [
122 'page' => $params['page'],
123 'perPage' => $params['perPage'],
124 'total' => $total,
125 ],
126 ];
127 }
128
129 /**
130 * @param array<string, mixed>|null $params
131 * @param array<int|string, mixed> $queryParams
132 * @return string
133 */
134 protected function buildWhereClauses(?array $params, array &$queryParams): string
135 {
136 $whereClauses = ['1=1'];
137
138 $this->addSearchClauses($params, $whereClauses, $queryParams);
139 $this->addFilterClauses($params, $whereClauses, $queryParams);
140 $this->addDateRangeClauses($params['dateRange'] ?? [], $whereClauses, $queryParams);
141
142 return implode(' AND ', $whereClauses);
143 }
144
145 /**
146 * @param array<string, mixed>|null $params
147 * @param array<int, string> $whereClauses
148 * @param array<int|string, mixed> $queryParams
149 */
150 protected function addSearchClauses(?array $params, array &$whereClauses, array &$queryParams): void
151 {
152 if (!empty($params['search'])) {
153 $searchEscaped = '%' . $this->wpdb->esc_like($params['search']) . '%';
154 $searchableColumns = $this->getSearchableColumns();
155 $searchConditions = array_map(fn($col) => "$col LIKE %s", $searchableColumns);
156 $whereClauses[] = '(' . implode(' OR ', $searchConditions) . ')';
157 foreach ($searchableColumns as $col) {
158 $queryParams[] = $searchEscaped;
159 }
160 }
161 }
162
163 /**
164 * @param array<string, mixed>|null $params
165 * @param array<int, string> $whereClauses
166 * @param array<int|string, mixed> $queryParams
167 */
168 protected function addFilterClauses(?array $params, array &$whereClauses, array &$queryParams): void
169 {
170 if (!empty($params['filters'])) {
171 $filterableColumns = $this->getFilterableColumns();
172 foreach ($params['filters'] as $key => $value) {
173 if (in_array($key, $filterableColumns, true) && $value !== null && $value !== '') {
174 $whereClauses[] = "{$key} = %s";
175 $queryParams[] = sanitize_text_field($value);
176 }
177 }
178 }
179 }
180
181 /**
182 * @param array<int, string> $dateRange
183 * @param array<int, string> $whereClauses
184 * @param array<int|string, mixed> $queryParams
185 */
186 protected function addDateRangeClauses(array $dateRange, array &$whereClauses, array &$queryParams): void
187 {
188 $dateColumn = $this->getDateColumn();
189
190 if (!empty($dateRange[0])) {
191 $whereClauses[] = "$dateColumn >= %s";
192 $queryParams[] = gmdate('Y-m-d H:i:s', strtotime($dateRange[0]));
193 }
194
195 if (!empty($dateRange[1])) {
196 $whereClauses[] = "$dateColumn <= %s";
197 $queryParams[] = gmdate('Y-m-d H:i:s', strtotime($dateRange[1]));
198 }
199 }
200
201 /**
202 * @param string $whereClauses
203 * @param array<string, mixed>|null $params
204 * @param array<int|string, mixed> $queryParams
205 * @return string
206 */
207 protected function buildSearchQuery(string $whereClauses, ?array $params, array &$queryParams): string
208 {
209 $sortableColumns = $this->getSortableColumns();
210 $sortBy = in_array($params['orderBy'] ?? 'id', $sortableColumns, true) ? $params['orderBy'] : 'id';
211 $order = strtolower($params['order'] ?? 'asc') === 'asc' ? 'ASC' : 'DESC';
212 $page = max(($params['page'] ?? 1), 1);
213 $perPage = max(($params['perPage'] ?? 10), 1);
214 $offset = ($page - 1) * $perPage;
215
216 $queryParams[] = $perPage;
217 $queryParams[] = $offset;
218
219 return $this->selectQuery() . " WHERE {$whereClauses} ORDER BY {$sortBy} {$order} LIMIT %d OFFSET %d";
220 }
221
222 /**
223 * @param string $whereClauses
224 * @param array<int|string, mixed> $queryParams
225 * @return int
226 */
227 protected function getTotalCount(string $whereClauses, array $queryParams): int
228 {
229 // Exclude only LIMIT and OFFSET, keeping filter parameters intact
230 $countParams = $queryParams;
231 if (isset($queryParams[count($queryParams) - 2]) && isset($queryParams[count($queryParams) - 1])) {
232 $countParams = array_slice($queryParams, 0, -2);
233 }
234
235 $countQuery = "SELECT COUNT(*) FROM $this->table WHERE {$whereClauses}";
236 return (int)$this->wpdb->get_var($this->wpdb->prepare($countQuery, $countParams));
237 }
238
239 public function selectQuery(): string
240 {
241 return "SELECT * FROM " . $this->table;
242 }
243
244 /**
245 * Start a database transaction.
246 *
247 * @return bool
248 */
249 public function beginTransaction(): bool
250 {
251 return (bool) $this->wpdb->query('START TRANSACTION');
252 }
253
254 /**
255 * Commit a database transaction.
256 *
257 * @return bool
258 */
259 public function commit(): bool
260 {
261 return (bool) $this->wpdb->query('COMMIT');
262 }
263
264 /**
265 * Rollback a database transaction.
266 *
267 * @return bool
268 */
269 public function rollback(): bool
270 {
271 return (bool) $this->wpdb->query('ROLLBACK');
272 }
273
274 /**
275 * @return string[]
276 */
277 protected function getSearchableColumns(): array
278 {
279 return ['id'];
280 }
281
282 /**
283 * @return array<string, string>
284 */
285 protected function getFilterableColumns(): array
286 {
287 return [];
288 }
289
290 /**
291 * @return string[]
292 */
293 protected function getSortableColumns(): array
294 {
295 return ['id'];
296 }
297
298 /**
299 * @return string
300 */
301 protected function getDateColumn(): string
302 {
303 return 'dateCreated';
304 }
305 }
306