PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
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/FieldOptions/FieldOptionsRepository.php +239 -0 0.6.1 → 1.5 View file →
@@ -1,0 +1,239 @@
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 +}