src/Table/Column/FilterModel.php
<?php
declare(strict_types=1);
namespace Atk4\Ui\Table\Column;
use Atk4\Core\AppScopeTrait;
use Atk4\Core\NameTrait;
use Atk4\Data\Field;
use Atk4\Data\Model;
use Atk4\Data\Persistence;
use Atk4\Data\Type\Types as CustomTypes;
use Atk4\Ui\App;
use Atk4\Ui\SessionTrait;
use Doctrine\DBAL\Types\Types;
/**
* Implement a generic filter model for filtering column data.
*/
abstract class FilterModel extends Model
{
use AppScopeTrait; // needed for SessionTrait
use NameTrait; // needed for SessionTrait
use SessionTrait;
/** @var Field The operator for defining a condition on a field. */
public $op;
/** @var Field The value for defining a condition on a field. */
public $value;
/** @var bool Determines if this field shouldn't have a value field, and use only op field. */
public $noValueField = false;
/** The field where this filter need to query data. */
public Field $lookupField;
public function __construct(App $app, array $defaults = [])
{
$this->setApp($app);
$persistence = new Persistence\Array_();
parent::__construct($persistence, $defaults);
}
/**
* Factory method that will return a FilterModel Type class.
*/
public static function factoryType(App $app, Field $field): self
{
$class = [
Types::STRING => FilterModel\TypeString::class,
Types::TEXT => FilterModel\TypeString::class,
Types::BOOLEAN => FilterModel\TypeBoolean::class,
Types::SMALLINT => FilterModel\TypeNumber::class,
Types::INTEGER => FilterModel\TypeNumber::class,
Types::BIGINT => FilterModel\TypeNumber::class,
Types::FLOAT => FilterModel\TypeNumber::class,
CustomTypes::MONEY => FilterModel\TypeNumber::class,
Types::DATE_MUTABLE => FilterModel\TypeDate::class,
Types::DATE_IMMUTABLE => FilterModel\TypeDate::class,
Types::TIME_MUTABLE => FilterModel\TypeTime::class,
Types::TIME_IMMUTABLE => FilterModel\TypeTime::class,
Types::DATETIME_MUTABLE => FilterModel\TypeDatetime::class,
Types::DATETIME_IMMUTABLE => FilterModel\TypeDatetime::class,
Types::JSON => FilterModel\TypeString::class,
'TODO we do not support enum type, any type can be enum' => FilterModel\TypeEnum::class,
][$field->type] ?? null;
// you can set your own filter model class
if (isset($field->ui['filterModel'])) {
if ($field->ui['filterModel'] instanceof self) {
return $field->ui['filterModel'];
}
$class = $field->ui['filterModel'];
}
$filterModel = new $class($app, ['lookupField' => $field]);
return $filterModel;
}
#[\Override]
protected function init(): void
{
parent::init();
$this->op = $this->addField('op', ['ui' => ['caption' => '']]);
if (!$this->noValueField) {
$this->value = $this->addField('value', ['ui' => ['caption' => '']]);
}
$this->afterInit();
}
public function afterInit(): void
{
// create a name for our filter model to save as session data
$this->name = 'filter_model_' . $this->lookupField->shortName;
if ($this->getApp()->tryGetRequestQueryParam('atk_clear_filter') ?? false) {
$this->forget();
}
// add hook in order to persist data in session
$this->onHook(self::HOOK_AFTER_SAVE, function (Model $entity) {
$this->memorize('data', $entity->get());
});
}
/**
* @return array<string, mixed>|null
*/
public function recallData(): ?array
{
return $this->recall('data');
}
/**
* Method that will set conditions on a model base on $op and $value value.
* Each FilterModel\TypeModel should override this method.
*/
abstract public function setConditionForModel(Model $model): void;
/**
* Method that will set Field display condition in a form.
* If form filter need to have a field display at certain condition, then
* override this method in your FilterModel\TypeModel.
*
* @return array<string, array<mixed>>
*/
public function getFormDisplayRules(): array
{
return [];
}
public function clearData(): void
{
$this->forget();
}
}