3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
6 * Dynamic fields handle, aggregating field descriptors and values
10 * Copyright © 2011-2023 The Galette Team
12 * This file is part of Galette (http://galette.tuxfamily.org).
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.
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.
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/>.
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2011-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.7dev - 2011-06-20
37 namespace Galette\Entity
;
40 use Galette\DynamicFields\File
;
41 use Galette\DynamicFields\Separator
;
44 use Laminas\Db\Adapter\Driver\StatementInterface
;
46 use Galette\Core\Login
;
47 use Galette\Core\Authentication
;
48 use Galette\DynamicFields\DynamicField
;
49 use Galette\Repository\DynamicFieldsSet
;
52 * Dynamic fields handle, aggregating field descriptors and values
54 * @name DynamicFieldsHandle
58 * @author Johan Cwiklinski <johan@x-tnd.be>
59 * @copyright 2011-2023 The Galette Team
60 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
61 * @link http://galette.tuxfamily.org
64 class DynamicFieldsHandle
66 public const TABLE
= 'dynamic_fields';
68 private $dynamic_fields = [];
69 private $current_values = [];
73 private $errors = array();
82 private $has_changed = false;
87 * @param Db $zdb Database instance
88 * @param Login $login Login instance
89 * @param mixed $instance Object instance
91 public function __construct(Db
$zdb, Login
$login, $instance = null)
94 $this->login
= $login;
95 if ($instance !== null) {
96 $this->load($instance);
101 * Load dynamic fields values for specified object
103 * @param mixed $object Object instance
107 public function load($object)
109 $this->form_name
= $object->getFormName();
112 $this->item_id
= $object->id
;
113 $fields = new DynamicFieldsSet($this->zdb
, $this->login
);
114 $this->dynamic_fields
= $fields->getList($this->form_name
);
116 $results = $this->getCurrentFields();
118 if ($results->count() > 0) {
119 foreach ($results as $f) {
120 if (isset($this->dynamic_fields
[$f->{DynamicField
::PK
}])) {
121 $field = $this->dynamic_fields
[$f->{DynamicField
::PK
}];
122 if ($field->hasFixedValues()) {
123 $choices = $field->getValues();
124 if (!isset($choices[$f->field_val
])) {
125 if ($idx = array_search($f->field_val
, $choices)) {
126 //text has been stored (from CSV import?), but we want the index
127 $f->text_val
= $f->field_val
;
128 $f->field_val
= $idx;
130 //something went wrong here :(
132 'Dynamic choice value "' . $f->field_val
. '" does not exists!',
135 $f->text_val
= $f->field_val
;
138 $f->text_val
= $choices[$f->field_val
];
141 $this->current_values
[$f->{DynamicField
::PK
}][] = array_filter(
143 static function ($k) {
144 return $k != DynamicField
::PK
;
150 'Dynamic values found for ' . get_class($object) . ' #' . $this->item_id
.
151 '; but no dynamic field configured!',
160 } catch (Throwable
$e) {
162 __METHOD__
. ' | ' . $e->getMessage(),
174 public function getErrors(): array
176 return $this->errors
;
184 public function getFields(): array
186 return $this->dynamic_fields
;
190 * Get fields for search pages
194 public function getSearchFields(): array
196 $dynamics = $this->dynamic_fields
;
198 foreach ($dynamics as $key => $field) {
199 if ($field instanceof Separator ||
$field instanceof File
) {
200 unset($dynamics[$key]);
210 * @param integer $field Field ID
214 public function getValues($field): array
216 if (!isset($this->current_values
[$field])) {
217 $this->current_values
[$field][] = [
218 'item_id' => $this->item_id
,
219 'field_form' => $this->dynamic_fields
[$field]->getForm(),
225 return $this->current_values
[$field];
231 * @param integer $item Item ID
232 * @param integer $field Field ID
233 * @param integer $index Value index
234 * @param mixed $value Value
238 public function setValue($item, $field, $index, $value)
243 'field_form' => $this->dynamic_fields
[$field]->getForm(),
244 'val_index' => $index,
245 'field_val' => $value,
248 if (!isset($this->current_values
[$field][$idx])) {
249 $input['is_new'] = true;
252 $this->current_values
[$field][$idx] = $input;
258 * @param integer $item Item ID
259 * @param integer $field Field ID
260 * @param integer $index Value index
264 public function unsetValue($item, $field, $index)
267 if (isset($this->current_values
[$field][$idx])) {
268 unset($this->current_values
[$field][$idx]);
275 * @param integer $item_id Curent item id to use (will be used if current item_id is 0)
276 * @param boolean $transaction True if a transaction already exists
280 public function storeValues($item_id = null, $transaction = false)
283 if ($item_id !== null && ($this->item_id
== null ||
$this->item_id
== 0)) {
284 $this->item_id
= $item_id;
287 $this->zdb
->connection
->beginTransaction();
290 $this->handleRemovals();
292 foreach ($this->current_values
as $field_id => $values) {
293 foreach ($values as $value) {
294 $value[DynamicField
::PK
] = $field_id;
295 if ($value['item_id'] == 0) {
296 $value['item_id'] = $this->item_id
;
299 if (isset($value['is_new'])) {
300 unset($value['is_new']);
301 $this->getInsertStatement()->execute($value);
302 $this->has_changed
= true;
305 'field_val' => $value['field_val'],
306 'val_index' => $value['val_index'],
307 'item_id' => $value['item_id'],
308 'field_id' => $value['field_id'],
309 'field_form' => $value['field_form'],
310 'old_val_index' => $value['old_val_index'] ??
$value['val_index'] //:old_val_index
312 $this->getUpdateStatement()->execute($params);
313 $this->has_changed
= true;
319 $this->zdb
->connection
->commit();
322 } catch (Throwable
$e) {
324 $this->zdb
->connection
->rollBack();
327 'An error occurred storing dynamic field. Form name: ' . $this->form_name
.
328 ' | Error was: ' . $e->getMessage(),
341 * Get (and prepare if not done yet) insert statement
343 * @return StatementInterface
345 private function getInsertStatement(): StatementInterface
347 if (!isset($this->insert_stmt
)) {
348 $insert = $this->zdb
->insert(self
::TABLE
);
350 'item_id' => ':item_id',
351 'field_id' => ':field_id',
352 'field_form' => ':field_form',
353 'val_index' => ':val_index',
354 'field_val' => ':field_val'
356 $this->insert_stmt
= $this->zdb
->sql
->prepareStatementForSqlObject($insert);
358 return $this->insert_stmt
;
362 * Get (and prepare if not done yet) update statement
364 * @return StatementInterface
366 private function getUpdateStatement(): StatementInterface
368 if (!isset($this->update_stmt
)) {
369 $update = $this->zdb
->update(self
::TABLE
);
371 'field_val' => ':field_val',
372 'val_index' => ':val_index'
374 'item_id' => ':item_id',
375 'field_id' => ':field_id',
376 'field_form' => ':field_form',
377 'val_index' => ':old_val_index'
379 $this->update_stmt
= $this->zdb
->sql
->prepareStatementForSqlObject($update);
381 return $this->update_stmt
;
385 * Handle values that have been removed
389 private function handleRemovals()
391 $fields = new DynamicFieldsSet($this->zdb
, $this->login
);
392 $this->dynamic_fields
= $fields->getList($this->form_name
);
394 $results = $this->getCurrentFields();
397 if ($results->count() > 0) {
398 foreach ($results as $result) {
399 $fromdb[$result->field_id
. '_' . $result->val_index
] = [
400 'item_id' => $this->item_id
,
401 'field_form' => $this->form_name
,
402 'field_id' => $result->field_id
,
403 'val_index' => $result->val_index
408 if (!count($fromdb)) {
409 //no entry in database, nothing to do.
413 foreach ($this->current_values
as $field_id => $values) {
414 foreach ($values as $value) {
415 $key = $field_id . '_' . $value['val_index'];
416 if (isset($fromdb[$key])) {
417 unset($fromdb[$key]);
422 if (count($fromdb)) {
423 foreach ($fromdb as $entry) {
424 if ($this->delete_stmt
=== null) {
425 $delete = $this->zdb
->delete(self
::TABLE
);
427 'item_id' => ':item_id',
428 'field_form' => ':field_form',
429 'field_id' => ':field_id',
430 'val_index' => ':val_index'
432 $this->delete_stmt
= $this->zdb
->sql
->prepareStatementForSqlObject($delete);
434 $this->delete_stmt
->execute($entry);
436 $field_id = $entry['field_id'];
438 isset($this->current_values
[$field_id])
439 && count($this->current_values
[$field_id])
441 $val_index = (int)$entry['val_index'];
442 foreach ($this->current_values
[$field_id] as &$current) {
443 if ((int)$current['val_index'] === $val_index +
1) {
444 $current['val_index'] = $val_index;
446 $current['old_val_index'] = $val_index;
451 $this->has_changed
= true;
456 * Is there any change in dynamic fields?
460 public function hasChanged()
462 return $this->has_changed
;
468 * @param integer $item_id Curent item id to use (will be used if current item_id is 0)
469 * @param boolean $transaction True if a transaction already exists
473 public function removeValues($item_id = null, $transaction = false)
476 if ($item_id !== null && ($this->item_id
== null ||
$this->item_id
== 0)) {
477 $this->item_id
= $item_id;
480 $this->zdb
->connection
->beginTransaction();
483 $delete = $this->zdb
->delete(self
::TABLE
);
486 'item_id' => $this->item_id
,
487 'field_form' => $this->form_name
490 $this->zdb
->execute($delete);
493 $this->zdb
->connection
->commit();
496 } catch (Throwable
$e) {
498 $this->zdb
->connection
->rollBack();
501 'An error occurred removing dynamic field. Form name: ' . $this->form_name
.
502 ' | Error was: ' . $e->getMessage(),
510 * Get current fields resultset
512 * @return ArrayObject
514 protected function getCurrentFields()
516 $select = $this->zdb
->select(self
::TABLE
, 'd');
518 array('t' => PREFIX_DB
. DynamicField
::TABLE
),
519 'd.' . DynamicField
::PK
. '=t.' . DynamicField
::PK
,
523 'item_id' => $this->item_id
,
524 'd.field_form' => $this->form_name
528 /** only load values for accessible fields*/
529 $accessible_fields = [];
530 $access_level = $this->login
->getAccessLevel();
532 foreach ($this->dynamic_fields
as $field) {
533 $perm = $field->getPerm();
535 ($perm == DynamicField
::PERM_MANAGER
&&
536 $access_level < Authentication
::ACCESS_MANAGER
) ||
537 ($perm == DynamicField
::PERM_STAFF
&&
538 $access_level < Authentication
::ACCESS_STAFF
) ||
539 ($perm == DynamicField
::PERM_ADMIN
&&
540 $access_level < Authentication
::ACCESS_ADMIN
)
544 $accessible_fields[] = $field->getId();
547 if (count($accessible_fields)) {
548 $select->where
->in('d.' . DynamicField
::PK
, $accessible_fields);
551 $results = $this->zdb
->execute($select);