]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/DynamicFields/DynamicField.php
edbf9f085dbab694811dcb34d8e99a082b260f5c
[galette.git] / galette / lib / Galette / DynamicFields / DynamicField.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Abstract dynamic field
7 *
8 * PHP version 5
9 *
10 * Copyright © 2012-2023 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category DynamicFields
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2012-2023 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.7.1dev - 2012-07-28
35 */
36
37 namespace Galette\DynamicFields;
38
39 use ArrayObject;
40 use Throwable;
41 use Analog\Analog;
42 use Galette\Core\Db;
43 use Galette\Entity\DynamicFieldsHandle;
44 use Galette\Features\Translatable;
45 use Galette\Features\I18n;
46 use Laminas\Db\Sql\Expression;
47 use Laminas\Db\Sql\Predicate\Expression as PredicateExpression;
48
49 /**
50 * Abstract dynamic field
51 *
52 * @name DynamicField
53 * @category DynamicFields
54 * @package Galette
55 *
56 * @author Johan Cwiklinski <johan@x-tnd.be>
57 * @copyright 2012-2023 The Galette Team
58 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
59 * @link http://galette.tuxfamily.org
60 */
61
62 abstract class DynamicField
63 {
64 use Translatable;
65 use I18n;
66
67 public const TABLE = 'field_types';
68 public const PK = 'field_id';
69
70 /** Separator field */
71 public const SEPARATOR = 0;
72 /** Simple text field */
73 public const TEXT = 1;
74 /** Line field */
75 public const LINE = 2;
76 /** Choice field (listbox) */
77 public const CHOICE = 3;
78 /** Date field */
79 public const DATE = 4;
80 /** Boolean field (checkbox) */
81 public const BOOLEAN = 5;
82 /** File field (upload) */
83 public const FILE = 6;
84
85 public const MOVE_UP = 'up';
86 public const MOVE_DOWN = 'down';
87
88 public const PERM_USER_WRITE = 0;
89 public const PERM_ADMIN = 1;
90 public const PERM_STAFF = 2;
91 public const PERM_MANAGER = 3;
92 public const PERM_USER_READ = 4;
93
94 public const DEFAULT_MAX_FILE_SIZE = 1024;
95 public const VALUES_FIELD_LENGTH = 100;
96
97 protected $has_data = false;
98 protected $has_width = false;
99 protected $has_height = false;
100 protected $has_size = false;
101 protected $multi_valued = false;
102 protected $fixed_values = false;
103 protected $has_permissions = true;
104
105 protected $id;
106 protected $index;
107 protected $perm;
108 protected $required;
109 protected $width;
110 protected $height;
111 protected $repeat;
112 protected $size;
113 protected $old_size;
114 protected $values;
115 protected $form;
116 protected $information;
117 protected $name;
118 protected $old_name;
119
120 protected $errors = [];
121
122 protected $zdb;
123
124 /**
125 * Default constructor
126 *
127 * @param Db $zdb Database instance
128 * @param mixed $args Arguments
129 */
130 public function __construct(Db $zdb, $args = null)
131 {
132 $this->zdb = $zdb;
133
134 if (is_int($args)) {
135 $this->load($args);
136 } elseif (is_object($args)) {
137 $this->loadFromRs($args);
138 }
139 }
140
141 /**
142 * Load field from its id
143 *
144 * @param Db $zdb Database instance
145 * @param int $id Field id
146 *
147 * @return DynamicField|false
148 */
149 public static function loadFieldType(Db $zdb, int $id)
150 {
151 try {
152 $select = $zdb->select(self::TABLE);
153 $select->where(['field_id' => $id]);
154
155 $results = $zdb->execute($select);
156 if ($results->count() > 0) {
157 /** @var ArrayObject $result */
158 $result = $results->current();
159 $field_type = $result->field_type;
160 $field_type = self::getFieldType($zdb, $field_type);
161 $field_type->loadFromRs($result);
162 return $field_type;
163 }
164 } catch (Throwable $e) {
165 Analog::log(
166 __METHOD__ . ' | Unable to retrieve field `' . $id .
167 '` information | ' . $e->getMessage(),
168 Analog::ERROR
169 );
170 return false;
171 }
172 return false;
173 }
174
175 /**
176 * Get correct field type instance
177 *
178 * @param Db $zdb Database instance
179 * @param int $t Field type
180 * @param int|null $id Optional dynamic field id (to load data)
181 *
182 * @return DynamicField
183 */
184 public static function getFieldType(Db $zdb, int $t, int $id = null)
185 {
186 $df = null;
187 switch ($t) {
188 case self::SEPARATOR:
189 $df = new Separator($zdb, $id);
190 break;
191 case self::TEXT:
192 $df = new Text($zdb, $id);
193 break;
194 case self::LINE:
195 $df = new Line($zdb, $id);
196 break;
197 case self::CHOICE:
198 $df = new Choice($zdb, $id);
199 break;
200 case self::DATE:
201 $df = new Date($zdb, $id);
202 break;
203 case self::BOOLEAN:
204 $df = new Boolean($zdb, $id);
205 break;
206 case self::FILE:
207 $df = new File($zdb, $id);
208 break;
209 default:
210 throw new \Exception('Unknown field type ' . $t . '!');
211 }
212 return $df;
213 }
214
215 /**
216 * Load field
217 *
218 * @param integer $id Id
219 *
220 * @return void
221 */
222 public function load(int $id): void
223 {
224 try {
225 $select = $this->zdb->select(self::TABLE);
226 $select->where([self::PK => $id]);
227
228 $results = $this->zdb->execute($select);
229 if ($results->count() > 0) {
230 /** @var ArrayObject $result */
231 $result = $results->current();
232 $this->loadFromRs($result);
233 }
234 } catch (Throwable $e) {
235 Analog::log(
236 'Unable to retrieve field type for field ' . $id . ' | ' .
237 $e->getMessage(),
238 Analog::ERROR
239 );
240 }
241 }
242
243 /**
244 * Load field type from a db ResultSet
245 *
246 * @param ArrayObject $rs ResultSet
247 * @param bool $values Whether to load values. Defaults to true
248 *
249 * @return void
250 */
251 public function loadFromRs(ArrayObject $rs, bool $values = true): void
252 {
253 $this->id = (int)$rs->field_id;
254 $this->name = $rs->field_name;
255 $this->index = (int)$rs->field_index;
256 $this->perm = (int)$rs->field_perm;
257 $this->required = $rs->field_required == 1;
258 $this->width = $rs->field_width;
259 $this->height = $rs->field_height;
260 $this->repeat = (int)$rs->field_repeat;
261 $this->size = $rs->field_size;
262 $this->form = $rs->field_form;
263 $this->information = $rs->field_information;
264 if ($values && $this->hasFixedValues()) {
265 $this->loadFixedValues();
266 }
267 }
268
269 /**
270 * Retrieve fixed values table name
271 *
272 * @param integer $id Field ID
273 * @param bool $prefixed Whether table name should be prefixed
274 *
275 * @return string
276 */
277 public static function getFixedValuesTableName(int $id, bool $prefixed = false): string
278 {
279 $name = 'field_contents_' . $id;
280 if ($prefixed === true) {
281 $name = PREFIX_DB . $name;
282 }
283 return $name;
284 }
285
286 /**
287 * Returns an array of fixed valued for a field of type 'choice'.
288 *
289 * @return void
290 */
291 private function loadFixedValues()
292 {
293 try {
294 $val_select = $this->zdb->select(
295 self::getFixedValuesTableName($this->id)
296 );
297
298 $val_select->columns(
299 array(
300 'val'
301 )
302 )->order('id');
303
304 $results = $this->zdb->execute($val_select);
305 $this->values = array();
306 if ($results->count() > 0) {
307 foreach ($results as $val) {
308 $this->values[] = $val->val;
309 }
310 }
311 } catch (Throwable $e) {
312 Analog::log(
313 __METHOD__ . ' | ' . $e->getMessage(),
314 Analog::WARNING
315 );
316 }
317 }
318
319 /**
320 * Get field type
321 *
322 * @return integer
323 */
324 abstract public function getType(): int;
325
326 /**
327 * Get field type name
328 *
329 * @return String
330 */
331 public function getTypeName(): string
332 {
333 $types = $this->getFieldsTypesNames();
334 if (isset($types[$this->getType()])) {
335 return $types[$this->getType()];
336 } else {
337 throw new \RuntimeException(
338 'Unknown type ' . $this->getType()
339 );
340 }
341 }
342
343 /**
344 * Does the field handle data?
345 *
346 * @return bool
347 */
348 public function hasData(): bool
349 {
350 return (bool)$this->has_data;
351 }
352
353 /**
354 * Does the field has width?
355 *
356 * @return bool
357 */
358 public function hasWidth(): bool
359 {
360 return (bool)$this->has_width;
361 }
362
363 /**
364 * Does the field has height?
365 *
366 * @return bool
367 */
368 public function hasHeight(): bool
369 {
370 return (bool)$this->has_height;
371 }
372
373 /**
374 * Does the field has a size?
375 *
376 * @return bool
377 */
378 public function hasSize(): bool
379 {
380 return (bool)$this->has_size;
381 }
382
383 /**
384 * Is the field multivalued?
385 *
386 * @return bool
387 */
388 public function isMultiValued(): bool
389 {
390 return (bool)$this->multi_valued;
391 }
392
393 /**
394 * Does the field has fixed values?
395 *
396 * @return bool
397 */
398 public function hasFixedValues(): bool
399 {
400 return (bool)$this->fixed_values;
401 }
402
403 /**
404 * Does the field require permissions?
405 *
406 * @return bool
407 */
408 public function hasPermissions(): bool
409 {
410 return (bool)$this->has_permissions;
411 }
412
413 /**
414 * Get field id
415 *
416 * @return integer|null
417 */
418 public function getId(): ?int
419 {
420 return $this->id;
421 }
422
423 /**
424 * Get field Permissions
425 *
426 * @return integer|null
427 */
428 public function getPerm(): ?int
429 {
430 return $this->perm;
431 }
432
433 /**
434 * Is field required?
435 *
436 * @return bool
437 */
438 public function isRequired(): bool
439 {
440 return (bool)$this->required;
441 }
442
443 /**
444 * Get field width
445 *
446 * @return integer|null
447 */
448 public function getWidth(): ?int
449 {
450 return $this->width;
451 }
452
453 /**
454 * Get field height
455 *
456 * @return integer|null
457 */
458 public function getHeight(): ?int
459 {
460 return $this->height;
461 }
462
463 /**
464 * Is current field repeatable?
465 *
466 * @return bool
467 */
468 public function isRepeatable(): bool
469 {
470 return $this->repeat != null && trim($this->repeat) != '' && (int)$this->repeat >= 0;
471 }
472
473 /**
474 * Get fields repetitions
475 *
476 * @return integer|null
477 */
478 public function getRepeat(): ?int
479 {
480 return $this->repeat;
481 }
482
483 /**
484 * Get field size
485 *
486 * @return integer|null
487 */
488 public function getSize(): ?int
489 {
490 return $this->size;
491 }
492
493 /**
494 * Get field index
495 *
496 * @return integer|null
497 */
498 public function getIndex(): ?int
499 {
500 return $this->index;
501 }
502
503 /**
504 * Get field information
505 *
506 * @return string
507 */
508 public function getInformation(): string
509 {
510 return $this->information ?? '';
511 }
512
513 /**
514 * Retrieve permissions names for display
515 *
516 * @return array
517 */
518 public static function getPermsNames(): array
519 {
520 return [
521 self::PERM_USER_WRITE => _T("User, read/write"),
522 self::PERM_STAFF => _T("Staff member"),
523 self::PERM_ADMIN => _T("Administrator"),
524 self::PERM_MANAGER => _T("Group manager"),
525 self::PERM_USER_READ => _T("User, read only")
526 ];
527 }
528
529 /**
530 * Retrieve forms names
531 *
532 * @return array
533 */
534 public static function getFormsNames(): array
535 {
536 return [
537 'adh' => _T("Members"),
538 'contrib' => _T("Contributions"),
539 'trans' => _T("Transactions")
540 ];
541 }
542
543 /**
544 * Retrieve form name
545 *
546 * @param string $form_name Form name
547 *
548 * @return string
549 */
550 public static function getFormTitle(string $form_name): string
551 {
552 $names = self::getFormsNames();
553 return $names[$form_name];
554 }
555
556 /**
557 * Get permission name
558 *
559 * @return string
560 */
561 public function getPermName(): string
562 {
563 $perms = self::getPermsNames();
564 return $perms[$this->getPerm()];
565 }
566
567 /**
568 * Get form
569 *
570 * @return string
571 */
572 public function getForm(): string
573 {
574 return $this->form;
575 }
576
577 /**
578 * Get field values
579 *
580 * @param bool $imploded Whether to implode values
581 *
582 * @return array|string|false
583 */
584 public function getValues(bool $imploded = false)
585 {
586 if (!is_array($this->values)) {
587 return false;
588 }
589 if ($imploded === true) {
590 return implode("\n", $this->values);
591 } else {
592 return $this->values;
593 }
594 }
595
596 /**
597 * Check posted values validity
598 *
599 * @param array $values All values to check, basically the $_POST array
600 * after sending the form
601 *
602 * @return bool
603 */
604 public function check(array $values)
605 {
606 $this->errors = [];
607 $this->warnings = [];
608
609 if (
610 (!isset($values['field_name']) || $values['field_name'] == '')
611 && get_class($this) != '\Galette\DynamicField\Separator'
612 ) {
613 $this->errors[] = _T('Missing required field name!');
614 } else {
615 if ($this->old_name === null && $this->name !== null && $this->name != $values['field_name']) {
616 $this->old_name = $this->name;
617 }
618 $this->name = $values['field_name'];
619 }
620
621 if (!isset($values['field_perm']) || $values['field_perm'] === '') {
622 $this->errors[] = _T('Missing required field permissions!');
623 } else {
624 if (in_array($values['field_perm'], array_keys(self::getPermsNames()))) {
625 $this->perm = $values['field_perm'];
626 } else {
627 $this->errors[] = _T('Unknown permission!');
628 }
629 }
630
631 if ($this->id === null) {
632 if (!isset($values['form_name']) || $values['form_name'] == '') {
633 $this->errors[] = _T('Missing required form!');
634 } else {
635 if (in_array($values['form_name'], array_keys(self::getFormsNames()))) {
636 $this->form = $values['form_name'];
637 } else {
638 $this->errors[] = _T('Unknown form!');
639 }
640 }
641 }
642
643 $this->required = $values['field_required'] ?? false;
644
645 if (count($this->errors) === 0 && $this->isDuplicate()) {
646 $this->errors[] = _T("- Field name already used.");
647 }
648
649 if ($this->hasWidth() && isset($values['field_width']) && trim($values['field_width']) != '') {
650 $this->width = $values['field_width'];
651 }
652
653 if ($this->hasHeight() && isset($values['field_height']) && trim($values['field_height']) != '') {
654 $this->height = $values['field_height'];
655 }
656
657 if ($this->hasSize() && isset($values['field_size']) && trim($values['field_size']) != '') {
658 $this->size = $values['field_size'];
659 }
660
661 if (isset($values['field_repeat']) && trim($values['field_repeat']) != '') {
662 $this->repeat = $values['field_repeat'];
663 }
664
665 if (isset($values['field_information']) && trim($values['field_information']) != '') {
666 global $preferences;
667 $this->information = $preferences->cleanHtmlValue($values['field_information']);
668 }
669
670 if ($this->hasFixedValues() && isset($values['fixed_values'])) {
671 $fixed_values = [];
672 foreach (explode("\n", $values['fixed_values']) as $val) {
673 $val = trim($val);
674 $len = mb_strlen($val);
675 if ($len > 0) {
676 $fixed_values[] = $val;
677 if ($len > $this->size) {
678 if ($this->old_size === null) {
679 $this->old_size = $this->size;
680 }
681 $this->size = $len;
682 }
683 }
684 }
685
686 $this->values = $fixed_values;
687 }
688
689 if ($this->id == null) {
690 $this->index = $this->getNewIndex();
691 }
692
693 if (count($this->errors) === 0) {
694 return true;
695 } else {
696 return false;
697 }
698 }
699
700 /**
701 * Store the field type
702 *
703 * @param array $values All values to check, basically the $_POST array
704 * after sending the form
705 *
706 * @return bool
707 */
708 public function store(array $values): bool
709 {
710 if (!$this->check($values)) {
711 return false;
712 }
713
714 $isnew = ($this->id === null);
715 if ($this->old_name !== null) {
716 $this->deleteTranslation($this->old_name);
717 $this->addTranslation($this->name);
718 }
719
720 try {
721 $values = array(
722 'field_name' => strip_tags($this->name),
723 'field_perm' => $this->perm,
724 'field_required' => $this->required,
725 'field_width' => ($this->width === null ? new Expression('NULL') : $this->width),
726 'field_height' => ($this->height === null ? new Expression('NULL') : $this->height),
727 'field_size' => ($this->size === null ? new Expression('NULL') : $this->size),
728 'field_repeat' => ($this->repeat === null ? new Expression('NULL') : $this->repeat),
729 'field_form' => $this->form,
730 'field_index' => $this->index,
731 'field_information' => ($this->information === null ? new Expression('NULL') : $this->information)
732 );
733
734 if ($this->required === false) {
735 //Handle booleans for postgres ; bugs #18899 and #19354
736 $values['field_required'] = $this->zdb->isPostgres() ? 'false' : 0;
737 }
738
739 if (!$isnew) {
740 $update = $this->zdb->update(self::TABLE);
741 $update->set($values)->where([self::PK => $this->id]);
742 $this->zdb->execute($update);
743 } else {
744 $values['field_type'] = $this->getType();
745 $insert = $this->zdb->insert(self::TABLE);
746 $insert->values($values);
747 $this->zdb->execute($insert);
748
749 $this->id = $this->zdb->getLastGeneratedValue($this);
750
751 if ($this->name != '') {
752 $this->addTranslation($this->name);
753 }
754 }
755 } catch (Throwable $e) {
756 Analog::log(
757 'An error occurred storing field | ' . $e->getMessage(),
758 Analog::ERROR
759 );
760 $this->errors[] = _T("An error occurred storing the field.");
761 }
762
763 if (count($this->errors) === 0 && $this->hasFixedValues()) {
764 $contents_table = self::getFixedValuesTableName($this->id, true);
765
766 try {
767 $this->zdb->drop(str_replace(PREFIX_DB, '', $contents_table), true);
768 $field_size = ((int)$this->size > 0) ? $this->size : 1;
769 $this->zdb->db->query(
770 'CREATE TABLE ' . $contents_table .
771 ' (id INTEGER NOT NULL,val varchar(' . $field_size .
772 ') NOT NULL)',
773 \Laminas\Db\Adapter\Adapter::QUERY_MODE_EXECUTE
774 );
775 } catch (Throwable $e) {
776 Analog::log(
777 'Unable to manage fields values table ' .
778 $contents_table . ' | ' . $e->getMessage(),
779 Analog::ERROR
780 );
781 $this->errors[] = _T("An error occurred creating field values table");
782 }
783
784 if (count($this->errors) == 0 && is_array($this->values)) {
785 $contents_table = self::getFixedValuesTableName($this->id);
786 try {
787 $this->zdb->connection->beginTransaction();
788
789 $insert = $this->zdb->insert($contents_table);
790 $insert->values(
791 array(
792 'id' => ':id',
793 'val' => ':val'
794 )
795 );
796 $stmt = $this->zdb->sql->prepareStatementForSqlObject($insert);
797
798 $cnt_values = count($this->values);
799 for ($i = 0; $i < $cnt_values; $i++) {
800 $stmt->execute(
801 array(
802 'id' => $i,
803 'val' => $this->values[$i]
804 )
805 );
806 }
807 $this->zdb->connection->commit();
808 } catch (Throwable $e) {
809 $this->zdb->connection->rollBack();
810 Analog::log(
811 'Unable to store field ' . $this->id . ' values (' .
812 $e->getMessage() . ')',
813 Analog::ERROR
814 );
815 $this->warnings[] = _T('An error occurred storing dynamic field values :(');
816 }
817 }
818 }
819
820 if (count($this->errors) === 0) {
821 return true;
822 } else {
823 return false;
824 }
825 }
826
827 /**
828 * Get new index
829 *
830 * @return integer
831 */
832 protected function getNewIndex(): int
833 {
834 $select = $this->zdb->select(self::TABLE);
835 $select->columns(
836 array(
837 'idx' => new \Laminas\Db\Sql\Expression('COUNT(*) + 1')
838 )
839 );
840 $select->where(['field_form' => $this->form]);
841 $results = $this->zdb->execute($select);
842 $result = $results->current();
843 $idx = $result->idx;
844 return (int)$idx;
845 }
846
847 /**
848 * Is field duplicated?
849 *
850 * @return bool
851 */
852 public function isDuplicate(): bool
853 {
854 //let's consider field is duplicated, in case of future errors
855 $duplicated = true;
856 try {
857 $select = $this->zdb->select(self::TABLE);
858 $select->columns(
859 array(
860 'cnt' => new \Laminas\Db\Sql\Expression('COUNT(' . self::PK . ')')
861 )
862 )->where(
863 array(
864 'field_form' => $this->form,
865 'field_name' => $this->name
866 )
867 );
868
869 if ($this->id !== null) {
870 $select->where->addPredicate(
871 new PredicateExpression(
872 'field_id NOT IN (?)',
873 array($this->id)
874 )
875 );
876 }
877
878 $results = $this->zdb->execute($select);
879 $result = $results->current();
880 $dup = $result->cnt;
881 if (!$dup > 0) {
882 $duplicated = false;
883 }
884 } catch (Throwable $e) {
885 Analog::log(
886 'An error occurred checking field duplicity' . $e->getMessage(),
887 Analog::ERROR
888 );
889 }
890 return $duplicated;
891 }
892
893 /**
894 * Move a dynamic field
895 *
896 * @param string $action What to do (one of self::MOVE_*)
897 *
898 * @return bool
899 */
900 public function move(string $action): bool
901 {
902 if ($action !== self::MOVE_UP && $action !== self::MOVE_DOWN) {
903 throw new \RuntimeException(('Unknown action ' . $action));
904 }
905
906 try {
907 $this->zdb->connection->beginTransaction();
908
909 $old_rank = $this->index;
910
911 $direction = $action == self::MOVE_UP ? -1 : 1;
912 $new_rank = $old_rank + $direction;
913 $update = $this->zdb->update(self::TABLE);
914 $update->set([
915 'field_index' => $old_rank
916 ])->where([
917 'field_index' => $new_rank,
918 'field_form' => $this->form
919 ]);
920 $this->zdb->execute($update);
921
922 $update = $this->zdb->update(self::TABLE);
923 $update->set(
924 array(
925 'field_index' => $new_rank
926 )
927 )->where(
928 array(
929 self::PK => $this->id
930 )
931 );
932 $this->zdb->execute($update);
933 $this->zdb->connection->commit();
934
935 return true;
936 } catch (Throwable $e) {
937 $this->zdb->connection->rollBack();
938 Analog::log(
939 'Unable to change field ' . $this->id . ' rank | ' .
940 $e->getMessage(),
941 Analog::ERROR
942 );
943 return false;
944 }
945 }
946
947 /**
948 * Delete a dynamic field
949 *
950 * @return bool
951 */
952 public function remove(): bool
953 {
954 try {
955 if ($this->hasFixedValues()) {
956 $contents_table = self::getFixedValuesTableName($this->id);
957 $this->zdb->drop($contents_table);
958 }
959
960 $this->zdb->connection->beginTransaction();
961 $old_rank = $this->index;
962
963 $update = $this->zdb->update(self::TABLE);
964 $update->set(
965 array(
966 'field_index' => new \Laminas\Db\Sql\Expression('field_index-1')
967 )
968 )->where
969 ->greaterThan('field_index', $old_rank)
970 ->equalTo('field_form', $this->form);
971 $this->zdb->execute($update);
972
973 //remove associated values
974 try {
975 $delete = $this->zdb->delete(DynamicFieldsHandle::TABLE);
976 $delete->where(
977 array(
978 'field_id' => $this->id,
979 'field_form' => $this->form
980 )
981 );
982 $this->zdb->execute($delete);
983 } catch (Throwable $e) {
984 throw new \RuntimeException('Unable to remove associated values for field ' . $this->id . '!');
985 }
986
987 //remove field type
988 try {
989 $delete = $this->zdb->delete(self::TABLE);
990 $delete->where(
991 array(
992 'field_id' => $this->id,
993 'field_form' => $this->form
994 )
995 );
996 $this->zdb->execute($delete);
997 } catch (Throwable $e) {
998 throw new \RuntimeException('Unable to remove field type ' . $this->id . '!');
999 }
1000
1001 $this->deleteTranslation($this->name);
1002
1003 $this->zdb->connection->commit();
1004
1005 return true;
1006 } catch (Throwable $e) {
1007 if ($this->zdb->connection->inTransaction()) {
1008 //because of DROP autocommit on mysql...
1009 $this->zdb->connection->rollBack();
1010 }
1011 Analog::log(
1012 'An error occurred deleting field | ' . $e->getMessage(),
1013 Analog::ERROR
1014 );
1015 return false;
1016 }
1017 }
1018
1019 /**
1020 * Retrieve fields types names
1021 *
1022 * @return array
1023 */
1024 public static function getFieldsTypesNames(): array
1025 {
1026 $names = [
1027 self::SEPARATOR => _T("separator"),
1028 self::TEXT => _T("free text"),
1029 self::LINE => _T("single line"),
1030 self::CHOICE => _T("choice"),
1031 self::DATE => _T("date"),
1032 self::BOOLEAN => _T("boolean"),
1033 self::FILE => _T("file")
1034 ];
1035 return $names;
1036 }
1037
1038 /**
1039 * Get errors
1040 *
1041 * @return array
1042 */
1043 public function getErrors(): array
1044 {
1045 return $this->errors;
1046 }
1047
1048 /**
1049 * Get warnings
1050 *
1051 * @return array
1052 */
1053 public function getWarnings(): array
1054 {
1055 return $this->warnings;
1056 }
1057 }