PluginProbe
The Innovative Form Builder – IvyForms / 1.4.1
The Innovative Form Builder – IvyForms v1.4.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
ivyforms / backend / src / Repository / FieldOptions / FieldOptionsRepository.php

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

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