1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Ui\Table\Column;
6:
7: use Atk4\Core\AppScopeTrait;
8: use Atk4\Core\NameTrait;
9: use Atk4\Data\Field;
10: use Atk4\Data\Model;
11: use Atk4\Data\Persistence;
12: use Atk4\Data\Type\Types as CustomTypes;
13: use Atk4\Ui\App;
14: use Atk4\Ui\SessionTrait;
15: use Doctrine\DBAL\Types\Types;
16:
17: /**
18: * Implement a generic filter model for filtering column data.
19: */
20: abstract class FilterModel extends Model
21: {
22: use AppScopeTrait; // needed for SessionTrait
23: use NameTrait; // needed for SessionTrait
24: use SessionTrait;
25:
26: /** @var Field The operator for defining a condition on a field. */
27: public $op;
28:
29: /** @var Field The value for defining a condition on a field. */
30: public $value;
31:
32: /** @var bool Determines if this field shouldn't have a value field, and use only op field. */
33: public $noValueField = false;
34:
35: /** The field where this filter need to query data. */
36: public Field $lookupField;
37:
38: public function __construct(App $app, array $defaults = [])
39: {
40: $this->setApp($app);
41:
42: $persistence = new Persistence\Array_();
43:
44: parent::__construct($persistence, $defaults);
45: }
46:
47: /**
48: * Factory method that will return a FilterModel Type class.
49: */
50: public static function factoryType(App $app, Field $field): self
51: {
52: $class = [
53: Types::STRING => FilterModel\TypeString::class,
54: Types::TEXT => FilterModel\TypeString::class,
55:
56: Types::BOOLEAN => FilterModel\TypeBoolean::class,
57: Types::INTEGER => FilterModel\TypeNumber::class,
58: Types::FLOAT => FilterModel\TypeNumber::class,
59: CustomTypes::MONEY => FilterModel\TypeNumber::class,
60:
61: Types::DATE_MUTABLE => FilterModel\TypeDate::class,
62: Types::DATE_IMMUTABLE => FilterModel\TypeDate::class,
63: Types::TIME_MUTABLE => FilterModel\TypeTime::class,
64: Types::TIME_IMMUTABLE => FilterModel\TypeTime::class,
65: Types::DATETIME_MUTABLE => FilterModel\TypeDatetime::class,
66: Types::DATETIME_IMMUTABLE => FilterModel\TypeDatetime::class,
67:
68: Types::JSON => FilterModel\TypeString::class,
69:
70: 'TODO we do not support enum type, any type can be enum' => FilterModel\TypeEnum::class,
71: ][$field->type];
72:
73: // you can set your own filter model class
74: if (isset($field->ui['filterModel'])) {
75: if ($field->ui['filterModel'] instanceof self) {
76: return $field->ui['filterModel'];
77: }
78: $class = $field->ui['filterModel'];
79: }
80:
81: $filterModel = new $class($app, ['lookupField' => $field]);
82:
83: return $filterModel;
84: }
85:
86: #[\Override]
87: protected function init(): void
88: {
89: parent::init();
90:
91: $this->op = $this->addField('op', ['ui' => ['caption' => '']]);
92:
93: if (!$this->noValueField) {
94: $this->value = $this->addField('value', ['ui' => ['caption' => '']]);
95: }
96:
97: $this->afterInit();
98: }
99:
100: public function afterInit(): void
101: {
102: $this->addField('name', ['default' => $this->lookupField->shortName, 'system' => true]);
103:
104: // create a name for our filter model to save as session data
105: $this->name = 'filter_model_' . $this->lookupField->shortName;
106:
107: if ($this->getApp()->tryGetRequestQueryParam('atk_clear_filter') ?? false) {
108: $this->forget();
109: }
110:
111: // add hook in order to persist data in session
112: $this->onHook(self::HOOK_AFTER_SAVE, function (Model $model) {
113: $this->memorize('data', $model->get());
114: });
115: }
116:
117: public function recallData(): ?array
118: {
119: return $this->recall('data');
120: }
121:
122: /**
123: * Method that will set conditions on a model base on $op and $value value.
124: * Each FilterModel\TypeModel should override this method.
125: *
126: * @return Model
127: */
128: abstract public function setConditionForModel(Model $model);
129:
130: /**
131: * Method that will set Field display condition in a form.
132: * If form filter need to have a field display at certain condition, then
133: * override this method in your FilterModel\TypeModel.
134: */
135: public function getFormDisplayRules(): array
136: {
137: return [];
138: }
139:
140: public function clearData(): void
141: {
142: $this->forget();
143: }
144: }
145: