3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
10 * Copyright © 2007-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 2007-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 - 2007-10-27
37 namespace Galette\Entity
;
42 use Laminas\Db\Sql\Expression
;
44 use Galette\Features\I18n
;
47 * Entitled handling. Manage:
50 * - extra (that may differ from one entity to another)
55 * @author Johan Cwiklinski <johan@x-tnd.be>
56 * @copyright 2007-2023 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 * @since Available since 0.7dev - 2007-10-27
61 * @property integer $id
62 * @property string $label
63 * @property string $libelle
64 * @property mixed $third
65 * @property mixed $extension
68 abstract class Entitled
72 public const ID_NOT_EXITS
= -1;
81 public static $fields;
82 protected static $defaults;
84 /** @var string|false */
85 protected $order_field = false;
91 private $errors = array();
96 * @param Db $zdb Database
97 * @param string $table Table name
98 * @param string $fpk Primary key field name
99 * @param string $flabel Label fields name
100 * @param string $fthird The third field name
101 * @param string $used Table name for isUsed function
102 * @param mixed $args Either an int or a resultset to load
104 public function __construct(Db
$zdb, $table, $fpk, $flabel, $fthird, $used, $args = null)
107 $this->table
= $table;
109 $this->flabel
= $flabel;
110 $this->fthird
= $fthird;
114 } elseif (is_object($args)) {
115 $this->loadFromRS($args);
120 * Loads an entry from its id
122 * @param int $id Entry ID
124 * @return boolean true if query succeed, false otherwise
126 public function load($id)
129 $select = $this->zdb
->select($this->table
);
130 $select->where([$this->fpk
=> $id]);
132 $results = $this->zdb
->execute($select);
133 if ($results->count() > 0) {
134 /** @var ArrayObject $result */
135 $result = $results->current();
136 $this->loadFromRS($result);
146 } catch (Throwable
$e) {
148 'Cannot load ' . $this->getType() . ' from id `' . $id . '` | ' .
157 * Populate object from a resultset row
159 * @param ArrayObject $r the resultset row
163 private function loadFromRS(ArrayObject
$r)
167 $flabel = $this->flabel
;
168 $this->label
= $r->$flabel;
169 $fthird = $this->fthird
;
170 $this->third
= $r->$fthird;
174 * Set defaults at install time
179 public function installInit()
181 $class = get_class($this);
184 //first, we drop all values
185 $delete = $this->zdb
->delete($this->table
);
186 $this->zdb
->execute($delete);
189 foreach ($class::$fields as $key => $f) {
190 $values[$f] = ':' . $key;
193 $insert = $this->zdb
->insert($this->table
);
194 $insert->values($values);
195 $stmt = $this->zdb
->sql
->prepareStatementForSqlObject($insert);
197 $this->zdb
->handleSequence(
199 count(static::$defaults)
202 $fnames = array_values($values);
203 foreach ($class::$defaults as $d) {
205 if (isset($d['priority'])) {
206 $val = $d['priority'];
208 $val = $d['extension'];
213 $fnames[0] => $d['id'],
214 $fnames[1] => $d['libelle'],
221 'Defaults (' . $this->getType() .
222 ') were successfully stored into database.',
226 } catch (Throwable
$e) {
228 'Unable to initialize defaults (' . $this->getType() . ').' .
237 * Get list in an array built as:
238 * $array[id] = "translated label"
240 * @param boolean|null $extent Filter on (non) cotisations types
242 * @return array|false
244 public function getList(bool $extent = null)
249 $select = $this->zdb
->select($this->table
);
250 $fields = array($this->fpk
, $this->flabel
);
252 $this->order_field
!== false
253 && $this->order_field
!== $this->fpk
254 && $this->order_field
!== $this->flabel
256 $fields[] = $this->order_field
;
258 $select->quantifier('DISTINCT');
259 $select->columns($fields);
261 if ($this->order_field
!== false) {
262 $select->order($this->order_field
);
264 if ($extent !== null) {
265 if ($extent === true) {
266 $select->where(array($this->fthird
=> new Expression('true')));
267 } elseif ($extent === false) {
268 $select->where(array($this->fthird
=> new Expression('false')));
272 $results = $this->zdb
->execute($select);
274 foreach ($results as $r) {
276 $flabel = $this->flabel
;
277 $list[$r->$fpk] = _T($r->$flabel);
280 } catch (Throwable
$e) {
282 __METHOD__
. ' | ' . $e->getMessage(),
292 * @return array of all objects if succeeded, false otherwise
294 public function getCompleteList()
299 $select = $this->zdb
->select($this->table
);
300 if ($this->order_field
!== false) {
301 $select->order(array($this->order_field
, $this->fpk
));
304 $results = $this->zdb
->execute($select);
306 if ($results->count() == 0) {
308 'No entries (' . $this->getType() . ') defined in database.',
313 $flabel = $this->flabel
;
314 $fprio = $this->fthird
;
316 foreach ($results as $r) {
317 $list[$r->$pk] = array(
318 'name' => _T($r->$flabel),
319 'extra' => $r->$fprio
324 } catch (Throwable
$e) {
326 'Cannot list entries (' . $this->getType() .
327 ') | ' . $e->getMessage(),
337 * @param integer $id Entry ID
339 * @return mixed|false Row if succeed ; false: no such id
341 public function get($id)
343 if (!is_numeric($id)) {
344 $this->errors
[] = _T("ID must be an integer!");
349 $select = $this->zdb
->select($this->table
);
350 $select->where([$this->fpk
=> $id]);
352 $results = $this->zdb
->execute($select);
353 $result = $results->current();
356 $this->errors
[] = _T("Label does not exist");
361 } catch (Throwable
$e) {
363 __METHOD__
. ' | ' . $e->getMessage(),
373 * @param integer $id Id
374 * @param boolean $translated Do we want translated or original label?
379 public function getLabel($id, $translated = true)
381 $res = $this->get($id);
382 if ($res === false) {
383 //get() already logged
384 return self
::ID_NOT_EXITS
;
386 $field = $this->flabel
;
387 return ($translated) ?
_T($res->$field) : $res->$field;
391 * Get an ID from a label
393 * @param string $label The label
395 * @return int|false Return id if it exists false otherwise
397 public function getIdByLabel($label)
401 $select = $this->zdb
->select($this->table
);
402 $select->columns(array($pk))
403 ->where(array($this->flabel
=> $label));
405 $results = $this->zdb
->execute($select);
406 $result = $results->current();
412 } catch (Throwable
$e) {
414 'Unable to retrieve ' . $this->getType() . ' from label `' .
415 $label . '` | ' . $e->getMessage(),
425 * @param string $label The label
426 * @param integer $extra Extra values (priority for statuses,
427 * extension for contributions types, ...)
429 * @return bool|integer -2 : label already exists
431 public function add($label, $extra)
434 $label = strip_tags($label);
435 $ret = $this->getIdByLabel($label);
437 if ($ret !== false) {
439 $this->getType() . ' with label `' . $label . '` already exists',
446 $this->zdb
->connection
->beginTransaction();
448 $this->flabel
=> $label,
449 $this->fthird
=> $extra
452 $insert = $this->zdb
->insert($this->table
);
453 $insert->values($values);
455 $ret = $this->zdb
->execute($insert);
457 if ($ret->count() > 0) {
459 'New ' . $this->getType() . ' `' . $label .
460 '` added successfully.',
464 $this->id
= $this->zdb
->getLastGeneratedValue($this);
466 $this->addTranslation($label);
468 throw new \
Exception('New ' . $this->getType() . ' not added.');
470 $this->zdb
->connection
->commit();
472 } catch (Throwable
$e) {
473 $this->zdb
->connection
->rollBack();
475 'Unable to add new entry `' . $label . '` | ' .
484 * Update in database.
486 * @param integer $id Entry ID
487 * @param string $label The label
488 * @param integer $extra Extra values (priority for statuses,
489 * extension for contributions types, ...)
491 * @return self::ID_NOT_EXITS|boolean
493 public function update($id, $label, $extra)
495 $label = strip_tags($label);
496 $ret = $this->get($id);
498 /* get() already logged and set $this->error. */
499 return self
::ID_NOT_EXITS
;
503 $oldlabel = $ret->{$this->flabel
};
504 $this->zdb
->connection
->beginTransaction();
506 $this->flabel
=> $label,
507 $this->fthird
=> $extra
510 $update = $this->zdb
->update($this->table
);
511 $update->set($values);
512 $update->where([$this->fpk
=> $id]);
514 $this->zdb
->execute($update);
516 if ($oldlabel != $label) {
517 $this->deleteTranslation($oldlabel);
518 $this->addTranslation($label);
522 $this->getType() . ' #' . $id . ' updated successfully.',
525 $this->zdb
->connection
->commit();
527 } catch (Throwable
$e) {
528 $this->zdb
->connection
->rollBack();
530 'Unable to update ' . $this->getType() . ' #' . $id . ' | ' .
541 * @param integer $id Entry ID
543 * @return self::ID_NOT_EXITS|boolean
545 public function delete($id)
547 $ret = $this->get($id);
549 /* get() already logged */
550 return self
::ID_NOT_EXITS
;
553 if ($this->isUsed($id)) {
554 $this->errors
[] = _T("Cannot delete this label: it's still used");
559 $this->zdb
->connection
->beginTransaction();
560 $delete = $this->zdb
->delete($this->table
);
561 $delete->where([$this->fpk
=> $id]);
563 $this->zdb
->execute($delete);
564 $this->deleteTranslation($ret->{$this->flabel
});
567 $this->getType() . ' ' . $id . ' deleted successfully.',
571 $this->zdb
->connection
->commit();
573 } catch (Throwable
$e) {
574 $this->zdb
->connection
->rollBack();
576 'Unable to delete ' . $this->getType() . ' ' . $id .
577 ' | ' . $e->getMessage(),
585 * Check if this entry is used.
587 * @param integer $id Entry ID
591 public function isUsed($id)
594 $select = $this->zdb
->select($this->used
);
595 $select->where([$this->fpk
=> $id]);
597 $results = $this->zdb
->execute($select);
598 $result = $results->current();
600 if ($result !== null) {
605 } catch (Throwable
$e) {
607 'Unable to check if ' . $this->getType() . ' `' . $id .
608 '` is used. | ' . $e->getMessage(),
611 //in case of error, we consider that it is used, to avoid errors
617 * Get textual type representation
621 abstract protected function getType();
624 * Get translated textual representation
628 abstract public function getI18nType();
631 * Global getter method
633 * @param string $name name of the property we want to retrive
635 * @return mixed the called property
637 public function __get($name)
639 $forbidden = array();
640 $virtuals = array('extension', 'libelle');
642 in_array($name, $virtuals)
643 ||
!in_array($name, $forbidden)
644 && isset($this->$name)
648 return _T($this->label
);
660 * Global isset method
661 * Required for twig to access properties via __get
663 * @param string $name name of the property we want to retrive
667 public function __isset($name)
669 $forbidden = array();
670 $virtuals = array('extension', 'libelle');
672 in_array($name, $virtuals)
673 ||
!in_array($name, $forbidden)
674 && isset($this->$name)
687 public function getErrors(): array
689 return $this->errors
;