namespace Galette\Core;
use Slim\Routing\RouteParser;
-use Slim\Slim;
use Analog\Analog;
use Laminas\Db\Sql\Select;
use Slim\Views\Twig;
private ?int $counter = null;
protected ?Twig $view;
protected ?RouteParser $routeparser;
+ /** @var array<string> */
+ protected array $errors = [];
public const ORDER_ASC = 'ASC';
public const ORDER_DESC = 'DESC';
use Galette\IO\PdfContribution;
use Galette\Repository\PaymentTypes;
use Galette\Features\Dynamics;
-use Galette\Features\EntityHelper;
+use Galette\Helpers\EntityHelper;
/**
* Contribution class for galette
use ArrayObject;
use DateTime;
-use Galette\Features\EntityHelper;
+use Galette\Helpers\EntityHelper;
use Laminas\Db\Sql\Expression;
use Laminas\Db\Sql\Predicate\IsNull;
use Laminas\Db\Sql\Predicate\Operator;
use Galette\Core\History;
use Galette\Core\Login;
use Galette\Features\Dynamics;
-use Galette\Features\EntityHelper;
+use Galette\Helpers\EntityHelper;
/**
* Transaction class for galette
+++ /dev/null
-<?php
-
-/**
- * Copyright © 2003-2024 The Galette Team
- *
- * This file is part of Galette (https://galette.eu).
- *
- * Galette is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Galette is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Galette. If not, see <http://www.gnu.org/licenses/>.
- */
-
-namespace Galette\Features;
-
-use DateTime;
-use Galette\Entity\Adherent;
-use Galette\Repository\DynamicFieldsSet;
-use Throwable;
-use Analog\Analog;
-use Galette\DynamicFields\File;
-use Galette\DynamicFields\Date;
-use Galette\DynamicFields\Boolean;
-use Galette\Entity\DynamicFieldsHandle;
-
-/**
- * Dynamics fields trait
- *
- * @author Johan Cwiklinski <johan@x-tnd.be>
- */
-
-trait EntityHelper
-{
- /**
- * Fields configuration. Each field is an array and must reflect:
- * array(
- * (string)label,
- * (string)propname
- * )
- *
- * @var array<string, array<string, string>>
- */
- protected array $fields;
-
- /** @var string[] */
- //protected array $forbidden_fields = [];
-
- /** @var string[] */
- //protected array $virtual_fields = [];
-
- /**
- * Set fields, must populate $this->fields
- *
- * @return self
- */
- abstract protected function setFields(): self;
-
- /**
- * Get fields
- *
- * @return array<string, array<string, string>>
- */
- public function getFields(): array
- {
- return $this->fields;
- }
-
- /**
- * Global isset method
- * Required for twig to access properties via __get
- *
- * @param string $name name of the property we want to retrieve
- *
- * @return bool
- */
- public function __isset(string $name): bool
- {
- if (in_array($name, ($this->forbidden_fields ?? []))) {
- return false;
- }
-
- $virtual_fields = [];
- if (isset($this->virtual_fields)) {
- $virtual_fields = $this->virtual_fields;
- }
- return (in_array($name, $virtual_fields) || property_exists($this, $name));
- }
-
- /**
- * Get field label
- *
- * @param string $field Field name
- * @param string $entry Array entry to use (defaults to "label")
- *
- * @return string
- */
- public function getFieldLabel(string $field, string $entry = 'label'): string
- {
- $label = $this->fields[$field][$entry] ?? $field;
- //replace " "
- $label = str_replace(' ', ' ', $label);
- //remove trailing ':' and then trim
- $label = trim(trim($label, ':'));
- return $label;
- }
-
- /**
- * Set a Date
- *
- * @param string $field Field to store date
- * @param string $value Date to store
- *
- * @return self
- */
- protected function setDate(string $field, string $value): self
- {
- try {
- $date = \DateTime::createFromFormat(__("Y-m-d"), $value);
- if ($date === false) {
- //try with non localized date
- $date = \DateTime::createFromFormat("Y-m-d", $value);
- if ($date === false) {
- throw new \Exception('Incorrect format');
- }
- }
- if (isset($this->fields[$field]['propname'])) {
- $propname = $this->fields[$field]['propname'];
- } else {
- $propname = $field;
- }
- $this->$propname = $date->format('Y-m-d');
- } catch (Throwable $e) {
- Analog::log(
- 'Wrong date format. field: ' . $field .
- ', value: ' . $field . ', expected fmt: ' .
- __("Y-m-d") . ' | ' . $e->getMessage(),
- Analog::INFO
- );
- $this->errors[] = sprintf(
- //TRANS: %1$s is the date format, %2$s is the field name
- _T('- Wrong date format (%1$s) for %2$s!'),
- __("Y-m-d"),
- $this->getFieldLabel($field)
- );
- }
- return $this;
- }
-
- /**
- * Get a date
- *
- * @param string $field Field name to retrieve
- * @param bool $formatted Get formatted date, or DateTime object
- * @param bool $translated Get translated or db value
- *
- * @return string|DateTime|null
- */
- public function getDate(string $field, bool $formatted = true, bool $translated = true): string|DateTime|null
- {
- if ($this->$field !== null && $this->$field != '') {
- try {
- $date = new \DateTime($this->$field);
- if ($formatted === false) {
- return $date;
- }
- if ($translated === false) {
- return $date->format('Y-m-d');
- }
- return $date->format(__('Y-m-d'));
- } catch (Throwable $e) {
- //oops, we've got a bad date :/
- Analog::log(
- 'Bad date (' . $this->$field . ') | ' .
- $e->getMessage(),
- Analog::INFO
- );
- return $this->$field;
- }
- }
- return null;
- }
-}
namespace Galette\Filters;
+use Galette\Helpers\DatesHelper;
use Throwable;
use Analog\Analog;
use Galette\Entity\Status;
use Galette\Entity\ContributionsTypes;
-use Galette\Entity\Contribution;
use Galette\Repository\Members;
use Galette\DynamicFields\DynamicField;
use Galette\Repository\PaymentTypes;
class AdvancedMembersList extends MembersList
{
+ use DatesHelper;
+
public const OP_AND = 0;
public const OP_OR = 1;
case 'contrib_begin_date_end':
case 'contrib_end_date_begin':
case 'contrib_end_date_end':
- try {
- if ($this->$name !== null) {
- $d = new \DateTime($this->$name);
- return $d->format(__("Y-m-d"));
- }
- } catch (Throwable $e) {
- //oops, we've got a bad date :/
- Analog::log(
- 'Bad date (' . $this->$name . ') | ' .
- $e->getMessage(),
- Analog::INFO
- );
- return $this->$name;
- }
- break;
+ return $this->getDate($name);
case 'rcreation_date_begin':
case 'rcreation_date_end':
case 'rmodif_date_begin':
case 'rcontrib_end_date_begin':
case 'rcontrib_end_date_end':
$rname = substr($name, 1);
- return $this->$rname;
+ return $this->getDate($rname, true, false);
case 'search_fields':
$search_fields = array_merge($this->memberslist_fields, $this->advancedmemberslist_fields);
$key = array_search('selected', $search_fields);
case 'contrib_begin_date_end':
case 'contrib_end_date_begin':
case 'contrib_end_date_end':
- if ($value !== null && trim($value) !== '') {
- try {
- $d = \DateTime::createFromFormat(__("Y-m-d"), $value);
- if ($d === false) {
- throw new \Exception('Incorrect format');
- }
- $this->$name = $d->format('Y-m-d');
- } catch (Throwable $e) {
- Analog::log(
- 'Incorrect date format for ' . $name .
- '! was: ' . $value,
- Analog::WARNING
- );
- }
- }
+ $this->setFilterDate($name, $value, str_contains($name, 'begin'));
break;
case 'contrib_min_amount':
case 'contrib_max_amount':
namespace Galette\Filters;
+use Galette\Helpers\DatesHelper;
use Throwable;
use Analog\Analog;
use Galette\Core\Pagination;
class ContributionsList extends Pagination
{
+ use DatesHelper;
+
public const ORDERBY_DATE = 0;
public const ORDERBY_BEGIN_DATE = 1;
public const ORDERBY_END_DATE = 2;
switch ($name) {
case 'start_date_filter':
case 'end_date_filter':
- if ($this->$name === null) {
- return $this->$name;
- }
- try {
- $d = \DateTime::createFromFormat(__("Y-m-d"), $this->$name);
- if ($d === false) {
- //try with non localized date
- $d = \DateTime::createFromFormat("Y-m-d", $this->$name);
- if ($d === false) {
- throw new \Exception('Incorrect format');
- }
- }
- return $d->format(__("Y-m-d"));
- } catch (Throwable $e) {
- //oops, we've got a bad date :/
- Analog::log(
- 'Bad date (' . $this->$name . ') | ' .
- $e->getMessage(),
- Analog::INFO
- );
- return $this->$name;
- }
+ return $this->getDate($name);
case 'rstart_date_filter':
case 'rend_date_filter':
//same as above, but raw format
$rname = substr($name, 1);
- return $this->$rname;
+ return $this->getDate($rname, true, false);
default:
return $this->$name;
}
switch ($name) {
case 'start_date_filter':
case 'end_date_filter':
- try {
- if ($value !== '') {
- $y = \DateTime::createFromFormat(__("Y"), $value);
- if ($y !== false) {
- $month = 1;
- $day = 1;
- if ($name === 'end_date_filter') {
- $month = 12;
- $day = 31;
- }
- $y->setDate(
- (int)$y->format('Y'),
- $month,
- $day
- );
- $this->$name = $y->format('Y-m-d');
- }
-
- $ym = \DateTime::createFromFormat(__("Y-m"), $value);
- if ($y === false && $ym !== false) {
- $day = 1;
- if ($name === 'end_date_filter') {
- $day = (int)$ym->format('t');
- }
- $ym->setDate(
- (int)$ym->format('Y'),
- (int)$ym->format('m'),
- $day
- );
- $this->$name = $ym->format('Y-m-d');
- }
-
- $d = \DateTime::createFromFormat(__("Y-m-d"), $value);
- if ($y === false && $ym === false && $d !== false) {
- $this->$name = $d->format('Y-m-d');
- }
-
- if ($y === false && $ym === false && $d === false) {
- $formats = array(
- __("Y"),
- __("Y-m"),
- __("Y-m-d"),
- );
-
- $field = null;
- if ($name === 'start_date_filter') {
- $field = _T("start date filter");
- }
- if ($name === 'end_date_filter') {
- $field = _T("end date filter");
- }
-
- throw new \Exception(
- sprintf(
- //TRANS: %1$s is field name, %2$s is list of known date formats
- _T('Unknown date format for %1$s.<br/>Know formats are: %2$s'),
- $field,
- implode(', ', $formats)
- )
- );
- }
- } else {
- $this->$name = null;
- }
- } catch (Throwable $e) {
- Analog::log(
- 'Wrong date format. field: ' . $name .
- ', value: ' . $value . ', expected fmt: ' .
- __("Y-m-d") . ' | ' . $e->getMessage(),
- Analog::INFO
- );
- throw $e;
- }
+ $this->setFilterDate($name, $value, $name === 'start_date_filter');
break;
default:
$this->$name = $value;
namespace Galette\Filters;
+use Galette\Helpers\DatesHelper;
use Throwable;
use Analog\Analog;
use Galette\Core\Pagination;
class HistoryList extends Pagination
{
+ use DatesHelper;
+
public const ORDERBY_DATE = 0;
public const ORDERBY_IP = 1;
public const ORDERBY_USER = 2;
if (in_array($name, $this->list_fields)) {
switch ($name) {
case 'raw_start_date_filter':
- return $this->start_date_filter;
+ return $this->getDate('start_date_filter', true, false);
case 'raw_end_date_filter':
- return $this->end_date_filter;
+ return $this->getDate('end_date_filter', true, false);
case 'start_date_filter':
case 'end_date_filter':
- try {
- if ($this->$name !== null) {
- $d = new \DateTime($this->$name);
- return $d->format(__("Y-m-d"));
- }
- } catch (Throwable $e) {
- //oops, we've got a bad date :/
- Analog::log(
- 'Bad date (' . $this->$name . ') | ' .
- $e->getMessage(),
- Analog::INFO
- );
- }
- return $this->$name;
+ return $this->getDate($name);
default:
return $this->$name;
}
switch ($name) {
case 'start_date_filter':
case 'end_date_filter':
- try {
- if ($value !== '') {
- $y = \DateTime::createFromFormat(__("Y"), $value);
- if ($y !== false) {
- $month = 1;
- $day = 1;
- if ($name === 'end_date_filter') {
- $month = 12;
- $day = 31;
- }
- $y->setDate(
- (int)$y->format('Y'),
- $month,
- $day
- );
- $this->$name = $y->format('Y-m-d');
- }
-
- $ym = \DateTime::createFromFormat(__("Y-m"), $value);
- if ($y === false && $ym !== false) {
- $day = 1;
- if ($name === 'end_date_filter') {
- $day = (int)$ym->format('t');
- }
- $ym->setDate(
- (int)$ym->format('Y'),
- (int)$ym->format('m'),
- $day
- );
- $this->$name = $ym->format('Y-m-d');
- }
-
- $d = \DateTime::createFromFormat(__("Y-m-d"), $value);
- if ($y === false && $ym === false && $d !== false) {
- $this->$name = $d->format('Y-m-d');
- }
-
- if ($y === false && $ym === false && $d === false) {
- $formats = array(
- __("Y"),
- __("Y-m"),
- __("Y-m-d"),
- );
-
- $field = null;
- if ($name === 'start_date_filter') {
- $field = _T("start date filter");
- }
- if ($name === 'end_date_filter') {
- $field = _T("end date filter");
- }
-
- throw new \Exception(
- sprintf(
- //TRANS: %1$s is field name, %2$s is list of known date formats
- _T('Unknown date format for %1$s.<br/>Know formats are: %2$s'),
- $field,
- implode(', ', $formats)
- )
- );
- }
- } else {
- $this->$name = null;
- }
- } catch (Throwable $e) {
- Analog::log(
- 'Wrong date format. field: ' . $name .
- ', value: ' . $value . ', expected fmt: ' .
- __("Y-m-d") . ' | ' . $e->getMessage(),
- Analog::INFO
- );
- throw $e;
- }
+ $this->setFilterDate($name, $value, $name === 'start_date_filter');
break;
default:
$this->$name = $value;
namespace Galette\Filters;
+use Galette\Helpers\DatesHelper;
use Throwable;
use Analog\Analog;
use Galette\Core\Pagination;
class ScheduledPaymentsList extends Pagination
{
+ use DatesHelper;
+
public const ORDERBY_DATE = 0;
public const ORDERBY_MEMBER = 1;
public const ORDERBY_SCHEDULED_DATE = 2;
switch ($name) {
case 'start_date_filter':
case 'end_date_filter':
- if ($this->$name === null) {
- return $this->$name;
- }
- try {
- $d = \DateTime::createFromFormat(__("Y-m-d"), $this->$name);
- if ($d === false) {
- //try with non localized date
- $d = \DateTime::createFromFormat("Y-m-d", $this->$name);
- if ($d === false) {
- throw new \Exception('Incorrect format');
- }
- }
- return $d->format(__("Y-m-d"));
- } catch (Throwable $e) {
- //oops, we've got a bad date :/
- Analog::log(
- 'Bad date (' . $this->$name . ') | ' .
- $e->getMessage(),
- Analog::INFO
- );
- return $this->$name;
- }
+ return $this->getDate($name);
case 'rstart_date_filter':
case 'rend_date_filter':
//same as above, but raw format
$rname = substr($name, 1);
- return $this->$rname;
+ return $this->getDate($rname, true, false);
default:
return $this->$name;
}
switch ($name) {
case 'start_date_filter':
case 'end_date_filter':
- try {
- if ($value !== '') {
- $y = \DateTime::createFromFormat(__("Y"), $value);
- if ($y !== false) {
- $month = 1;
- $day = 1;
- if ($name === 'end_date_filter') {
- $month = 12;
- $day = 31;
- }
- $y->setDate(
- (int)$y->format('Y'),
- $month,
- $day
- );
- $this->$name = $y->format('Y-m-d');
- }
-
- $ym = \DateTime::createFromFormat(__("Y-m"), $value);
- if ($y === false && $ym !== false) {
- $day = 1;
- if ($name === 'end_date_filter') {
- $day = (int)$ym->format('t');
- }
- $ym->setDate(
- (int)$ym->format('Y'),
- (int)$ym->format('m'),
- $day
- );
- $this->$name = $ym->format('Y-m-d');
- }
-
- $d = \DateTime::createFromFormat(__("Y-m-d"), $value);
- if ($y === false && $ym === false && $d !== false) {
- $this->$name = $d->format('Y-m-d');
- }
-
- if ($y === false && $ym === false && $d === false) {
- $formats = array(
- __("Y"),
- __("Y-m"),
- __("Y-m-d"),
- );
-
- $field = null;
- if ($name === 'start_date_filter') {
- $field = _T("start date filter");
- }
- if ($name === 'end_date_filter') {
- $field = _T("end date filter");
- }
-
- throw new \Exception(
- sprintf(
- //TRANS: %1$s is field name, %2$s is list of known date formats
- _T('Unknown date format for %1$s.<br/>Know formats are: %2$s'),
- $field,
- implode(', ', $formats)
- )
- );
- }
- } else {
- $this->$name = null;
- }
- } catch (Throwable $e) {
- Analog::log(
- 'Wrong date format. field: ' . $name .
- ', value: ' . $value . ', expected fmt: ' .
- __("Y-m-d") . ' | ' . $e->getMessage(),
- Analog::INFO
- );
- throw $e;
- }
+ $this->setFilterDate($name, $value, $name === 'start_date_filter');
break;
default:
$this->$name = $value;
namespace Galette\Filters;
+use Galette\Helpers\DatesHelper;
use Throwable;
use Analog\Analog;
use Galette\Core\Pagination;
class TransactionsList extends Pagination
{
+ use DatesHelper;
+
public const ORDERBY_DATE = 0;
public const ORDERBY_MEMBER = 3;
public const ORDERBY_AMOUNT = 5;
switch ($name) {
case 'start_date_filter':
case 'end_date_filter':
- if ($this->$name === null) {
- return $this->$name;
- }
- try {
- $d = \DateTime::createFromFormat(__("Y-m-d"), $this->$name);
- if ($d === false) {
- //try with non localized date
- $d = \DateTime::createFromFormat("Y-m-d", $this->$name);
- if ($d === false) {
- throw new \Exception('Incorrect format');
- }
- }
- return $d->format(__("Y-m-d"));
- } catch (Throwable $e) {
- //oops, we've got a bad date :/
- Analog::log(
- 'Bad date (' . $this->$name . ') | ' .
- $e->getMessage(),
- Analog::INFO
- );
- return $this->$name;
- }
+ return $this->getDate($name);
case 'rstart_date_filter':
case 'rend_date_filter':
//same as above, but raw format
$rname = substr($name, 1);
- return $this->$rname;
+ return $this->getDate($rname, true, false);
default:
return $this->$name;
}
switch ($name) {
case 'start_date_filter':
case 'end_date_filter':
- try {
- if ($value !== '') {
- $y = \DateTime::createFromFormat(__("Y"), $value);
- if ($y !== false) {
- $month = 1;
- $day = 1;
- if ($name === 'end_date_filter') {
- $month = 12;
- $day = 31;
- }
- $y->setDate(
- (int)$y->format('Y'),
- $month,
- $day
- );
- $this->$name = $y->format('Y-m-d');
- }
-
- $ym = \DateTime::createFromFormat(__("Y-m"), $value);
- if ($y === false && $ym !== false) {
- $day = 1;
- if ($name === 'end_date_filter') {
- $day = (int)$ym->format('t');
- }
- $ym->setDate(
- (int)$ym->format('Y'),
- (int)$ym->format('m'),
- $day
- );
- $this->$name = $ym->format('Y-m-d');
- }
-
- $d = \DateTime::createFromFormat(__("Y-m-d"), $value);
- if ($y === false && $ym === false && $d !== false) {
- $this->$name = $d->format('Y-m-d');
- }
-
- if ($y === false && $ym === false && $d === false) {
- $formats = array(
- __("Y"),
- __("Y-m"),
- __("Y-m-d"),
- );
-
- $field = null;
- if ($name === 'start_date_filter') {
- $field = _T("start date filter");
- }
- if ($name === 'end_date_filter') {
- $field = _T("end date filter");
- }
-
- throw new \Exception(
- sprintf(
- //TRANS: %1$s is field name, %2$s is list of known date formats
- _T('Unknown date format for %1$s.<br/>Know formats are: %2$s'),
- $field,
- implode(', ', $formats)
- )
- );
- }
- } else {
- $this->$name = null;
- }
- } catch (Throwable $e) {
- Analog::log(
- 'Wrong date format. field: ' . $name .
- ', value: ' . $value . ', expected fmt: ' .
- __("Y-m-d") . ' | ' . $e->getMessage(),
- Analog::INFO
- );
- throw $e;
- }
+ $this->setFilterDate($name, $value, $name === 'start_date_filter');
break;
default:
$this->$name = $value;
--- /dev/null
+<?php
+
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Galette\Helpers;
+
+use DateTime;
+use Throwable;
+use Analog\Analog;
+
+/**
+ * Entity helper trait
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ */
+
+trait DatesHelper
+{
+ /**
+ * Set a Date
+ *
+ * @param string $field Field to store date
+ * @param string $value Date to store
+ *
+ * @return self
+ */
+ protected function setDate(string $field, string $value): self
+ {
+ try {
+ //first, try with localized date
+ $date = DateTime::createFromFormat(__("Y-m-d"), $value);
+ if ($date === false) {
+ //try with non localized date
+ $date = DateTime::createFromFormat("Y-m-d", $value);
+ if ($date === false) {
+ throw new \Exception('Incorrect format');
+ }
+ }
+
+ if (method_exists($this, 'getFieldPropertyName')) {
+ $fieldPropertyName = $this->getFieldPropertyName($field);
+ } else {
+ $fieldPropertyName = $field;
+ }
+ $this->$fieldPropertyName = $date->format('Y-m-d');
+ } catch (Throwable $e) {
+ Analog::log(
+ 'Wrong date format. field: ' . $field .
+ ', value: ' . $field . ', expected fmt: ' .
+ __("Y-m-d") . ' | ' . $e->getMessage(),
+ Analog::INFO
+ );
+
+ if (method_exists($this, 'getFieldLabel')) {
+ $fieldLabel = $this->getFieldLabel($field);
+ } else {
+ $fieldLabel = $field;
+ }
+
+ $this->errors[] = sprintf(
+ //TRANS: %1$s is the date format, %2$s is the field name
+ _T('- Wrong date format (%1$s) for %2$s!'),
+ __("Y-m-d"),
+ $fieldLabel
+ );
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set a Date for filtering
+ *
+ * @param string $field Field to store date
+ * @param string $value Date to store
+ * @param bool $start Is a start date (or is a end date))
+ *
+ * @return self
+ */
+ protected function setFilterDate(string $field, string $value, bool $start): self
+ {
+ $formats = array(
+ __("Y"),
+ __("Y-m"),
+ __("Y-m-d"),
+ );
+
+ try {
+ if ($value !== '') {
+ $y = \DateTime::createFromFormat(__("Y"), $value);
+ if ($y !== false) {
+ $month = 1;
+ $day = 1;
+ if ($start === false) {
+ $month = 12;
+ $day = 31;
+ }
+ $y->setDate(
+ (int)$y->format('Y'),
+ $month,
+ $day
+ );
+ $this->$field = $y->format('Y-m-d');
+ return $this;
+ }
+
+ $ym = \DateTime::createFromFormat(__("Y-m"), $value);
+ if ($ym !== false) {
+ $day = 1;
+ if ($start === false) {
+ $day = (int)$ym->format('t');
+ }
+ $ym->setDate(
+ (int)$ym->format('Y'),
+ (int)$ym->format('m'),
+ $day
+ );
+ $this->$field = $ym->format('Y-m-d');
+ return $this;
+ }
+
+ $d = \DateTime::createFromFormat(__("Y-m-d"), $value);
+ if ($d !== false) {
+ $this->$field = $d->format('Y-m-d');
+ return $this;
+ }
+
+ $field = null;
+ if ($start === true) {
+ $field = _T("start date filter");
+ } else {
+ $field = _T("end date filter");
+ }
+
+ throw new \Exception(
+ sprintf(
+ //TRANS: %1$s is field name, %2$s is list of known date formats
+ _T('Unknown date format for %1$s.<br/>Know formats are: %2$s'),
+ $field,
+ implode(', ', $formats)
+ )
+ );
+ } else {
+ $this->$field = null;
+ }
+ } catch (Throwable $e) {
+ Analog::log(
+ 'Wrong date format. field: ' . $field .
+ ', value: ' . $value . ', known formats: ' .
+ implode(', ', $formats) . ' | ' . $e->getMessage(),
+ Analog::INFO
+ );
+
+ if (method_exists($this, 'getFieldLabel')) {
+ $fieldLabel = $this->getFieldLabel($field);
+ } else {
+ $fieldLabel = $field;
+ }
+
+ $this->errors[] = sprintf(
+ //TRANS: %1$s is the date format, %2$s is the field name
+ _T('- Wrong date format (%1$s) for %2$s!'),
+ implode(', ', $formats),
+ $fieldLabel
+ );
+
+ throw $e;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get a date
+ *
+ * @param string $field Field name to retrieve
+ * @param bool $formatted Get formatted date, or DateTime object
+ * @param bool $translated Get translated or db value
+ *
+ * @return string|DateTime|null
+ */
+ public function getDate(string $field, bool $formatted = true, bool $translated = true): string|DateTime|null
+ {
+ if (method_exists($this, 'getFieldPropertyName')) {
+ $fieldPropertyName = $this->getFieldPropertyName($field);
+ } else {
+ $fieldPropertyName = $field;
+ }
+
+ if ($this->$fieldPropertyName !== null && $this->$fieldPropertyName != '') {
+ try {
+ $date = new DateTime($this->$fieldPropertyName);
+ if ($formatted === false) {
+ return $date;
+ }
+ if ($translated === false) {
+ return $date->format('Y-m-d');
+ }
+ return $date->format(__('Y-m-d'));
+ } catch (Throwable $e) {
+ //oops, we've got a bad date :/
+ Analog::log(
+ 'Bad date (' . $this->$fieldPropertyName . ') | ' .
+ $e->getMessage(),
+ Analog::INFO
+ );
+ return $this->$fieldPropertyName;
+ }
+ }
+
+ return null;
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace Galette\Helpers;
+
+use DateTime;
+use Throwable;
+use Analog\Analog;
+
+/**
+ * Entity helper trait
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ */
+
+trait EntityHelper
+{
+ use DatesHelper;
+
+ /**
+ * Fields configuration. Each field is an array and must reflect:
+ * array(
+ * (string)label,
+ * (string)property name
+ * )
+ *
+ * @var array<string, array<string, string>>
+ */
+ protected array $fields;
+
+ /** @var string[] */
+ //protected array $forbidden_fields = [];
+
+ /** @var string[] */
+ //protected array $virtual_fields = [];
+
+ /**
+ * Set fields, must populate $this->fields
+ *
+ * @return self
+ */
+ abstract protected function setFields(): self;
+
+ /**
+ * Get fields
+ *
+ * @return array<string, array<string, string>>
+ */
+ public function getFields(): array
+ {
+ return $this->fields;
+ }
+
+ /**
+ * Global isset method
+ * Required for twig to access properties via __get
+ *
+ * @param string $name name of the property we want to retrieve
+ *
+ * @return bool
+ */
+ public function __isset(string $name): bool
+ {
+ if (in_array($name, ($this->forbidden_fields ?? []))) {
+ return false;
+ }
+
+ $virtual_fields = [];
+ if (isset($this->virtual_fields)) {
+ $virtual_fields = $this->virtual_fields;
+ }
+ return (in_array($name, $virtual_fields) || property_exists($this, $name));
+ }
+
+ /**
+ * Get field label
+ *
+ * @param string $field Field name
+ * @param string $entry Array entry to use (defaults to "label")
+ *
+ * @return string
+ */
+ public function getFieldLabel(string $field, string $entry = 'label'): string
+ {
+ $label = $this->fields[$field][$entry] ?? $field;
+ //replace " "
+ $label = str_replace(' ', ' ', $label);
+ //remove trailing ':' and then trim
+ return trim(trim($label, ':'));
+ }
+
+ /**
+ * Get property name for given field
+ *
+ * @param string $field Field
+ *
+ * @return string
+ */
+ protected function getFieldPropertyName(string $field): string
+ {
+ if (isset($this->fields[$field]['propname'])) {
+ return $this->fields[$field]['propname'];
+ }
+ return $field;
+ }
+}