PluginProbe
The Innovative Form Builder – IvyForms / 0.8
The Innovative Form Builder – IvyForms v0.8
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 1.2 1.3
ivyforms / backend / src / Repository / FieldOptions / FieldOptionsRepository.php

FieldOptionsRepository.php in The Innovative Form Builder – IvyForms 0.8, at backend/src/Repository/FieldOptions/FieldOptionsRepository.php

245 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @copyright © Melograno Venture Studio. All rights reserved.
5 * @licence See LICENCE.md for license details.
6 */
7
8 namespace IvyForms\Repository\FieldOptions;
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 use IvyForms\Common\Exceptions\QueryExecutionException;
17 use IvyForms\Common\Exceptions\ValidationException;
18 use IvyForms\Entity\FieldOptions\FieldOptions;
19 use IvyForms\Factory\FieldOptions\FieldOptionsFactory;
20 use IvyForms\Repository\AbstractRepository;
21 use IvyForms\Services\InstallActions\DB\Field\FieldsTable;
22 use IvyForms\Services\Translations\BackendStrings;
23
24 class FieldOptionsRepository extends AbstractRepository implements FieldOptionsRepositoryInterface
25 {
26 public const FACTORY = FieldOptionsFactory::class;
27
28 /**
29 * @throws QueryExecutionException
30 */
31 public function add($option): int
32 {
33 $data = $option->toArray();
34
35 $result = $this->wpdb->insert(
36 $this->table,
37 [
38 'fieldId' => $data['fieldId'],
39 'label' => $data['label'],
40 'value' => $data['value'],
41 'isDefault' => (int) $data['isDefault'],
42 'position' => $data['position'] ?? 1,
43 ],
44 ['%d', '%s', '%s', '%d', '%d']
45 );
46
47 if ($result === false) {
48 throw new QueryExecutionException(BackendStrings::getExceptionStrings()['unable_to_add_data'] . __CLASS__);
49 }
50
51 return $this->wpdb->insert_id;
52 }
53
54 /**
55 * @throws QueryExecutionException
56 */
57 public function update(int $id, object $entity): bool
58 {
59 $data = $entity->toArray();
60 $result = $this->wpdb->update(
61 $this->table,
62 [
63 'fieldId' => $data['fieldId'],
64 'label' => $data['label'],
65 'value' => $data['value'],
66 'isDefault' => (int)$data['isDefault'],
67 'position' => $data['position'] ?? 1,
68 ],
69 ['id' => $id],
70 ['%d', '%s', '%s', '%d', '%d'],
71 ['%d']
72 );
73
74 if ($result === false) {
75 throw new QueryExecutionException(BackendStrings::getAllFormsStrings()['unable_to_save_data'] . __CLASS__);
76 }
77
78 return (bool)$result;
79 }
80
81 /**
82 * Delete all options for a given field ID
83 * @param int $fieldId
84 * @return bool
85 * @throws QueryExecutionException
86 */
87 public function deleteByFieldId(int $fieldId): bool
88 {
89 $result = $this->wpdb->delete(
90 $this->table,
91 ['fieldId' => $fieldId],
92 ['%d']
93 );
94 if ($result === false) {
95 throw new QueryExecutionException(
96 BackendStrings::getExceptionStrings()['unable_to_delete_data'] . __CLASS__
97 );
98 }
99 return true;
100 }
101
102 /**
103 * Bulk delete all options for multiple field IDs.
104 * @param array<int> $fieldIds
105 * @return int Number of deleted rows
106 * @throws QueryExecutionException
107 */
108 public function deleteByFieldIds(array $fieldIds): int
109 {
110 if (empty($fieldIds)) {
111 return 0;
112 }
113 $placeholders = implode(',', array_fill(0, count($fieldIds), '%d'));
114 $query = "DELETE FROM {$this->table} WHERE fieldId IN ($placeholders)";
115 $result = $this->wpdb->query($this->wpdb->prepare($query, ...$fieldIds));
116 if ($result === false) {
117 throw new QueryExecutionException(
118 BackendStrings::getExceptionStrings()['failed_delete_by_foreign_keys'] . __CLASS__
119 );
120 }
121 return $result;
122 }
123
124 /**
125 * Bulk delete all options for field IDs matching a column and values (safe, prevents SQL injection).
126 * @param string $columnName
127 * @param array<int> $values
128 * @return int Number of deleted rows
129 * @throws QueryExecutionException|InvalidArgumentException
130 */
131 public function deleteByFieldIdsByColumnValues(string $columnName, array $values): int
132 {
133 // Whitelist allowed columns
134 $allowedColumns = ['formId'];
135 if (!in_array($columnName, $allowedColumns, true)) {
136 throw new InvalidArgumentException(
137 BackendStrings::getExceptionStrings()['invalid_column_name_deletion']
138 );
139 }
140 if (empty($values)) {
141 return 0;
142 }
143 $fieldsTable = FieldsTable::getTableName();
144 $placeholders = implode(',', array_fill(0, count($values), '%d'));
145 $query = "DELETE FROM {$this->table} WHERE fieldId IN
146 (SELECT id FROM {$fieldsTable} WHERE {$columnName} IN ($placeholders))";
147 $result = $this->wpdb->query($this->wpdb->prepare($query, ...$values));
148 if ($result === false) {
149 throw new QueryExecutionException(
150 BackendStrings::getExceptionStrings()['failed_delete_by_foreign_keys'] . __CLASS__
151 );
152 }
153 return $result;
154 }
155
156 /**
157 * Get all options for a given field ID, ordered by position
158 * @param int $fieldId
159 * @return array<int, FieldOptions>
160 * @throws ValidationException
161 * @throws QueryExecutionException
162 */
163 public function getByFieldId(int $fieldId): array
164 {
165 $rows = $this->wpdb->get_results(
166 $this->wpdb->prepare(
167 "SELECT * FROM $this->table WHERE fieldId = %d ORDER BY position ASC",
168 $fieldId
169 ),
170 ARRAY_A
171 );
172 if ($rows === false) {
173 throw new QueryExecutionException(
174 BackendStrings::getExceptionStrings()['unable_find_by_id'] . __CLASS__
175 );
176 }
177 if (empty($rows)) {
178 return [];
179 }
180 return array_map(function ($row) {
181 return call_user_func([static::FACTORY, 'create'], $row);
182 }, $rows);
183 }
184
185 /**
186 * Batch add FieldOptions entities.
187 * @param array<object> $entities
188 * @return array<int> Inserted IDs
189 * @throws QueryExecutionException
190 */
191 public function addMany(array $entities): array
192 {
193 if (empty($entities)) {
194 return [];
195 }
196 $insertedIds = [];
197 foreach ($entities as $option) {
198 $insertedIds[] = $this->add($option);
199 }
200 return $insertedIds;
201 }
202
203 /**
204 * Batch update FieldOptions entities by ID.
205 * @param array<object> $entities
206 * @return int Number of updated rows
207 * @throws QueryExecutionException
208 */
209 public function updateMany(array $entities): int
210 {
211 if (empty($entities)) {
212 return 0;
213 }
214 $updatedCount = 0;
215 foreach ($entities as $option) {
216 if ($this->update($option->getId(), $option)) {
217 $updatedCount++;
218 }
219 }
220 return $updatedCount;
221 }
222
223 /**
224 * Bulk delete options by their own IDs (not field IDs).
225 * @param array<int> $optionIds
226 * @return int Number of deleted rows
227 * @throws QueryExecutionException
228 */
229 public function deleteByOptionIds(array $optionIds): int
230 {
231 if (empty($optionIds)) {
232 return 0;
233 }
234 $placeholders = implode(',', array_fill(0, count($optionIds), '%d'));
235 $query = "DELETE FROM {$this->table} WHERE id IN ($placeholders)";
236 $result = $this->wpdb->query($this->wpdb->prepare($query, ...$optionIds));
237 if ($result === false) {
238 throw new QueryExecutionException(
239 BackendStrings::getExceptionStrings()['failed_delete_by_ids'] . __CLASS__
240 );
241 }
242 return $result;
243 }
244 }
245