]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Entity/Entitled.php
Remove 'svn' lines
[galette.git] / galette / lib / Galette / Entity / Entitled.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Entitleds handling
7 *
8 * PHP version 5
9 *
10 * Copyright © 2007-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 2007-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 * @link http://galette.tuxfamily.org
34 * @since Available since 0.7dev - 2007-10-27
35 */
36
37 namespace Galette\Entity;
38
39 use Analog\Analog;
40 use Laminas\Db\Sql\Expression;
41 use Laminas\Db\Adapter\Adapter;
42 use Galette\Core\Db;
43
44 /**
45 * Entitled handling. Manage:
46 * - id
47 * - label
48 * - extra (that may differ from one entity to another)
49 *
50 * @category Entity
51 * @name Entitled
52 * @package Galette
53 * @author Johan Cwiklinski <johan@x-tnd.be>
54 * @copyright 2007-2014 The Galette Team
55 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
56 * @link http://galette.tuxfamily.org
57 * @since Available since 0.7dev - 2007-10-27
58 */
59
60 abstract class Entitled
61 {
62 use I18nTrait;
63
64 const ID_NOT_EXITS = -1;
65
66 private $zdb;
67 private $table;
68 private $fpk;
69 private $flabel;
70 private $fthird;
71 private $used;
72
73 public static $fields;
74 protected static $defaults;
75
76 protected $order_field = false;
77
78 private $id;
79 private $label;
80 private $third;
81
82 private $errors = array();
83
84 /**
85 * Default constructor
86 *
87 * @param Db $zdb Database
88 * @param string $table Table name
89 * @param string $fpk Primary key field name
90 * @param string $flabel Label fields name
91 * @param string $fthird The third field name
92 * @param string $used Table name for isUsed function
93 * @param mixed $args Either an int or a resultset to load
94 */
95 public function __construct(Db $zdb, $table, $fpk, $flabel, $fthird, $used, $args = null)
96 {
97 $this->zdb = $zdb;
98 $this->table = $table;
99 $this->fpk = $fpk;
100 $this->flabel = $flabel;
101 $this->fthird = $fthird;
102 $this->used = $used;
103 if (is_int($args)) {
104 $this->load($args);
105 } elseif (is_object($args)) {
106 $this->loadFromRS($args);
107 }
108 }
109
110 /**
111 * Loads an entry from its id
112 *
113 * @param int $id Entry ID
114 *
115 * @return boolean true if query succeed, false otherwise
116 */
117 public function load($id)
118 {
119 try {
120 $select = $this->zdb->select($this->table);
121 $select->where($this->fpk . ' = ' . $id);
122
123 $results = $this->zdb->execute($select);
124 if ($results->count() > 0) {
125 $result = $results->current();
126 $this->loadFromRS($result);
127
128 return true;
129 } else {
130 throw new \RuntimeException(
131 'Unknown ID ' . $id . '!'
132 );
133 }
134 } catch (\Exception $e) {
135 Analog::log(
136 'Cannot load ' . $this->getType() . ' from id `' . $id . '` | ' .
137 $e->getMessage(),
138 Analog::WARNING
139 );
140 return false;
141 }
142 }
143
144 /**
145 * Populate object from a resultset row
146 *
147 * @param ResultSet $r the resultset row
148 *
149 * @return void
150 */
151 private function loadFromRS($r)
152 {
153 $pk = $this->fpk;
154 $this->id = $r->$pk;
155 $flabel = $this->flabel;
156 $this->label = $r->$flabel;
157 $fthird = $this->fthird;
158 $this->third = $r->$fthird;
159 }
160
161 /**
162 * Set defaults at install time
163 *
164 * @return boolean|Exception
165 */
166 public function installInit()
167 {
168 $class = get_class($this);
169
170 try {
171 //first, we drop all values
172 $delete = $this->zdb->delete($this->table);
173 $this->zdb->execute($delete);
174
175 $values = array();
176 foreach ($class::$fields as $key => $f) {
177 $values[$f] = ':' . $key;
178 }
179
180 $insert = $this->zdb->insert($this->table);
181 $insert->values($values);
182 $stmt = $this->zdb->sql->prepareStatementForSqlObject($insert);
183
184 $this->zdb->handleSequence(
185 $this->table,
186 count(static::$defaults)
187 );
188
189 $fnames = array_keys($values);
190 foreach ($class::$defaults as $d) {
191 $val = null;
192 if (isset($d['priority'])) {
193 $val = $d['priority'];
194 } else {
195 $val = $d['extension'];
196 }
197
198 $stmt->execute(
199 array(
200 $fnames[0] => $d['id'],
201 $fnames[1] => $d['libelle'],
202 $fnames[2] => $val
203 )
204 );
205 }
206
207 Analog::log(
208 'Defaults (' . $this->getType() .
209 ') were successfully stored into database.',
210 Analog::INFO
211 );
212 return true;
213 } catch (\Exception $e) {
214 Analog::log(
215 'Unable to initialize defaults (' . $this->getType() . ').' .
216 $e->getMessage(),
217 Analog::WARNING
218 );
219 return $e;
220 }
221 }
222
223 /**
224 * Get list in an array built as:
225 * $array[id] = "translated label"
226 *
227 * @param int $extent Filter on (non) cotisations types
228 *
229 * @return array|false
230 */
231 public function getList($extent = null)
232 {
233 $list = array();
234
235 try {
236 $select = $this->zdb->select($this->table);
237 $fields = array($this->fpk, $this->flabel);
238 if (
239 $this->order_field !== false
240 && $this->order_field !== $this->fpk
241 && $this->order_field !== $this->flabel
242 ) {
243 $fields[] = $this->order_field;
244 }
245 $select->quantifier('DISTINCT');
246 $select->columns($fields);
247
248 if ($this->order_field !== false) {
249 $select->order($this->order_field, $this->fpk);
250 }
251 if ($extent !== null) {
252 if ($extent === true) {
253 $select->where(array($this->fthird => new Expression('true')));
254 } elseif ($extent === false) {
255 $select->where(array($this->fthird => new Expression('false')));
256 }
257 }
258
259 $results = $this->zdb->execute($select);
260
261 foreach ($results as $r) {
262 $fpk = $this->fpk;
263 $flabel = $this->flabel;
264 $list[$r->$fpk] = _T($r->$flabel);
265 }
266 return $list;
267 } catch (\Exception $e) {
268 Analog::log(
269 __METHOD__ . ' | ' . $e->getMessage(),
270 Analog::ERROR
271 );
272 return false;
273 }
274 }
275
276 /**
277 * Complete list
278 *
279 * @return array of all objects if succeed, false otherwise
280 */
281 public function getCompleteList()
282 {
283 $list = array();
284
285 try {
286 $select = $this->zdb->select($this->table);
287 if ($this->order_field !== false) {
288 $select->order(array($this->order_field, $this->fpk));
289 }
290
291 $results = $this->zdb->execute($select);
292
293 if ($results->count() == 0) {
294 Analog::log(
295 'No entries (' . $this->getType() . ') defined in database.',
296 Analog::INFO
297 );
298 } else {
299 $pk = $this->fpk;
300 $flabel = $this->flabel;
301 $fprio = $this->fthird;
302
303 foreach ($results as $r) {
304 $list[$r->$pk] = array(
305 'name' => _T($r->$flabel),
306 'extra' => $r->$fprio
307 );
308 }
309 }
310 return $list;
311 } catch (\Exception $e) {
312 Analog::log(
313 'Cannot list entries (' . $this->getType() .
314 ') | ' . $e->getMessage(),
315 Analog::WARNING
316 );
317 return false;
318 }
319 }
320
321 /**
322 * Get a entry
323 *
324 * @param integer $id Entry ID
325 *
326 * @return mixed|false Row if succeed ; false: no such id
327 */
328 public function get($id)
329 {
330 if (!is_numeric($id)) {
331 $this->errors[] = _T("ID must be an integer!");
332 return false;
333 }
334
335 try {
336 $select = $this->zdb->select($this->table);
337 $select->where($this->fpk . '=' . $id);
338
339 $results = $this->zdb->execute($select);
340 $result = $results->current();
341
342 if (!$result) {
343 $this->errors[] = _T("Label does not exist");
344 return false;
345 }
346
347 return $result;
348 } catch (\Exception $e) {
349 Analog::log(
350 __METHOD__ . ' | ' . $e->getMessage(),
351 Analog::WARNING
352 );
353 return false;
354 }
355 }
356
357 /**
358 * Get a label
359 *
360 * @param integer $id Id
361 * @param boolean $translated Do we want translated or original label?
362 * Defaults to true.
363 *
364 * @return string
365 */
366 public function getLabel($id, $translated = true)
367 {
368 $res = $this->get($id);
369 if ($res === false) {
370 //get() alred logged
371 return self::ID_NOT_EXITS;
372 };
373 $field = $this->flabel;
374 return ($translated) ? _T($res->$field) : $res->$field;
375 }
376
377 /**
378 * Get an ID from a label
379 *
380 * @param string $label The label
381 *
382 * @return int|false Return id if it exists false otherwise
383 */
384 public function getIdByLabel($label)
385 {
386 try {
387 $pk = $this->fpk;
388 $select = $this->zdb->select($this->table);
389 $select->columns(array($pk))
390 ->where(array($this->flabel => $label));
391
392 $results = $this->zdb->execute($select);
393 $result = $results->current();
394 if ($result) {
395 return $result->$pk;
396 } else {
397 return false;
398 }
399 } catch (\Exception $e) {
400 Analog::log(
401 'Unable to retrieve ' . $this->getType() . ' from label `' .
402 $label . '` | ' . $e->getMessage(),
403 Analog::ERROR
404 );
405 return false;
406 }
407 }
408
409 /**
410 * Add a new entry
411 *
412 * @param string $label The label
413 * @param integer $extra Extra values (priority for statuses,
414 * extension for contributions types, ...)
415 *
416 * @return integer id if success ; -1 : DB error ; -2 : label already exists
417 */
418 public function add($label, $extra)
419 {
420 // Avoid duplicates.
421 $ret = $this->getIdByLabel($label);
422
423 if ($ret !== false) {
424 Analog::log(
425 $this->getType() . ' with label `' . $label . '` already exists',
426 Analog::WARNING
427 );
428 return -2;
429 }
430
431 try {
432 $this->zdb->connection->beginTransaction();
433 $values = array(
434 $this->flabel => $label,
435 $this->fthird => $extra
436 );
437
438 $insert = $this->zdb->insert($this->table);
439 $insert->values($values);
440
441 $ret = $this->zdb->execute($insert);
442
443 if ($ret->count() > 0) {
444 Analog::log(
445 'New ' . $this->getType() . ' `' . $label .
446 '` added successfully.',
447 Analog::INFO
448 );
449
450 if ($this->zdb->isPostgres()) {
451 $this->id = $this->zdb->driver->getLastGeneratedValue(
452 PREFIX_DB . $this->table . '_id_seq'
453 );
454 } else {
455 $this->id = $this->zdb->driver->getLastGeneratedValue();
456 }
457
458 $this->addTranslation($label);
459 } else {
460 throw new \Exception('New ' . $this->getType() . ' not added.');
461 }
462 $this->zdb->connection->commit();
463 return true;
464 } catch (\Exception $e) {
465 $this->zdb->connection->rollBack();
466 Analog::log(
467 'Unable to add new entry `' . $label . '` | ' .
468 $e->getMessage(),
469 Analog::ERROR
470 );
471 throw $e;
472 }
473 }
474
475 /**
476 * Update in database.
477 *
478 * @param integer $id Entry ID
479 * @param string $label The label
480 * @param integer $extra Extra values (priority for statuses,
481 * extension for contributions types, ...)
482 *
483 * @return ID_NOT_EXITS|boolean
484 */
485 public function update($id, $label, $extra)
486 {
487 $ret = $this->get($id);
488 if (!$ret) {
489 /* get() already logged and set $this->error. */
490 return self::ID_NOT_EXITS;
491 }
492
493 $class = get_class($this);
494
495 try {
496 $oldlabel = $ret->{$this->flabel};
497 $this->zdb->connection->beginTransaction();
498 $values = array(
499 $this->flabel => $label,
500 $this->fthird => $extra
501 );
502
503 $update = $this->zdb->update($this->table);
504 $update->set($values);
505 $update->where($this->fpk . ' = ' . $id);
506
507 $ret = $this->zdb->execute($update);
508
509 if ($oldlabel != $label) {
510 $this->deleteTranslation($oldlabel);
511 $this->addTranslation($label);
512 }
513
514 Analog::log(
515 $this->getType() . ' #' . $id . ' updated successfully.',
516 Analog::INFO
517 );
518 $this->zdb->connection->commit();
519 return true;
520 } catch (\Exception $e) {
521 $this->zdb->connection->rollBack();
522 Analog::log(
523 'Unable to update ' . $this->getType() . ' #' . $id . ' | ' .
524 $e->getMessage(),
525 Analog::ERROR
526 );
527 throw $e;
528 }
529 }
530
531 /**
532 * Delete entry
533 *
534 * @param integer $id Entry ID
535 *
536 * @return ID_NOT_EXITS|boolean
537 */
538 public function delete($id)
539 {
540 $ret = $this->get($id);
541 if (!$ret) {
542 /* get() already logged */
543 return self::ID_NOT_EXITS;
544 }
545
546 if ($this->isUsed($id)) {
547 $this->errors[] = _T("Cannot delete this label: it's still used");
548 return false;
549 }
550
551 try {
552 $this->zdb->connection->beginTransaction();
553 $delete = $this->zdb->delete($this->table);
554 $delete->where($this->fpk . ' = ' . $id);
555
556 $this->zdb->execute($delete);
557 $this->deleteTranslation($ret->{$this->flabel});
558
559 Analog::log(
560 $this->getType() . ' ' . $id . ' deleted successfully.',
561 Analog::INFO
562 );
563
564 $this->zdb->connection->commit();
565 return true;
566 } catch (\Exception $e) {
567 $this->zdb->connection->rollBack();
568 Analog::log(
569 'Unable to delete ' . $this->getType() . ' ' . $id .
570 ' | ' . $e->getMessage(),
571 Analog::ERROR
572 );
573 throw $e;
574 }
575 }
576
577 /**
578 * Check if this entry is used.
579 *
580 * @param integer $id Entry ID
581 *
582 * @return boolean
583 */
584 public function isUsed($id)
585 {
586 try {
587 $select = $this->zdb->select($this->used);
588 $select->where($this->fpk . ' = ' . $id);
589
590 $results = $this->zdb->execute($select);
591 $result = $results->current();
592
593 if ($result !== null) {
594 return true;
595 } else {
596 return false;
597 }
598 } catch (\Exception $e) {
599 Analog::log(
600 'Unable to check if ' . $this->getType . ' `' . $id .
601 '` is used. | ' . $e->getMessage(),
602 Analog::ERROR
603 );
604 //in case of error, we consider that it is used, to avoid errors
605 return true;
606 }
607 }
608
609 /**
610 * Get textual type representation
611 *
612 * @return string
613 */
614 abstract protected function getType();
615
616 /**
617 * Get translated textual representation
618 *
619 * @return string
620 */
621 abstract public function getI18nType();
622
623 /**
624 * Global getter method
625 *
626 * @param string $name name of the property we want to retrive
627 *
628 * @return false|object the called property
629 */
630 public function __get($name)
631 {
632 $forbidden = array();
633 $virtuals = array('extension', 'libelle');
634 if (
635 in_array($name, $virtuals)
636 || !in_array($name, $forbidden)
637 && isset($this->$name)
638 ) {
639 switch ($name) {
640 case 'libelle':
641 return _T($this->label);
642 break;
643 case 'extension':
644 return $this->third;
645 break;
646 default:
647 return $this->$name;
648 break;
649 }
650 } else {
651 return false;
652 }
653 }
654
655 /**
656 * Get errors
657 *
658 * @return array
659 */
660 public function getErrors(): array
661 {
662 return $this->errors;
663 }
664 }