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