]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Entity/DynamicFieldsHandle.php
Scrutinizer Auto-Fixes (#59)
[galette.git] / galette / lib / Galette / Entity / DynamicFieldsHandle.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Dynamic fields handle, aggregating field descriptors and values
7 *
8 * PHP version 5
9 *
10 * Copyright © 2011-2014 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 Entity
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2011-2014 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 * @version SVN: $Id$
34 * @link http://galette.tuxfamily.org
35 * @since Available since 0.7dev - 2011-06-20
36 */
37
38 namespace Galette\Entity;
39
40 use Analog\Analog;
41 use Laminas\Db\Sql\Expression;
42 use Laminas\Db\Sql\Predicate\Expression as PredicateExpression;
43 use Galette\Core\Db;
44 use Galette\Core\Login;
45 use Galette\Core\Authentication;
46 use Galette\DynamicFields\Separator;
47 use Galette\DynamicFields\Text;
48 use Galette\DynamicFields\Line;
49 use Galette\DynamicFields\Choice;
50 use Galette\DynamicFields\Date;
51 use Galette\DynamicFields\Boolean;
52 use Galette\DynamicFields\File;
53 use Galette\DynamicFields\DynamicField;
54 use Galette\Repository\DynamicFieldsSet;
55
56 /**
57 * Dynamic fields handle, aggregating field descriptors and values
58 *
59 * @name DynamicFieldsHandle
60 * @category Entity
61 * @package Galette
62 *
63 * @author Johan Cwiklinski <johan@x-tnd.be>
64 * @copyright 2011-2014 The Galette Team
65 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
66 * @link http://galette.tuxfamily.org
67 */
68
69 class DynamicFieldsHandle
70 {
71 const TABLE = 'dynamic_fields';
72
73 private $dynamic_fields = [];
74 private $current_values = [];
75 private $form_name;
76 private $item_id;
77
78 private $errors = array();
79
80 private $zdb;
81
82 private $insert_stmt;
83 private $update_stmt;
84 private $delete_stmt;
85
86 private $has_changed = false;
87
88 /**
89 * Default constructor
90 *
91 * @param Db $zdb Database instance
92 * @param Login $login Login instance
93 * @param mixed $instance Object instance
94 */
95 public function __construct(Db $zdb, Login $login, $instance = null)
96 {
97 $this->zdb = $zdb;
98 $this->login = $login;
99 if ($instance !== null) {
100 $this->load($instance);
101 }
102 }
103
104 /**
105 * Load dynamic fields values for specified object
106 *
107 * @param mixed $object Object instance
108 *
109 * @return array|false
110 */
111 public function load($object)
112 {
113 switch (get_class($object)) {
114 case 'Galette\Entity\Adherent':
115 $this->form_name = 'adh';
116 break;
117 case 'Galette\Entity\Contribution':
118 $this->form_name = 'contrib';
119 break;
120 case 'Galette\Entity\Transaction':
121 $this->form_name = 'trans';
122 break;
123 default:
124 throw new \RuntimeException('Class ' . get_class($object) . ' does not handle dynamic fields!');
125 break;
126 }
127
128 try {
129 $this->item_id = $object->id;
130 $fields = new DynamicFieldsSet($this->zdb, $this->login);
131 $this->dynamic_fields = $fields->getList($this->form_name);
132
133 $results = $this->getCurrentFields();
134
135 if ($results->count() > 0) {
136 $dfields = array();
137
138 foreach ($results as $f) {
139 if (isset($this->dynamic_fields[$f->{DynamicField::PK}])) {
140 $field = $this->dynamic_fields[$f->{DynamicField::PK}];
141 if ($field->hasFixedValues()) {
142 $choices = $field->getValues();
143 $f['text_val'] = $choices[$f->field_val];
144 }
145 $this->current_values[$f->{DynamicField::PK}][] = array_filter(
146 (array)$f,
147 function ($k) {
148 return $k != DynamicField::PK;
149 },
150 ARRAY_FILTER_USE_KEY
151 );
152 } else {
153 Analog::log(
154 'Dynamic values found for ' . get_class($object) . ' #' . $this->item_id .
155 '; but no dynamic field configured!',
156 Analog::WARNING
157 );
158 }
159 }
160 return true;
161 } else {
162 return false;
163 }
164 } catch (\Exception $e) {
165 Analog::log(
166 __METHOD__ . ' | ' . $e->getMessage(),
167 Analog::WARNING
168 );
169 return false;
170 }
171 }
172
173 /**
174 * Get errors
175 *
176 * @return array
177 */
178 public function getErrors()
179 {
180 return $this->errors;
181 }
182
183 /**
184 * Get fields
185 *
186 * @return array
187 */
188 public function getFields()
189 {
190 return $this->dynamic_fields;
191 }
192
193 /**
194 * Get values
195 *
196 * @param integer $field Field ID
197 *
198 * @return array
199 */
200 public function getValues($field)
201 {
202 if (!isset($this->current_values[$field])) {
203 $this->current_values[$field][] = [
204 'item_id' => '',
205 'field_form' => $this->dynamic_fields[$field]->getForm(),
206 'val_index' => '',
207 'field_val' => '',
208 'is_new' => true
209 ];
210 }
211 return $this->current_values[$field];
212 }
213
214 /**
215 * Set field value
216 *
217 * @param integer $item Item ID
218 * @param integer $field Field ID
219 * @param integer $index Value index
220 * @param mixed $value Value
221 *
222 * @return void
223 */
224 public function setValue($item, $field, $index, $value)
225 {
226 $idx = $index - 1;
227 if (isset($this->current_values[$field][$idx])) {
228 $this->current_values[$field][$idx]['field_val'] = $value;
229 } else {
230 $this->current_values[$field][$idx] = [
231 'item_id' => $item,
232 'field_form' => $this->dynamic_fields[$field]->getForm(),
233 'val_index' => $index,
234 'field_val' => $value,
235 'is_new' => true
236 ];
237 }
238 }
239
240 /**
241 * Unset field value
242 *
243 * @param integer $item Item ID
244 * @param integer $field Field ID
245 * @param integer $index Value index
246 *
247 * @return void
248 */
249 public function unsetValue($item, $field, $index)
250 {
251 $idx = $index - 1;
252 if (isset($this->current_values[$field][$idx])) {
253 unset($this->current_values[$field][$idx]);
254 }
255 }
256
257 /**
258 * Store values
259 *
260 * @param integer $item_id Curent item id to use (will be used if current item_id is 0)
261 * @param boolean $transaction True if a transaction already exists
262 *
263 * @return boolean
264 */
265 public function storeValues($item_id = null, $transaction = false)
266 {
267 try {
268 if ($item_id !== null && ($this->item_id == null || $this->item_id == 0)) {
269 $this->item_id = $item_id;
270 }
271 if (!$transaction) {
272 $this->zdb->connection->beginTransaction();
273 }
274
275 $this->handleRemovals();
276
277 foreach ($this->current_values as $field_id => $values) {
278 foreach ($values as $value) {
279 $value[DynamicField::PK] = $field_id;
280 if ($value['item_id'] == 0) {
281 $value['item_id'] = $this->item_id;
282 }
283
284 if (isset($value['is_new'])) {
285 if ($this->insert_stmt === null) {
286 $insert = $this->zdb->insert(self::TABLE);
287 $insert->values([
288 'item_id' => ':item_id',
289 'field_id' => ':field_id',
290 'field_form' => ':field_form',
291 'val_index' => ':val_index',
292 'field_val' => ':field_val'
293 ]);
294 $this->insert_stmt = $this->zdb->sql->prepareStatementForSqlObject($insert);
295 }
296 unset($value['is_new']);
297 $this->insert_stmt->execute($value);
298 $this->has_changed = true;
299 } else {
300 if ($this->update_stmt === null) {
301 $update = $this->zdb->update(self::TABLE);
302 $update->set([
303 'field_val' => ':field_val',
304 'val_index' => ':val_index'
305 ])->where([
306 'item_id' => ':item_id',
307 'field_id' => ':field_id',
308 'field_form' => ':field_form',
309 'val_index' => ':val_index'
310 ]);
311 $this->update_stmt = $this->zdb->sql->prepareStatementForSqlObject($update);
312 }
313 $params = [
314 'field_val' => $value['field_val'],
315 'val_index' => $value['val_index'],
316 'where1' => $value['item_id'],
317 'where2' => $value['field_id'],
318 'where3' => $value['field_form'],
319 'where4' => isset($value['old_val_index']) ?
320 $value['old_val_index'] : $value['val_index']
321 ];
322 $this->update_stmt->execute($params);
323 $this->has_changed = true;
324 }
325 }
326 }
327
328 if (!$transaction) {
329 $this->zdb->connection->commit();
330 }
331 return true;
332 } catch (\Exception $e) {
333 if (!$transaction) {
334 $this->zdb->connection->rollBack();
335 } else {
336 throw $e;
337 }
338 Analog::log(
339 'An error occurred storing dynamic field. Form name: ' . $this->form_name .
340 ' | Error was: ' . $e->getMessage(),
341 Analog::ERROR
342 );
343 return false;
344 } finally {
345 unset($this->update_stmt);
346 unset($this->insert_stmt);
347 }
348 }
349
350 /**
351 * Handle values that have been removed
352 *
353 * @return boolean
354 */
355 private function handleRemovals()
356 {
357 $fields = new DynamicFieldsSet($this->zdb, $this->login);
358 $this->dynamic_fields = $fields->getList($this->form_name, $this->login);
359
360 $results = $this->getCurrentFields();
361
362 $fromdb = [];
363 if ($results->count() > 0) {
364 foreach ($results as $result) {
365 $fromdb[$result->field_id . '_' . $result->val_index] = [
366 'where1' => $this->item_id,
367 'where2' => $this->form_name,
368 'where3' => $result->field_id,
369 'where4' => $result->val_index
370 ];
371 }
372 }
373
374 if (!count($fromdb)) {
375 //no entry in database, nothing to do.
376 return;
377 }
378
379 foreach ($this->current_values as $field_id => $values) {
380 foreach ($values as $value) {
381 $key = $field_id . '_' . $value['val_index'];
382 if (isset($fromdb[$key])) {
383 unset($fromdb[$key]);
384 }
385 }
386 }
387
388 if (count($fromdb)) {
389 foreach ($fromdb as $entry) {
390 if ($this->delete_stmt === null) {
391 $delete = $this->zdb->delete(self::TABLE);
392 $delete->where([
393 'item_id' => ':item_id',
394 'field_form' => ':field_form',
395 'field_id' => ':field_id',
396 'val_index' => ':val_index'
397 ]);
398 $this->delete_stmt = $this->zdb->sql->prepareStatementForSqlObject($delete);
399 }
400 $this->delete_stmt->execute($entry);
401 //update val index
402 $field_id = $entry['where3'];
403 if (isset($this->current_values[$field_id])
404 && count($this->current_values[$field_id])
405 ) {
406 $val_index = (int)$entry['where4'];
407 foreach ($this->current_values[$field_id] as &$current) {
408 if ((int)$current['val_index'] === $val_index + 1) {
409 $current['val_index'] = $val_index;
410 ++$val_index;
411 $current['old_val_index'] = $val_index;
412 }
413 }
414 }
415 }
416 $this->has_changed = true;
417 }
418 }
419
420 /**
421 * Is there any change in dynamic filelds?
422 *
423 * @return boolean
424 */
425 public function hasChanged()
426 {
427 return $this->has_changed;
428 }
429
430 /**
431 * Remove values
432 *
433 * @param integer $item_id Curent item id to use (will be used if current item_id is 0)
434 * @param boolean $transaction True if a transaction already exists
435 *
436 * @return boolean
437 */
438 public function removeValues($item_id = null, $transaction = false)
439 {
440 try {
441 if ($item_id !== null && ($this->item_id == null || $this->item_id == 0)) {
442 $this->item_id = $item_id;
443 }
444 if (!$transaction) {
445 $this->zdb->connection->beginTransaction();
446 }
447
448 $delete = $this->zdb->delete(self::TABLE);
449 $delete->where(
450 array(
451 'item_id' => $this->item_id,
452 'field_form' => $this->form_name
453 )
454 );
455 $this->zdb->execute($delete);
456
457 if (!$transaction) {
458 $this->zdb->connection->commit();
459 }
460 return true;
461 } catch (\Exception $e) {
462 if (!$transaction) {
463 $this->zdb->connection->rollBack();
464 } else {
465 throw $e;
466 }
467 Analog::log(
468 'An error occurred removing dynamic field. Form name: ' . $this->form_name .
469 ' | Error was: ' . $e->getMessage(),
470 Analog::ERROR
471 );
472 return false;
473 }
474 }
475
476 /**
477 * Get current fields resultset
478 *
479 * @return ResulSet
480 */
481 protected function getCurrentFields()
482 {
483 $select = $this->zdb->select(self::TABLE, 'd');
484 $select->join(
485 array('t' => PREFIX_DB . DynamicField::TABLE),
486 'd.' . DynamicField::PK . '=t.' . DynamicField::PK,
487 array('field_id')
488 )->where(
489 array(
490 'item_id' => $this->item_id,
491 'd.field_form' => $this->form_name
492 )
493 );
494
495 /** only load values for accessible fields*/
496 $accessible_fields = [];
497 $access_level = $this->login->getAccessLevel();
498
499 foreach ($this->dynamic_fields as $field) {
500 $perm = $field->getPerm();
501 if (($perm == DynamicField::PERM_MANAGER &&
502 $access_level < Authentication::ACCESS_MANAGER) ||
503 ($perm == DynamicField::PERM_STAFF &&
504 $access_level < Authentication::ACCESS_STAFF) ||
505 ($perm == DynamicField::PERM_ADMIN &&
506 $access_level < Authentication::ACCESS_ADMIN)
507 ) {
508 continue;
509 }
510 $accessible_fields[] = $field->getId();
511 }
512
513 if (count($accessible_fields)) {
514 $select->where->in('d.' . DynamicField::PK, $accessible_fields);
515 }
516
517 $results = $this->zdb->execute($select);
518 return $results;
519 }
520 }