4 * Copyright © 2003-2024 The Galette Team
6 * This file is part of Galette (https://galette.eu).
8 * Galette is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * Galette is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
22 namespace Galette\Filters
;
24 use Galette\Helpers\DatesHelper
;
27 use Galette\Entity\Status
;
28 use Galette\Entity\ContributionsTypes
;
29 use Galette\Repository\Members
;
30 use Galette\DynamicFields\DynamicField
;
31 use Galette\Repository\PaymentTypes
;
34 * Members list filters and paginator
36 * @author Johan Cwiklinski <johan@x-tnd.be>
38 * @property ?string $creation_date_begin
39 * @property ?string $creation_date_end
40 * @property ?string $modif_date_begin
41 * @property ?string $modif_date_end
42 * @property ?string $due_date_begin
43 * @property ?string $due_date_end
44 * @property ?string $birth_date_begin
45 * @property ?string $birth_date_end
46 * @property int $show_public_infos
47 * @property array|integer $status
48 * @property ?string $contrib_creation_date_begin
49 * @property ?string $contrib_creation_date_end
50 * @property ?string $contrib_begin_date_begin
51 * @property ?string $contrib_begin_date_end
52 * @property ?string $contrib_end_date_begin
53 * @property ?string $contrib_end_date_end
54 * @property array $contributions_types
55 * @property array $payments_types
56 * @property ?float $contrib_min_amount
57 * @property ?float $contrib_max_amount
58 * @property array $contrib_dynamic
59 * @property array $free_search
60 * @property array $groups_search
61 * @property integer $groups_search_log_op
63 * @property-read ?string $rcreation_date_begin
64 * @property-read ?string $rcreation_date_end
65 * @property-read ?string $rmodif_date_begin
66 * @property-read ?string $rmodif_date_end
67 * @property-read ?string $rdue_date_begin
68 * @property-read ?string $rdue_date_end
69 * @property-read ?string $rbirth_date_begin
70 * @property-read ?string $rbirth_date_end
71 * @property-read ?string $rcontrib_creation_date_begin
72 * @property-read ?string $rcontrib_creation_date_end
73 * @property-read ?string $rcontrib_begin_date_begin
74 * @property-read ?string $rcontrib_begin_date_end
75 * @property-read ?string $rcontrib_end_date_begin
76 * @property-read ?string $rcontrib_end_date_end
77 * @property-read array $search_fields
80 class AdvancedMembersList
extends MembersList
84 public const OP_AND
= 0;
85 public const OP_OR
= 1;
87 public const OP_EQUALS
= 0;
88 public const OP_CONTAINS
= 1;
89 public const OP_NOT_EQUALS
= 2;
90 public const OP_NOT_CONTAINS
= 3;
91 public const OP_STARTS_WITH
= 4;
92 public const OP_ENDS_WITH
= 5;
93 public const OP_BEFORE
= 6;
94 public const OP_AFTER
= 7;
96 private ?
string $creation_date_begin = null;
97 private ?
string $creation_date_end = null;
98 private ?
string $modif_date_begin = null;
99 private ?
string $modif_date_end = null;
100 private ?
string $due_date_begin = null;
101 private ?
string $due_date_end = null;
102 private ?
string $birth_date_begin = null;
103 private ?
string $birth_date_end = null;
104 private int $show_public_infos = Members
::FILTER_DC_PUBINFOS
;
105 /** @var array<int> */
106 private array $status = array();
107 private ?
string $contrib_creation_date_begin = null;
108 private ?
string $contrib_creation_date_end = null;
109 private ?
string $contrib_begin_date_begin = null;
110 private ?
string $contrib_begin_date_end = null;
111 private ?
string $contrib_end_date_begin = null;
112 private ?
string $contrib_end_date_end = null;
113 /** @var array<int> */
114 private array $contributions_types = array();
115 /** @var array<int> */
116 private array $payments_types = array();
117 private ?
float $contrib_min_amount = null;
118 private ?
float $contrib_max_amount = null;
120 /** @var array<string> */
121 protected array $advancedmemberslist_fields = array(
122 'creation_date_begin',
132 'contrib_creation_date_begin',
133 'contrib_creation_date_end',
134 'contrib_begin_date_begin',
135 'contrib_begin_date_end',
136 'contrib_end_date_begin',
137 'contrib_end_date_end',
138 'contributions_types',
140 'contrib_min_amount',
141 'contrib_max_amount',
145 'groups_search_log_op'
148 /** @var array<string> */
149 protected array $virtuals_advancedmemberslist_fields = array(
150 'rcreation_date_begin',
151 'rcreation_date_end',
158 'rcontrib_creation_date_begin',
159 'rcontrib_creation_date_end',
160 'rcontrib_begin_date_begin',
161 'rcontrib_begin_date_end',
162 'rcontrib_end_date_begin',
163 'rcontrib_end_date_end',
168 * an empty free search criteria to begin
170 * @var array<string,mixed>
172 private array $free_search = array(
176 'log_op' => self
::OP_AND
,
177 'qry_op' => self
::OP_EQUALS
182 * an empty group search criteria to begin
184 * @var array<string,mixed>
186 private array $groups_search = array(
192 //defaults to 'OR' for group search
193 private int $groups_search_log_op = self
::OP_OR
;
196 * an empty contributions dynamic field criteria to begin
198 * @var array<string,mixed>
200 private array $contrib_dynamic = array();
203 * Default constructor
205 * @param ?MembersList $simple A simple filter search to keep
207 public function __construct(MembersList
$simple = null)
209 parent
::__construct();
210 if ($simple instanceof MembersList
) {
211 foreach ($this->pagination_fields
as $pf) {
212 $this->$pf = $simple->$pf;
214 foreach ($this->memberslist_fields
as $mlf) {
215 $this->$mlf = $simple->$mlf;
221 * Do we want to filter within contributions?
225 public function withinContributions(): bool
228 $this->contrib_creation_date_begin
!= null
229 ||
$this->contrib_creation_date_end
!= null
230 ||
$this->contrib_begin_date_begin
!= null
231 ||
$this->contrib_begin_date_end
!= null
232 ||
$this->contrib_end_date_begin
!= null
233 ||
$this->contrib_end_date_end
!= null
234 ||
$this->contrib_min_amount
!= null
235 ||
$this->contrib_max_amount
!= null
236 ||
count($this->contrib_dynamic
) > 0
237 ||
count($this->contributions_types
) > 0
238 ||
count($this->payments_types
) > 0
247 * Reinit default parameters
251 public function reinit(): void
255 $this->creation_date_begin
= null;
256 $this->creation_date_end
= null;
257 $this->modif_date_begin
= null;
258 $this->modif_date_end
= null;
259 $this->due_date_begin
= null;
260 $this->due_date_end
= null;
261 $this->birth_date_begin
= null;
262 $this->birth_date_end
= null;
263 $this->show_public_infos
= Members
::FILTER_DC_PUBINFOS
;
264 $this->status
= array();
266 $this->contrib_creation_date_begin
= null;
267 $this->contrib_creation_date_end
= null;
268 $this->contrib_begin_date_begin
= null;
269 $this->contrib_begin_date_end
= null;
270 $this->contrib_end_date_begin
= null;
271 $this->contrib_begin_date_end
= null;
272 $this->contributions_types
= array();
273 $this->payments_types
= array();
275 $this->free_search
= array(
279 'log_op' => self
::OP_AND
,
280 'qry_op' => self
::OP_EQUALS
284 $this->contrib_dynamic
= array();
286 $this->groups_search
= array(
292 $this->groups_search_log_op
= self
::OP_OR
;
296 * Global getter method
298 * @param string $name name of the property we want to retrieve
300 * @return mixed the called property
302 public function __get(string $name): mixed
305 in_array($name, $this->pagination_fields
)
306 ||
in_array($name, $this->memberslist_fields
)
308 return parent
::__get($name);
311 in_array($name, $this->advancedmemberslist_fields
)
312 ||
in_array($name, $this->virtuals_advancedmemberslist_fields
)
315 case 'creation_date_begin':
316 case 'creation_date_end':
317 case 'modif_date_begin':
318 case 'modif_date_end':
319 case 'due_date_begin':
321 case 'birth_date_begin':
322 case 'birth_date_end':
323 case 'contrib_creation_date_begin':
324 case 'contrib_creation_date_end':
325 case 'contrib_begin_date_begin':
326 case 'contrib_begin_date_end':
327 case 'contrib_end_date_begin':
328 case 'contrib_end_date_end':
329 return $this->getDate($name);
330 case 'rcreation_date_begin':
331 case 'rcreation_date_end':
332 case 'rmodif_date_begin':
333 case 'rmodif_date_end':
334 case 'rdue_date_begin':
335 case 'rdue_date_end':
336 case 'rbirth_date_begin':
337 case 'rbirth_date_end':
338 case 'rcontrib_creation_date_begin':
339 case 'rcontrib_creation_date_end':
340 case 'rcontrib_begin_date_begin':
341 case 'rcontrib_begin_date_end':
342 case 'rcontrib_end_date_begin':
343 case 'rcontrib_end_date_end':
344 $rname = substr($name, 1);
345 return $this->getDate($rname, true, false);
346 case 'search_fields':
347 $search_fields = array_merge($this->memberslist_fields
, $this->advancedmemberslist_fields
);
348 $key = array_search('selected', $search_fields);
349 unset($search_fields[$key]);
350 $key = array_search('unreachable', $search_fields);
351 unset($search_fields[$key]);
352 $key = array_search('query', $search_fields);
353 unset($search_fields[$key]);
354 return $search_fields;
360 throw new \
RuntimeException(
362 'Unable to get property "%s::%s"!',
370 * Global isset method
371 * Required for twig to access properties via __get
373 * @param string $name name of the property we want to retrieve
377 public function __isset(string $name): bool
380 in_array($name, $this->pagination_fields
)
381 ||
in_array($name, $this->memberslist_fields
)
386 in_array($name, $this->advancedmemberslist_fields
)
387 ||
in_array($name, $this->virtuals_advancedmemberslist_fields
)
397 * Global setter method
399 * @param string $name name of the property we want to assign a value to
400 * @param mixed $value a relevant value for the property
404 public function __set(string $name, mixed $value): void
406 global $zdb, $preferences, $login;
409 in_array($name, $this->pagination_fields
)
410 ||
in_array($name, $this->memberslist_fields
)
412 parent
::__set($name, $value);
415 '[AdvancedMembersList] Setting property `' . $name . '`',
420 case 'creation_date_begin':
421 case 'creation_date_end':
422 case 'modif_date_begin':
423 case 'modif_date_end':
424 case 'due_date_begin':
426 case 'birth_date_begin':
427 case 'birth_date_end':
428 case 'contrib_creation_date_begin':
429 case 'contrib_creation_date_end':
430 case 'contrib_begin_date_begin':
431 case 'contrib_begin_date_end':
432 case 'contrib_end_date_begin':
433 case 'contrib_end_date_end':
434 $this->setFilterDate($name, $value, str_contains($name, 'begin'));
436 case 'contrib_min_amount':
437 case 'contrib_max_amount':
438 if (is_float($value)) {
439 $this->$name = $value;
441 if ($value !== null) {
443 'Incorrect amount for ' . $name . '! ' .
444 'Should be a float (' . gettype($value) . ' given)',
450 case 'show_public_infos':
451 if (is_numeric($value)) {
452 $this->$name = $value;
455 '[AdvancedMembersList] Value for property `' . $name .
456 '` should be an integer (' . gettype($value) . ' given)',
462 if (!is_array($value)) {
463 $value = array($value);
465 $this->status
= array();
466 foreach ($value as $v) {
467 if (is_numeric($v)) {
468 //check status existence
469 $s = new Status($zdb);
471 if ($res !== false) {
472 $this->status
[] = $v;
475 'Status #' . $v . ' does not exists!',
481 '[AdvancedMembersList] Value for status filter should be an '
482 . 'integer (' . gettype($v) . ' given',
488 case 'contributions_types':
489 if (!is_array($value)) {
490 $value = array($value);
492 $this->contributions_types
= array();
493 foreach ($value as $v) {
494 if (is_numeric($v)) {
495 //check type existence
496 $s = new ContributionsTypes($zdb);
498 if ($res !== false) {
499 $this->contributions_types
[] = $v;
502 'Contribution type #' . $v . ' does not exists!',
508 '[AdvancedMembersList] Value for contribution type '
509 . 'filter should be an integer (' . gettype($v) .
516 case 'payments_types':
517 if (!is_array($value)) {
518 $value = array($value);
520 $this->payments_types
= array();
521 $ptypes = new PaymentTypes(
526 $ptlist = $ptypes->getList();
528 foreach ($value as $v) {
529 if (is_numeric($v)) {
530 if (isset($ptlist[$v])) {
531 $this->payments_types
[] = $v;
534 'Payment type #' . $v . ' does not exists!',
540 '[AdvancedMembersList] Value for payment type filter should be an '
541 . 'integer (' . gettype($v) . ' given',
548 if (isset($this->free_search
['empty']) && !isset($value['empty'])) {
549 unset($this->free_search
['empty']);
552 if ($this->isValidFreeSearch($value)) {
553 //should this happen?
559 foreach ($values as $value) {
560 if ($this->isValidFreeSearch($value)) {
563 //handle value according to type
564 switch ($value['type']) {
565 case DynamicField
::DATE
:
566 if ($value['search'] !== null && trim($value['search']) !== '') {
568 $d = \DateTime
::createFromFormat(__("Y-m-d"), $value['search']);
570 throw new \
Exception('Incorrect format');
572 $value['search'] = $d->format('Y-m-d');
573 } catch (Throwable
$e) {
575 'Incorrect date format for ' . $value['field'] .
576 '! was: ' . $value['search'],
584 $this->free_search
[$id] = $value;
587 '[AdvancedMembersList] bad construct for free filter',
593 case 'contrib_dynamic':
594 if (is_array($value)) {
595 $this->contrib_dynamic
= $value;
598 '[AdvancedMembersList] Value for dynamic contribution fields filter should be an '
599 . 'array (' . gettype($value) . ' given',
604 case 'groups_search':
605 if (isset($this->groups_search
['empty'])) {
606 unset($this->groups_search
['empty']);
608 if (is_array($value)) {
610 isset($value['group'])
611 && isset($value['idx'])
614 unset($value['idx']);
615 $this->groups_search
[$id] = $value;
618 '[AdvancedMembersList] bad construct for group filter',
624 '[AdvancedMembersList] Value for group filter should be an '
625 . 'array (' . gettype($value) . ' given',
630 case 'groups_search_log_op':
631 if ($value == self
::OP_AND ||
$value == self
::OP_OR
) {
632 $this->groups_search_log_op
= $value;
635 '[AdvancedMembersList] Value for group filter logical operator should be '
636 . ' in [0,1] (' . gettype($value) . '-> ' . $value . ' given )',
643 substr($name, 0, 4) === 'cds_'
644 ||
substr($name, 0, 5) === 'cdsc_'
646 if (is_array($value) ||
trim($value) !== '') {
648 if (substr($name, 0, 5) === 'cdsc_') {
649 $id = substr($name, 5, strlen($name));
651 $id = substr($name, 4, strlen($name));
653 $this->contrib_dynamic
[$id] = $value;
657 '[AdvancedMembersList] Unable to set property `' .
668 * Validate free search internal array
670 * @param array<string,mixed> $data Array to validate
674 public static function isValidFreeSearch(array $data): bool
676 return isset($data['field'])
677 && isset($data['search'])
678 && isset($data['log_op'])
679 && isset($data['qry_op'])
680 && isset($data['idx'])
681 && isset($data['type']);