PluginProbe
The Innovative Form Builder – IvyForms / 1.5.1
The Innovative Form Builder – IvyForms v1.5.1
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
← All changes | backend/src/Repository/AbstractRepository.php +136 -50 0.3.1 → 1.5.1 View file →
@@ -1,19 +1,18 @@
1 1 <?php
2 2
3 3 /**
4 - * @copyright © TMS-Plugins. All rights reserved.
4 + * @copyright © Melograno Venture Studio. All rights reserved.
5 5 * @licence See LICENCE.md for license details.
6 6 */
7 7
8 8 namespace IvyForms\Repository;
9 9
10 -// phpcs:disable PSR1.Files.SideEffects
11 -if (!defined('ABSPATH')) {
12 - exit; // Exit if accessed directly
13 -}
14 -
15 10 use IvyForms\Common\Exceptions\InvalidArgumentException;
11 +use IvyForms\Common\Exceptions\NotFoundException;
12 +use IvyForms\Common\Exceptions\QueryExecutionException;
13 +use IvyForms\Services\Translations\BackendStrings;
14 +use IvyForms\Repository\ColumnRepositoryTrait;
16 15
17 16 /**
18 17 * Class AbstractRepository
19 18 *
@@ -20,8 +19,10 @@
20 19 * @package IvyForms\Repository
21 20 */
22 21 class AbstractRepository
23 22 {
23 + use ColumnRepositoryTrait;
24 +
24 25 public const FACTORY = '';
25 26
26 27 /** @var string */
27 28 protected string $table;
@@ -64,9 +65,9 @@
64 65 */
65 66 public function getAll(): array
66 67 {
67 68 $rows = $this->wpdb->get_results(
68 - $this->wpdb->prepare($this->selectQuery()),
69 + $this->selectQuery(),
69 70 ARRAY_A
70 71 );
71 72
72 73 $result = [];
@@ -77,21 +78,37 @@
77 78
78 79 return $result;
79 80 }
80 81
82 + /**
83 + * @param int $id
84 + * @return int
85 + * @throws QueryExecutionException
86 + */
81 87 public function delete(int $id): int
82 88 {
83 - return $this->wpdb->query(
89 + $result = $this->wpdb->query(
84 90 $this->wpdb->prepare(
85 91 "DELETE FROM $this->table WHERE id = %d",
86 92 $id
87 93 )
88 94 );
95 +
96 + if ($result === false) {
97 + throw new QueryExecutionException(
98 + sprintf(BackendStrings::getExceptionStrings()['failed_delete_by_id'], $id, $this->table)
99 + );
100 + }
101 +
102 + return $result;
89 103 }
90 104
91 105 /**
92 - * @param int[] $ids
93 - * @return int
106 + * Delete multiple entities by their IDs in bulk.
107 + *
108 + * @param array<int> $ids
109 + * @return int Number of deleted entities
110 + * @throws QueryExecutionException
94 111 */
95 112 public function deleteMany(array $ids): int
96 113 {
97 114 if (empty($ids)) {
@@ -98,12 +115,96 @@
98 115 return 0;
99 116 }
100 117 $placeholders = implode(',', array_fill(0, count($ids), '%d'));
101 118 $query = "DELETE FROM $this->table WHERE id IN ($placeholders)";
102 - return $this->wpdb->query($this->wpdb->prepare($query, ...$ids));
119 + $result = $this->wpdb->query($this->wpdb->prepare($query, ...$ids));
120 +
121 + if ($result === false) {
122 + throw new QueryExecutionException(
123 + sprintf(
124 + BackendStrings::getExceptionStrings()['failed_delete_by_ids'],
125 + implode(', ', $ids),
126 + $this->table
127 + )
128 + );
129 + }
130 +
131 + return $result;
103 132 }
104 133
105 134 /**
135 + * Delete multiple entities by foreign key values (e.g., form IDs).
136 + *
137 + * @param array<int> $foreignIds Array of foreign key values (e.g., form IDs)
138 + * @param string $foreignColumn Foreign key column name (default: 'formId')
139 + * @return int Number of deleted entities
140 + * @throws InvalidArgumentException If no foreign IDs are provided
141 + * @throws QueryExecutionException
142 + */
143 + public function deleteManyByForeignKeyValues(array $foreignIds, string $foreignColumn = 'formId'): int
144 + {
145 + if (empty($foreignIds)) {
146 + throw new InvalidArgumentException(
147 + BackendStrings::getExceptionStrings()['no_form_ids_provided']
148 + );
149 + }
150 +
151 + // Validate foreign column to prevent SQL injection
152 + if (!in_array($foreignColumn, $this->getAllowedForeignColumns(), true)) {
153 + throw new InvalidArgumentException(
154 + BackendStrings::getExceptionStrings()['invalid_foreign_column']
155 + );
156 + }
157 +
158 + $foreignIds = array_map('intval', $foreignIds);
159 + $placeholders = implode(',', array_fill(0, count($foreignIds), '%d'));
160 + $query = "DELETE FROM {$this->table} WHERE {$foreignColumn} IN ($placeholders)";
161 + $result = $this->wpdb->query($this->wpdb->prepare($query, ...$foreignIds));
162 +
163 + if ($result === false) {
164 + throw new QueryExecutionException(
165 + sprintf(
166 + BackendStrings::getExceptionStrings()['failed_delete_by_foreign_keys'],
167 + implode(', ', $foreignIds),
168 + $this->table
169 + )
170 + );
171 + }
172 +
173 + return $result;
174 + }
175 +
176 + /**
177 + * Delete entities by a single foreign key value (e.g., form ID).
178 + *
179 + * @param int $foreignId Foreign key value (e.g., form ID)
180 + * @param string $foreignColumn Foreign key column name (default: 'formId')
181 + * @return int Number of deleted entities
182 + * @throws InvalidArgumentException If invalid foreign column is provided
183 + * @throws QueryExecutionException
184 + */
185 + public function deleteOneByForeignKeyValue(int $foreignId, string $foreignColumn = 'formId'): int
186 + {
187 + // Validate foreign column to prevent SQL injection
188 + if (!in_array($foreignColumn, $this->getAllowedForeignColumns(), true)) {
189 + throw new InvalidArgumentException(
190 + BackendStrings::getExceptionStrings()['invalid_foreign_column']
191 + );
192 + }
193 +
194 + $query = "DELETE FROM {$this->table} WHERE {$foreignColumn} = %d";
195 + $result = $this->wpdb->query($this->wpdb->prepare($query, $foreignId));
196 +
197 + if ($result === false) {
198 + throw new QueryExecutionException(
199 + sprintf(BackendStrings::getExceptionStrings()['failed_delete_by_foreign_key'], $foreignId, $this->table)
200 + );
201 + }
202 +
203 + return $result;
204 + }
205 +
206 + /**
106 207 * @param array<string, mixed>|null $params
107 208 * @return array<string, mixed>
108 209 */
109 210 public function search(?array $params): array
@@ -184,19 +285,36 @@
184 285 * @param array<int|string, mixed> $queryParams
185 286 */
186 287 protected function addDateRangeClauses(array $dateRange, array &$whereClauses, array &$queryParams): void
187 288 {
289 +
290 + if (empty($dateRange) || empty($dateRange[0])) {
291 + return;
292 + }
293 +
188 294 $dateColumn = $this->getDateColumn();
189 295
190 - if (!empty($dateRange[0])) {
191 - $whereClauses[] = "$dateColumn >= %s";
192 - $queryParams[] = gmdate('Y-m-d H:i:s', strtotime($dateRange[0]));
296 + if (empty($dateRange[1])) {
297 + $dateRange[1] = $dateRange[0];
193 298 }
194 299
195 - if (!empty($dateRange[1])) {
196 - $whereClauses[] = "$dateColumn <= %s";
197 - $queryParams[] = gmdate('Y-m-d H:i:s', strtotime($dateRange[1]));
198 - }
300 + $normalizeStartOfDay = function ($isoDate) {
301 + $day = substr($isoDate, 0, 10);
302 + return $day . ' 00:00:00';
303 + };
304 + $normalizeEndOfDay = function ($isoDate) {
305 + $day = substr($isoDate, 0, 10);
306 + return $day . ' 23:59:59';
307 + };
308 +
309 + $start = $normalizeStartOfDay($dateRange[0]);
310 + $end = $normalizeEndOfDay($dateRange[1]);
311 +
312 + $whereClauses[] = "$dateColumn >= %s";
313 + $queryParams[] = $start;
314 +
315 + $whereClauses[] = "$dateColumn <= %s";
316 + $queryParams[] = $end;
199 317 }
200 318
201 319 /**
202 320 * @param string $whereClauses
@@ -268,38 +386,6 @@
268 386 */
269 387 public function rollback(): bool
270 388 {
271 389 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 390 }
305 391 }