diff --git a/src/Column/ColumnText.php b/src/Column/ColumnText.php new file mode 100644 index 0000000..8c0c14d --- /dev/null +++ b/src/Column/ColumnText.php @@ -0,0 +1,18 @@ +grid instanceof DataGrid); + + return $this->grid->addFilterBoolean($this->key, $this->name, $column ?? $this->column, $nullable); + } +} diff --git a/src/Component/DataGrid.php b/src/Component/DataGrid.php index 775f96a..b2b2b8a 100644 --- a/src/Component/DataGrid.php +++ b/src/Component/DataGrid.php @@ -13,6 +13,7 @@ use ADT\DoctrineComponents\QueryObject\QueryObject; use ADT\DoctrineComponents\QueryObject\QueryObjectByMode; use ADT\Forms\BootstrapFormRenderer; +use ADT\Datagrid\Column\ColumnText; use ADT\Datagrid\Filter\FilterSwitcher; use ADT\QueryObjectDataSource\QueryObjectDataSource; use ADT\Utils\Utils; @@ -24,17 +25,22 @@ use Contributte\Datagrid\Filter\FilterMultiSelect; use Contributte\Datagrid\Filter\FilterSelect; use Contributte\Datagrid\Utils\ArraysHelper; +use Contributte\Datagrid\Utils\PropertyAccessHelper; use DateTimeInterface; use Nette; use Nette\Application\Responses\FileResponse; use Nette\Application\UI\Form; +use Nette\Bridges\ApplicationLatte\Template; use Nette\Utils\DateTime; use Nette\Utils\Json; +use ReflectionClass; class DataGrid extends \Contributte\Datagrid\Datagrid { const string SELECTED_GRID_FILTER_KEY = 'advancedFilterId'; + private const string FILTER_BOOLEAN_NULL = 'null'; + public const string TEMPLATE_DEFAULT = 'DataGrid.latte'; public const array ACTION_NOT_DROPDOWN_ITEM = [ @@ -262,6 +268,110 @@ public function addColumnNumber(string $key, string $name, ?string $column = nul return parent::addColumnNumber($key, $name, $column)->setAlign('left'); } + public function addColumnText(string $key, string $name, ?string $column = null): ColumnText + { + $column ??= $key; + + $columnText = new ColumnText($this, $key, $column, $name); + $this->addColumn($key, $columnText); + + return $columnText; + } + + public function addColumnBoolean(string $key, string $name, ?string $column = null, string $filter = 'boolIcon'): ColumnText + { + $column ??= $key; + + $columnText = $this->addColumnText($key, $name, $column); + $columnText + ->setRenderer(function ($item) use ($column, $filter): string { + $template = $this->getTemplate(); + assert($template instanceof Template); + + return $template->getLatte()->invokeFilter($filter, [$this->resolveBoolValue($item, $column)]); + }) + ->setTemplateEscaping(false); + + return $columnText; + } + + private function resolveBoolValue(object $item, string $column): ?bool + { + $value = $item; + foreach (explode('.', $column) as $property) { + if (!is_object($value)) { + return null; + } + $value = PropertyAccessHelper::getValue($value, $property); + } + + return $value === null ? null : (bool) $value; + } + + public function addFilterBoolean(string $key, string $name, ?string $column = null, ?bool $nullable = null): FilterSelect|Filter + { + $column ??= $key; + + $options = $this->getBooleanOptions(); + if ($nullable ?? $this->isColumnNullable($column)) { + $options[self::FILTER_BOOLEAN_NULL] = $this->getTranslator()->translate('ublaboo_datagrid.not_set'); + } + + return $this->addFilterSelect($key, $name, $options, $column) + ->setPrompt($this->getTranslator()->translate('ublaboo_datagrid.all')) + ->setCondition(function (QueryObject $query, $value) use ($column) { + if ($value === '' || $value === null) { + return; + } + + if ($value === self::FILTER_BOOLEAN_NULL) { + $query->by($column, null); + return; + } + + $query->by($column, (bool) $value); + }); + } + + /** + * @return array + */ + private function getBooleanOptions(): array + { + return [ + 1 => $this->getTranslator()->translate('ublaboo_datagrid.yes'), + 0 => $this->getTranslator()->translate('ublaboo_datagrid.no'), + ]; + } + + private function isColumnNullable(string $column): bool + { + if (str_contains($column, '.')) { + return false; + } + + $dataSource = $this->getDataSource(); + if (!$dataSource instanceof QueryObjectDataSource) { + return false; + } + + $entityClass = $dataSource->getQueryObject()->getEntityClass(); + $metadata = $this->em->getClassMetadata($entityClass); + + if ($metadata->hasField($column)) { + return $metadata->isNullable($column); + } + + $getter = 'get' . ucfirst($column); + $reflection = new ReflectionClass($entityClass); + if ($reflection->hasMethod($getter)) { + $returnType = $reflection->getMethod($getter)->getReturnType(); + return $returnType !== null && $returnType->allowsNull(); + } + + return false; + } + public function addFilterSelectMonth(string $key, string $name): FilterSelect|Filter { return $this->addFilterSelect($key, $name, [ diff --git a/src/lang/ublaboo_datagrid.cs.yml b/src/lang/ublaboo_datagrid.cs.yml index 20bd24b..377a852 100644 --- a/src/lang/ublaboo_datagrid.cs.yml +++ b/src/lang/ublaboo_datagrid.cs.yml @@ -30,6 +30,7 @@ delete.confirm: "Opravdu smazat?" delete.label: Smazat yes: "Ano" no: "Ne" +not_set: "Nevyplněno" advanced_search.submit: Filtrovat advanced_search.required_filter: "Zadejte alespoň 1 filtr." advanced_search.save: Uložit diff --git a/src/lang/ublaboo_datagrid.en.yml b/src/lang/ublaboo_datagrid.en.yml index d38759f..cf2b175 100644 --- a/src/lang/ublaboo_datagrid.en.yml +++ b/src/lang/ublaboo_datagrid.en.yml @@ -30,6 +30,7 @@ delete.confirm: "Do you really want to delete this item?" delete.label: Delete yes: "Yes" no: "No" +not_set: "Not set" advanced_search.submit: Filter advanced_search.required_filter: "Please enter at least 1 filter." advanced_search.save: Save