diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index e9b19432..2bbb3ae6 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -32,4 +32,3 @@ jobs:
- name: Run PHPUnit
run: vendor/bin/phpunit
-
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 443d07a1..80f0117d 100755
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -6,6 +6,7 @@
;
return (new PhpCsFixer\Config())
+ ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
->registerCustomFixers(new PhpCsFixerCustomFixers\Fixers())
->setRules([
'@Symfony' => true,
diff --git a/docs/src/docs/composer-require-version.data.js b/docs/src/docs/composer-require-version.data.js
deleted file mode 100644
index 20dde068..00000000
--- a/docs/src/docs/composer-require-version.data.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import { major, minor } from 'semver';
-
-export default {
- async load() {
- return await fetch('https://packagist.org/p2/kreyu/data-table-bundle.json')
- .then(response => response.json())
- .then(body => body.packages['kreyu/data-table-bundle'].shift().version)
- .then(version => major(version).toString() + '.' + minor(version).toString() + '.*')
- ;
- }
-}
diff --git a/docs/src/docs/installation.md b/docs/src/docs/installation.md
index 79fbbec2..7434724a 100644
--- a/docs/src/docs/installation.md
+++ b/docs/src/docs/installation.md
@@ -1,7 +1,3 @@
-
-
# Installation
This bundle can be installed at any moment during a project’s lifecycle.
@@ -18,12 +14,10 @@ This bundle can be installed at any moment during a project’s lifecycle.
Use [Composer](https://getcomposer.org/) to install the bundle:
```shell-vue
-composer require kreyu/data-table-bundle:"{{ version }}"
+composer require kreyu/data-table-bundle
```
-::: danger This bundle is not production ready!
-It is recommended to lock the minor version, as minor versions can provide breaking changes until the stable release!
-:::
+> [!DANGER] This bundle is not stable yet. Use with caution.
## Enable the bundle
diff --git a/src/Column/Column.php b/src/Column/Column.php
index 6964ce7b..5b0b214c 100755
--- a/src/Column/Column.php
+++ b/src/Column/Column.php
@@ -16,8 +16,6 @@ class Column implements ColumnInterface
private ?DataTableInterface $dataTable = null;
private ?PropertyPathInterface $propertyPath = null;
private ?PropertyPathInterface $sortPropertyPath = null;
- private int $priority = 0;
- private bool $visible = true;
public function __construct(
private readonly ColumnConfigInterface $config,
@@ -107,28 +105,4 @@ public function createExportValueView(?ValueRowView $parent = null): ColumnValue
return $view;
}
-
- public function getPriority(): int
- {
- return $this->priority;
- }
-
- public function setPriority(int $priority): static
- {
- $this->priority = $priority;
-
- return $this;
- }
-
- public function isVisible(): bool
- {
- return $this->visible;
- }
-
- public function setVisible(bool $visible): static
- {
- $this->visible = $visible;
-
- return $this;
- }
}
diff --git a/src/Column/ColumnBuilder.php b/src/Column/ColumnBuilder.php
index 54d1eb76..ada9634b 100755
--- a/src/Column/ColumnBuilder.php
+++ b/src/Column/ColumnBuilder.php
@@ -8,59 +8,13 @@
class ColumnBuilder extends ColumnConfigBuilder implements ColumnBuilderInterface
{
- private int $priority = 0;
- private bool $visible = true;
-
- public function getPriority(): int
- {
- if ($this->locked) {
- throw $this->createBuilderLockedException();
- }
-
- return $this->priority;
- }
-
- public function setPriority(int $priority): static
- {
- if ($this->locked) {
- throw $this->createBuilderLockedException();
- }
-
- $this->priority = $priority;
-
- return $this;
- }
-
- public function isVisible(): bool
- {
- if ($this->locked) {
- throw $this->createBuilderLockedException();
- }
-
- return $this->visible;
- }
-
- public function setVisible(bool $visible): static
- {
- if ($this->locked) {
- throw $this->createBuilderLockedException();
- }
-
- $this->visible = $visible;
-
- return $this;
- }
-
public function getColumn(): ColumnInterface
{
if ($this->locked) {
throw $this->createBuilderLockedException();
}
- return (new Column($this->getColumnConfig()))
- ->setPriority($this->getPriority())
- ->setVisible($this->isVisible())
- ;
+ return new Column($this->getColumnConfig());
}
private function createBuilderLockedException(): BadMethodCallException
diff --git a/src/Column/ColumnBuilderInterface.php b/src/Column/ColumnBuilderInterface.php
index cf067a78..12e3f6ab 100755
--- a/src/Column/ColumnBuilderInterface.php
+++ b/src/Column/ColumnBuilderInterface.php
@@ -6,13 +6,5 @@
interface ColumnBuilderInterface extends ColumnConfigBuilderInterface
{
- public function getPriority(): int;
-
- public function setPriority(int $priority): static;
-
- public function isVisible(): bool;
-
- public function setVisible(bool $visible): static;
-
public function getColumn(): ColumnInterface;
}
diff --git a/src/Column/ColumnConfigBuilder.php b/src/Column/ColumnConfigBuilder.php
index cabdf1d8..06050d6c 100755
--- a/src/Column/ColumnConfigBuilder.php
+++ b/src/Column/ColumnConfigBuilder.php
@@ -19,6 +19,8 @@ class ColumnConfigBuilder implements ColumnConfigBuilderInterface
private bool $sortable = false;
private bool $exportable = false;
private bool $personalizable = true;
+ private int $priority = 0;
+ private bool $visible = true;
private ColumnFactoryInterface $columnFactory;
public function __construct(
@@ -222,6 +224,38 @@ public function setPersonalizable(bool $personalizable): static
return $this;
}
+ public function getPriority(): int
+ {
+ return $this->priority;
+ }
+
+ public function setPriority(int $priority): static
+ {
+ if ($this->locked) {
+ throw $this->createBuilderLockedException();
+ }
+
+ $this->priority = $priority;
+
+ return $this;
+ }
+
+ public function isVisible(): bool
+ {
+ return $this->visible;
+ }
+
+ public function setVisible(bool $visible): static
+ {
+ if ($this->locked) {
+ throw $this->createBuilderLockedException();
+ }
+
+ $this->visible = $visible;
+
+ return $this;
+ }
+
public function getColumnFactory(): ColumnFactoryInterface
{
if (!isset($this->columnFactory)) {
diff --git a/src/Column/ColumnConfigBuilderInterface.php b/src/Column/ColumnConfigBuilderInterface.php
index 76ca34fc..911f7973 100755
--- a/src/Column/ColumnConfigBuilderInterface.php
+++ b/src/Column/ColumnConfigBuilderInterface.php
@@ -9,23 +9,8 @@
interface ColumnConfigBuilderInterface extends ColumnConfigInterface
{
- /**
- * @deprecated since 0.14.0, provide the name using the factory {@see ColumnFactoryInterface} "named" methods instead
- */
- public function setName(string $name): static;
-
public function setType(ResolvedColumnTypeInterface $type): static;
- /**
- * @deprecated since 0.14.0, modifying the options dynamically will be removed as it creates unexpected behaviors
- */
- public function setOptions(array $options): static;
-
- /**
- * @deprecated since 0.14.0, modifying the options dynamically will be removed as it creates unexpected behaviors
- */
- public function setOption(string $name, mixed $value): static;
-
public function setAttributes(array $attributes): static;
public function setAttribute(string $name, mixed $value): static;
@@ -40,6 +25,10 @@ public function setExportable(bool $exportable): static;
public function setPersonalizable(bool $personalizable): static;
+ public function setPriority(int $priority): static;
+
+ public function setVisible(bool $visible): static;
+
public function setColumnFactory(ColumnFactoryInterface $columnFactory): static;
public function getColumnConfig(): ColumnConfigInterface;
diff --git a/src/Column/ColumnConfigInterface.php b/src/Column/ColumnConfigInterface.php
index 5644df57..cd7ae367 100755
--- a/src/Column/ColumnConfigInterface.php
+++ b/src/Column/ColumnConfigInterface.php
@@ -35,5 +35,9 @@ public function isExportable(): bool;
public function isPersonalizable(): bool;
+ public function getPriority(): int;
+
+ public function isVisible(): bool;
+
public function getColumnFactory(): ColumnFactoryInterface;
}
diff --git a/src/Column/ColumnInterface.php b/src/Column/ColumnInterface.php
index cf62cd61..2e8bb492 100755
--- a/src/Column/ColumnInterface.php
+++ b/src/Column/ColumnInterface.php
@@ -30,12 +30,4 @@ public function createValueView(?ValueRowView $parent = null): ColumnValueView;
public function createExportHeaderView(?HeaderRowView $parent = null): ColumnHeaderView;
public function createExportValueView(?ValueRowView $parent = null): ColumnValueView;
-
- public function getPriority(): int;
-
- public function setPriority(int $priority): static;
-
- public function isVisible(): bool;
-
- public function setVisible(bool $visible): static;
}
diff --git a/src/Column/ColumnSortUrlGenerator.php b/src/Column/ColumnSortUrlGenerator.php
index 250b9e3a..52455d81 100644
--- a/src/Column/ColumnSortUrlGenerator.php
+++ b/src/Column/ColumnSortUrlGenerator.php
@@ -40,7 +40,7 @@ public function generate(DataTableView $dataTableView, ColumnHeaderView ...$colu
}
// Clearing the filters should reset the pagination to the first page.
- if ($dataTableView->vars['pagination_enabled']) {
+ if ($dataTableView->vars['pagination_enabled'] ?? false) {
$parameters[$dataTableView->vars['page_parameter_name']] = 1;
}
diff --git a/src/Column/ColumnValueView.php b/src/Column/ColumnValueView.php
index 5d1e6612..0eda55f9 100755
--- a/src/Column/ColumnValueView.php
+++ b/src/Column/ColumnValueView.php
@@ -25,4 +25,9 @@ public function getDataTable(): DataTableView
{
return $this->parent->parent;
}
+
+ public function getRowData(): mixed
+ {
+ return $this->parent->data;
+ }
}
diff --git a/src/Column/Type/AbstractDateTimeColumnType.php b/src/Column/Type/AbstractDateTimeColumnType.php
new file mode 100644
index 00000000..a60cdfe4
--- /dev/null
+++ b/src/Column/Type/AbstractDateTimeColumnType.php
@@ -0,0 +1,72 @@
+vars = array_replace($view->vars, [
+ 'format' => $options['format'],
+ 'timezone' => $options['timezone'],
+ ]);
+ }
+
+ public function configureOptions(OptionsResolver $resolver): void
+ {
+ $resolver->define('format')
+ ->allowedTypes('string')
+ ->info('A date time string format, supported by the PHP date() function - null to use default.')
+ ;
+
+ $resolver->define('timezone')
+ ->default(null)
+ ->allowedTypes('null', 'bool', 'string', \DateTimeZone::class)
+ ->info('Target timezone - null to use the default, false to leave unchanged.')
+ ;
+
+ // When exporting, we ensure the export "formatter" option is present, so the value gets pre-formatted.
+ $resolver->addNormalizer('export', function (Options $options, mixed $export) {
+ if (false === $export) {
+ return false;
+ }
+
+ if (true === $export) {
+ $export = [];
+ }
+
+ $export['formatter'] ??= $options['formatter'] ?? function (mixed $value) use ($options) {
+ if (!$value instanceof \DateTimeInterface) {
+ return '';
+ }
+
+ $timezone = $options['timezone'];
+
+ if (null === $timezone) {
+ $timezone = date_default_timezone_get();
+ }
+
+ if (is_string($timezone)) {
+ $timezone = new \DateTimeZone($timezone);
+ }
+
+ $dateTime = \DateTime::createFromInterface($value);
+
+ if ($timezone instanceof \DateTimeZone) {
+ $dateTime->setTimezone($timezone);
+ }
+
+ return $dateTime->format($options['format']);
+ };
+
+ return $export;
+ });
+ }
+}
diff --git a/src/Column/Type/ActionsColumnType.php b/src/Column/Type/ActionsColumnType.php
index d16d2849..e32dbe6b 100755
--- a/src/Column/Type/ActionsColumnType.php
+++ b/src/Column/Type/ActionsColumnType.php
@@ -10,9 +10,19 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnHeaderView;
use Kreyu\Bundle\DataTableBundle\Column\ColumnInterface;
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
+use Kreyu\Bundle\DataTableBundle\DataTableBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * Represents a column that contains row actions.
+ *
+ * In most cases, it is not necessary to use this column type directly.
+ * Instead, use the {@see DataTableBuilderInterface::addRowAction()} method.
+ * If at least one row action is defined and visible, column of this type is added.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/actions
+ */
final class ActionsColumnType extends AbstractColumnType
{
public function __construct(
@@ -26,9 +36,14 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
foreach ($options['actions'] as $name => $action) {
$action = $this->resolveAction($name, $action, $view);
- $action?->setDataTable($column->getDataTable());
- $actions[$name] = $action?->createView($view);
+ if (null === $action) {
+ continue;
+ }
+
+ $action->setDataTable($column->getDataTable());
+
+ $actions[$name] = $action->createView($view);
}
$view->vars['actions'] = array_filter($actions);
@@ -36,14 +51,11 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefaults([
- 'label' => 'Actions',
- 'export' => false,
- 'property_path' => false,
- 'actions' => [],
- ])
- ->setNormalizer('actions', function (Options $options, mixed $value) {
+ $resolver->define('actions')
+ ->default([])
+ ->allowedTypes('actions', 'array[]', ActionBuilderInterface::class.'[]', ActionInterface::class.'[]')
+ ->info('An array of actions to render in the column.')
+ ->normalize(function (Options $options, mixed $value) {
($resolver = new OptionsResolver())
->setRequired([
'type',
@@ -68,9 +80,12 @@ public function configureOptions(OptionsResolver $resolver): void
return $value;
})
- ->setAllowedTypes('actions', ['array[]', ActionBuilderInterface::class.'[]', ActionInterface::class.'[]'])
- ->setInfo('actions', 'An array of actions configuration, which contains of their type and options.')
;
+
+ $resolver->setDefaults([
+ 'export' => false,
+ 'property_path' => false,
+ ]);
}
private function resolveAction(
diff --git a/src/Column/Type/BooleanColumnType.php b/src/Column/Type/BooleanColumnType.php
index 57b53b7a..f9f10ab3 100755
--- a/src/Column/Type/BooleanColumnType.php
+++ b/src/Column/Type/BooleanColumnType.php
@@ -9,6 +9,11 @@
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatableInterface;
+/**
+ * Represents a column with value displayed as "yes" or "no".
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/boolean
+ */
final class BooleanColumnType extends AbstractColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
@@ -21,16 +26,18 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefaults([
- 'label_true' => 'Yes',
- 'label_false' => 'No',
- 'value_translation_domain' => 'KreyuDataTable',
- ])
- ->setAllowedTypes('label_true', ['string', TranslatableInterface::class])
- ->setAllowedTypes('label_false', ['string', TranslatableInterface::class])
- ->setInfo('label_true', 'Label displayed when the value equals true.')
- ->setInfo('label_false', 'Label displayed when the value equals false.')
+ $resolver->define('label_true')
+ ->default('Yes')
+ ->allowedTypes('string', TranslatableInterface::class)
+ ->info('Label displayed when the value is truthy.')
;
+
+ $resolver->define('label_false')
+ ->default('No')
+ ->allowedTypes('string', TranslatableInterface::class)
+ ->info('Label displayed when the value is falsy.')
+ ;
+
+ $resolver->setDefault('value_translation_domain', 'KreyuDataTable');
}
}
diff --git a/src/Column/Type/CheckboxColumnType.php b/src/Column/Type/CheckboxColumnType.php
index 7ee4af40..75ef6256 100755
--- a/src/Column/Type/CheckboxColumnType.php
+++ b/src/Column/Type/CheckboxColumnType.php
@@ -7,8 +7,18 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnHeaderView;
use Kreyu\Bundle\DataTableBundle\Column\ColumnInterface;
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
+use Kreyu\Bundle\DataTableBundle\DataTableBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * Represents a column with checkboxes, one in its header, and one as its value.
+ *
+ * In most cases, it is not necessary to use this column type directly.
+ * Instead, use the {@see DataTableBuilderInterface::addBatchAction()} method.
+ * If at least one batch action is defined and visible, column of this type is added.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/checkbox
+ */
final class CheckboxColumnType extends AbstractColumnType
{
public function buildHeaderView(ColumnHeaderView $view, ColumnInterface $column, array $options): void
@@ -23,10 +33,15 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
public function configureOptions(OptionsResolver $resolver): void
{
+ $resolver->define('identifier_name')
+ ->default('id')
+ ->allowedTypes('string')
+ ->info('The name of the identifier property.')
+ ;
+
$resolver->setDefaults([
'label' => '□',
'property_path' => 'id',
- 'identifier_name' => 'id',
]);
}
}
diff --git a/src/Column/Type/CollectionColumnType.php b/src/Column/Type/CollectionColumnType.php
index 92833cb9..9145c8fe 100755
--- a/src/Column/Type/CollectionColumnType.php
+++ b/src/Column/Type/CollectionColumnType.php
@@ -10,6 +10,11 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * Represents a column with value displayed as a list.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/collection
+ */
final class CollectionColumnType extends AbstractColumnType
{
public function buildColumn(ColumnBuilderInterface $builder, array $options): void
@@ -45,15 +50,25 @@ public function buildExportValueView(ColumnValueView $view, ColumnInterface $col
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefaults([
- 'entry_type' => TextColumnType::class,
- 'entry_options' => [],
- 'separator' => ', ',
- ])
- ->setAllowedTypes('entry_type', 'string')
- ->setAllowedTypes('entry_options', 'array')
- ->setAllowedTypes('separator', ['null', 'string'])
+ /* @see https://data-table-bundle.swroblewski.pl/reference/types/column/collection#entry_type */
+ $resolver->define('entry_type')
+ ->default(TextColumnType::class)
+ ->info('Column type to render for each item in the collection.')
+ ->allowedTypes('string')
+ ;
+
+ /* @see https://data-table-bundle.swroblewski.pl/reference/types/column/collection#entry_options */
+ $resolver->define('entry_options')
+ ->default([])
+ ->info('Options to pass to the column type for each item in the collection.')
+ ->allowedTypes('array')
+ ;
+
+ /* @see https://data-table-bundle.swroblewski.pl/reference/types/column/collection#separator */
+ $resolver->define('separator')
+ ->default(', ')
+ ->info('Separator to render between each item in the collection.')
+ ->allowedTypes('null', 'string')
;
}
diff --git a/src/Column/Type/ColumnType.php b/src/Column/Type/ColumnType.php
index 2280a611..88a4f9c8 100755
--- a/src/Column/Type/ColumnType.php
+++ b/src/Column/Type/ColumnType.php
@@ -16,6 +16,11 @@
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
+/**
+ * Represents a base column with basic functionality, used as a parent for other column types.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/collection
+ */
final class ColumnType implements ColumnTypeInterface
{
public function __construct(
@@ -73,33 +78,34 @@ public function buildHeaderView(ColumnHeaderView $view, ColumnInterface $column,
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
{
- $rowData = $view->parent->data;
+ $valueRowView = $view->parent;
+ $dataTableView = $valueRowView->parent;
+
+ $rowData = $view->getRowData();
- $normData = $this->getNormDataFromRowData($rowData, $column, $options);
- $viewData = $this->getViewDataFromNormData($normData, $rowData, $column, $options);
+ $data = $this->getColumnDataFromRowData($rowData, $column, $options);
+ $value = $this->getColumnValueFromColumnData($data, $rowData, $column, $options);
- $view->data = $normData;
- $view->value = $viewData;
+ $view->data = $data;
+ $view->value = $value;
if (is_callable($attr = $options['value_attr'])) {
- $attr = $attr($normData, $rowData);
+ $attr = $attr($data, $rowData);
}
- $translationParameters = $options['value_translation_parameters'];
-
- if (is_callable($translationParameters)) {
- $translationParameters = $translationParameters($normData, $rowData);
+ if (is_callable($translationParameters = $options['value_translation_parameters'])) {
+ $translationParameters = $translationParameters($data, $rowData);
}
$view->vars = array_replace($view->vars, [
'name' => $column->getName(),
'column' => $view,
- 'row' => $view->parent,
- 'data_table' => $view->parent->parent,
+ 'row' => $valueRowView,
+ 'data_table' => $dataTableView,
'block_prefixes' => $this->getColumnBlockPrefixes($column, $options),
- 'data' => $view->data,
- 'value' => $view->value,
- 'translation_domain' => $options['value_translation_domain'] ?? $view->parent->parent->vars['translation_domain'] ?? null,
+ 'data' => $data,
+ 'value' => $value,
+ 'translation_domain' => $options['value_translation_domain'] ?? $dataTableView->vars['translation_domain'] ?? null,
'translation_parameters' => $translationParameters ?? [],
'attr' => $attr,
]);
@@ -162,29 +168,29 @@ public function buildExportValueView(ColumnValueView $view, ColumnInterface $col
$rowData = $view->parent->data;
- $normData = $this->getNormDataFromRowData($rowData, $column, $options['export']);
- $viewData = $this->getViewDataFromNormData($normData, $rowData, $column, $options['export']);
+ $data = $this->getColumnDataFromRowData($rowData, $column, $options['export']);
+ $value = $this->getColumnValueFromColumnData($data, $rowData, $column, $options['export']);
- if ($this->translator && (is_string($viewData) || $viewData instanceof TranslatableInterface)) {
- if ($viewData instanceof TranslatableInterface) {
+ if ($this->translator && (is_string($value) || $value instanceof TranslatableInterface)) {
+ if ($value instanceof TranslatableInterface) {
$locale = null;
if (method_exists(TranslatableInterface::class, 'getLocale')) {
$locale = $this->translator->getLocale();
}
- $viewData = $viewData->trans($this->translator, $locale);
+ $value = $value->trans($this->translator, $locale);
} else {
$translationDomain = $options['export']['value_translation_domain'];
$translationParameters = $options['export']['value_translation_parameters'];
if (is_callable($translationParameters)) {
- $translationParameters = $translationParameters($normData, $rowData);
+ $translationParameters = $translationParameters($data, $rowData);
}
if ($translationDomain) {
- $viewData = $this->translator->trans(
- id: $viewData,
+ $value = $this->translator->trans(
+ id: $value,
parameters: $translationParameters,
domain: $translationDomain,
);
@@ -192,54 +198,112 @@ public function buildExportValueView(ColumnValueView $view, ColumnInterface $col
}
}
- $view->data = $normData;
- $view->value = $viewData;
-
- $view->vars['data'] = $normData;
- $view->vars['value'] = $viewData;
+ $view->vars['data'] = $view->data = $data;
+ $view->vars['value'] = $view->value = $value;
}
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefaults([
- 'label' => null,
- 'header_translation_domain' => null,
- 'header_translation_parameters' => [],
- 'value_translation_domain' => false,
- 'value_translation_parameters' => [],
- 'block_name' => null,
- 'block_prefix' => null,
- 'sort' => false,
- 'export' => false,
- 'formatter' => null,
- 'property_path' => null,
- 'property_accessor' => PropertyAccess::createPropertyAccessor(),
- 'getter' => null,
- 'header_attr' => [],
- 'value_attr' => [],
- 'priority' => 0,
- 'visible' => true,
- 'personalizable' => true,
- ])
- ->setAllowedTypes('label', ['null', 'string', TranslatableInterface::class])
- ->setAllowedTypes('header_translation_domain', ['null', 'bool', 'string'])
- ->setAllowedTypes('header_translation_parameters', ['null', 'array'])
- ->setAllowedTypes('value_translation_domain', ['null', 'bool', 'string'])
- ->setAllowedTypes('value_translation_parameters', ['array', 'callable'])
- ->setAllowedTypes('block_name', ['null', 'string'])
- ->setAllowedTypes('block_prefix', ['null', 'string'])
- ->setAllowedTypes('sort', ['bool', 'string'])
- ->setAllowedTypes('export', ['bool', 'array'])
- ->setAllowedTypes('formatter', ['null', 'callable'])
- ->setAllowedTypes('property_path', ['null', 'bool', 'string', PropertyPathInterface::class])
- ->setAllowedTypes('property_accessor', PropertyAccessorInterface::class)
- ->setAllowedTypes('getter', ['null', 'callable'])
- ->setAllowedTypes('header_attr', 'array')
- ->setAllowedTypes('value_attr', ['array', 'callable'])
- ->setAllowedTypes('priority', 'int')
- ->setAllowedTypes('visible', 'bool')
- ->setAllowedTypes('personalizable', 'bool')
+ $resolver->define('label')
+ ->default(null)
+ ->allowedTypes('null', 'string', TranslatableInterface::class)
+ ->info('Label displayed in column header - null to default to sentence cased column name.')
+ ;
+
+ $resolver->define('header_translation_domain')
+ ->default(null)
+ ->allowedTypes('null', 'bool', 'string')
+ ->info('Translation domain used to translate the column header - set to false to disable translation.')
+ ;
+
+ $resolver->define('header_translation_parameters')
+ ->default([])
+ ->allowedTypes('null', 'array')
+ ->info('Translation parameters used to translate the column header.')
+ ;
+
+ $resolver->define('value_translation_domain')
+ ->default(false)
+ ->allowedTypes('null', 'bool', 'string')
+ ->info('Translation parameters used to translate the column value - set to false to disable translation.')
+ ;
+
+ $resolver->define('value_translation_parameters')
+ ->default([])
+ ->allowedTypes('array', 'callable')
+ ->info('Translation parameters used to translate the column value.')
+ ;
+
+ $resolver->define('block_prefix')
+ ->default(null)
+ ->allowedTypes('null', 'string')
+ ->info('Defines custom block prefix to use when rendering the column.')
+ ;
+
+ $resolver->define('sort')
+ ->default(false)
+ ->allowedTypes('bool', 'string')
+ ->info('Defines whether the column is sortable. Passing a string sets the sorting path.')
+ ;
+
+ $resolver->define('export')
+ ->default(false)
+ ->allowedTypes('bool', 'array')
+ ->info('Defines whether the column is exportable. You can pass an array of options to differentiate them during an export.')
+ ;
+
+ $resolver->define('formatter')
+ ->default(null)
+ ->allowedTypes('null', 'callable')
+ ->info('Formatter to use on non-empty value to customize it even further before rendering. Column value and row data are passed as arguments.')
+ ;
+
+ $resolver->define('property_path')
+ ->default(null)
+ ->allowedTypes('null', 'bool', 'string', PropertyPathInterface::class)
+ ->info('Path to use by property accessor component to retrieve the column value from row data. Defaults to column name.')
+ ;
+
+ $resolver->define('property_accessor')
+ ->default(PropertyAccess::createPropertyAccessor())
+ ->allowedTypes(PropertyAccessorInterface::class)
+ ->info('An instance of property accessor to use to retrieve the value.')
+ ;
+
+ $resolver->define('getter')
+ ->default(null)
+ ->allowedTypes('null', 'callable')
+ ->info('Callable used to retrieve column value from row data. If set, it is used instead of property accessor.')
+ ;
+
+ $resolver->define('header_attr')
+ ->default([])
+ ->allowedTypes('array')
+ ->info('Extra HTML attributes to render on the column header.')
+ ;
+
+ $resolver->define('value_attr')
+ ->default([])
+ ->allowedTypes('array', 'callable')
+ ->info('Extra HTML attributes to render on the column value.')
+ ;
+
+ $resolver->define('priority')
+ ->default(0)
+ ->allowedTypes('int')
+ ->info('Defines the priority of the column - the higher the priority, the earlier the column will be rendered.')
+ ;
+
+ $resolver->define('visible')
+ ->default(true)
+ ->allowedTypes('bool')
+ ->info('Defines the visibility of the column.')
+ ;
+
+ $resolver->define('personalizable')
+ ->default(true)
+ ->allowedTypes('bool')
+ ->info('Defines whether the column can be personalized by the user in personalization feature.')
;
}
@@ -253,14 +317,7 @@ public function getParent(): ?string
return null;
}
- /**
- * Retrieves the column norm data from the row data by either:.
- *
- * - using the "getter" option;
- * - using the property accessor with the "property_path" option;
- * - falling back to the unmodified column data;
- */
- private function getNormDataFromRowData(mixed $rowData, ColumnInterface $column, array $options): mixed
+ private function getColumnDataFromRowData(mixed $rowData, ColumnInterface $column, array $options): mixed
{
if (null === $rowData) {
return null;
@@ -279,29 +336,26 @@ private function getNormDataFromRowData(mixed $rowData, ColumnInterface $column,
return $rowData;
}
- /**
- * Retrieves the column view data from the norm data by applying the formatter if given.
- */
- private function getViewDataFromNormData(mixed $normData, mixed $rowData, ColumnInterface $column, array $options): mixed
+ private function getColumnValueFromColumnData(mixed $data, mixed $rowData, ColumnInterface $column, array $options): mixed
{
- if (null === $normData) {
+ if (null === $data) {
return null;
}
- $viewData = $normData;
+ $value = $data;
if (is_callable($formatter = $options['formatter'])) {
- $viewData = $formatter($normData, $rowData, $column, $options);
+ $value = $formatter($data, $rowData, $column, $options);
}
- return $viewData;
+ return $value;
}
/**
* Retrieves the column block prefixes, respecting the type hierarchy.
*
- * For example, take a look at the NumberColumnType. It is based on the TextColumnType,
- * which is based on the ColumnType, therefore its block prefixes are: ["number", "text", "column"].
+ * For example, take a look at the NumberColumnType. It is based on the ColumnType,
+ * therefore its block prefixes are: ["number", "column"].
*
* @return array
*/
diff --git a/src/Column/Type/DateColumnType.php b/src/Column/Type/DateColumnType.php
index 4bfd6399..ebc621a7 100755
--- a/src/Column/Type/DateColumnType.php
+++ b/src/Column/Type/DateColumnType.php
@@ -6,15 +6,17 @@
use Symfony\Component\OptionsResolver\OptionsResolver;
-final class DateColumnType extends AbstractColumnType
+/**
+ * Represents a column with value displayed as a date without time.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/date
+ */
+final class DateColumnType extends AbstractDateTimeColumnType
{
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver->setDefault('format', 'd.m.Y');
- }
+ parent::configureOptions($resolver);
- public function getParent(): ?string
- {
- return DateTimeColumnType::class;
+ $resolver->setDefault('format', 'd.m.Y');
}
}
diff --git a/src/Column/Type/DatePeriodColumnType.php b/src/Column/Type/DatePeriodColumnType.php
index 8b925259..c3f8ceec 100755
--- a/src/Column/Type/DatePeriodColumnType.php
+++ b/src/Column/Type/DatePeriodColumnType.php
@@ -8,10 +8,17 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
use Symfony\Component\OptionsResolver\OptionsResolver;
-final class DatePeriodColumnType extends AbstractColumnType
+/**
+ * Represents a column with value displayed as date range from date period object.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/date-period
+ */
+final class DatePeriodColumnType extends AbstractDateTimeColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
{
+ parent::buildValueView($view, $column, $options);
+
$view->vars = array_replace($view->vars, [
'separator' => $options['separator'],
]);
@@ -19,15 +26,14 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefault('separator', ' - ')
- ->setAllowedTypes('separator', ['null', 'string'])
- ->setInfo('separator', 'A string used to visually separate start and end dates.')
- ;
- }
+ parent::configureOptions($resolver);
- public function getParent(): ?string
- {
- return DateTimeColumnType::class;
+ $resolver->setDefault('format', 'd.m.Y');
+
+ $resolver->define('separator')
+ ->default(' - ')
+ ->allowedTypes('null', 'string')
+ ->info('A string used to visually separate start and end dates.')
+ ;
}
}
diff --git a/src/Column/Type/DateTimeColumnType.php b/src/Column/Type/DateTimeColumnType.php
index a08ccb6d..847f325b 100755
--- a/src/Column/Type/DateTimeColumnType.php
+++ b/src/Column/Type/DateTimeColumnType.php
@@ -4,56 +4,19 @@
namespace Kreyu\Bundle\DataTableBundle\Column\Type;
-use Kreyu\Bundle\DataTableBundle\Column\ColumnInterface;
-use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
-use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
-final class DateTimeColumnType extends AbstractColumnType
+/**
+ * Represents a column with value displayed as date and time.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/date-time
+ */
+final class DateTimeColumnType extends AbstractDateTimeColumnType
{
- public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
- {
- $view->vars = array_replace($view->vars, [
- 'format' => $options['format'],
- 'timezone' => $options['timezone'],
- ]);
- }
-
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefaults([
- 'format' => 'd.m.Y H:i:s',
- 'timezone' => null,
- ])
- ->setNormalizer('export', function (Options $options, $value) {
- if (true === $value) {
- $value = [];
- }
-
- if (is_array($value)) {
- $value += [
- 'formatter' => static function (mixed $value, mixed $data, ColumnInterface $column): string {
- if ($value instanceof \DateTimeInterface) {
- return $value->format($column->getConfig()->getOption('format'));
- }
+ parent::configureOptions($resolver);
- return '';
- },
- ];
- }
-
- return $value;
- })
- ->setAllowedTypes('format', ['string'])
- ->setAllowedTypes('timezone', ['null', 'string'])
- ->setInfo('format', 'A date time string format, supported by the PHP date() function.')
- ->setInfo('timezone', 'A timezone used to render the date time as string.')
- ;
- }
-
- public function getParent(): ?string
- {
- return TextColumnType::class;
+ $resolver->setDefault('format', 'd.m.Y H:i:s');
}
}
diff --git a/src/Column/Type/EnumColumnType.php b/src/Column/Type/EnumColumnType.php
index 1507b61f..4083fad8 100644
--- a/src/Column/Type/EnumColumnType.php
+++ b/src/Column/Type/EnumColumnType.php
@@ -8,6 +8,14 @@
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
+/**
+ * Represents a column with PHP enumeration as value.
+ *
+ * If Symfony Translator component is installed and the enumeration
+ * implements the {@see TranslatableInterface}, it will be translated.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/enum
+ */
final class EnumColumnType extends AbstractColumnType
{
public function __construct(
@@ -28,9 +36,4 @@ protected function format(\UnitEnum $enum): string
return $enum->name;
}
-
- public function getParent(): ?string
- {
- return TextColumnType::class;
- }
}
diff --git a/src/Column/Type/FormColumnType.php b/src/Column/Type/FormColumnType.php
deleted file mode 100755
index 1ca401ed..00000000
--- a/src/Column/Type/FormColumnType.php
+++ /dev/null
@@ -1,36 +0,0 @@
-vars = array_replace($view->vars, [
- 'form' => $options['form'],
- 'form_child_path' => $options['form_child_path'] ?? $column->getName(),
- ]);
- }
-
- public function configureOptions(OptionsResolver $resolver): void
- {
- $resolver
- ->setRequired('form')
- ->setDefault('form_child_path', null)
- ->setAllowedTypes('form', FormInterface::class)
- ->setAllowedTypes('form_child_path', ['null', 'bool', 'string'])
- ->setInfo('form', 'An instance of the form which wraps the data table.')
- ->setInfo('form_child_path', 'A path to the child form of each collection field.')
- ;
- }
-}
diff --git a/src/Column/Type/HtmlColumnType.php b/src/Column/Type/HtmlColumnType.php
index 31407b5d..8498f438 100644
--- a/src/Column/Type/HtmlColumnType.php
+++ b/src/Column/Type/HtmlColumnType.php
@@ -8,6 +8,11 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * Represents a column with value displayed as HTML.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/html
+ */
final class HtmlColumnType extends AbstractColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
diff --git a/src/Column/Type/IconColumnType.php b/src/Column/Type/IconColumnType.php
index b1205796..6ccea4b9 100644
--- a/src/Column/Type/IconColumnType.php
+++ b/src/Column/Type/IconColumnType.php
@@ -8,6 +8,9 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/icon
+ */
final class IconColumnType extends AbstractColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
diff --git a/src/Column/Type/LinkColumnType.php b/src/Column/Type/LinkColumnType.php
index 2ed60c3b..3b9a305f 100755
--- a/src/Column/Type/LinkColumnType.php
+++ b/src/Column/Type/LinkColumnType.php
@@ -8,6 +8,14 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * Represents a column with value displayed as a link.
+ *
+ * When using Turbo v8+, by default, the link will be prefetched on hover.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/link
+ * @see https://turbo.hotwired.dev/handbook/drive#prefetching-links-on-hover
+ */
final class LinkColumnType extends AbstractColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
@@ -28,18 +36,16 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefaults([
- 'href' => '#',
- 'target' => null,
- ])
- ->setAllowedTypes('href', ['string', 'callable'])
- ->setAllowedTypes('target', ['null', 'string', 'callable'])
+ $resolver->define('href')
+ ->default('#')
+ ->allowedTypes('string', 'callable')
+ ->info('Defines the URL to link to.')
;
- }
- public function getParent(): ?string
- {
- return TextColumnType::class;
+ $resolver->define('target')
+ ->default(null)
+ ->allowedTypes('null', 'string', 'callable')
+ ->info('Sets the value that will be used as a "target" HTML attribute.')
+ ;
}
}
diff --git a/src/Column/Type/MoneyColumnType.php b/src/Column/Type/MoneyColumnType.php
index 0dca8114..df32551c 100755
--- a/src/Column/Type/MoneyColumnType.php
+++ b/src/Column/Type/MoneyColumnType.php
@@ -7,7 +7,13 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnInterface;
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Translation\Formatter\IntlFormatter;
+/**
+ * Represents a column with monetary value, appropriately formatted and rendered with currency.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/money
+ */
final class MoneyColumnType extends AbstractColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
@@ -16,29 +22,48 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
$currency = $currency($view->parent->data);
}
- if (1 !== $options['divisor']) {
- $view->vars['value'] /= $options['divisor'];
+ if (null !== $divisor = $options['divisor']) {
+ $view->vars['value'] /= $divisor;
}
$view->vars = array_merge($view->vars, [
'currency' => $currency,
+ 'divisor' => $divisor,
+ 'use_intl_formatter' => $options['use_intl_formatter'],
+ 'intl_formatter_options' => $options['intl_formatter_options'],
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setRequired(['currency', 'divisor'])
- ->setAllowedTypes('currency', ['string', 'callable'])
- ->setAllowedTypes('divisor', 'int')
+ $resolver->define('currency')
+ ->required()
+ ->allowedTypes('string', 'callable')
;
- $resolver->setDefaults([
- 'divisor' => 1,
- ]);
- }
- public function getParent(): ?string
- {
- return NumberColumnType::class;
+ $resolver->define('divisor')
+ ->default(null)
+ ->allowedTypes('null', 'int')
+ ->allowedValues(fn (?int $value) => 0 !== $value)
+ ->info('A divisor used to divide the value before rendering.')
+ ;
+
+ $resolver->define('use_intl_formatter')
+ ->default(class_exists(IntlFormatter::class))
+ ->allowedTypes('bool')
+ ;
+
+ $resolver->define('intl_formatter_options')
+ ->default(function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefaults([
+ 'attrs' => [],
+ 'style' => 'decimal',
+ ])
+ ->setAllowedTypes('attrs', 'array')
+ ->setAllowedTypes('style', 'string')
+ ;
+ })
+ ;
}
}
diff --git a/src/Column/Type/NumberColumnType.php b/src/Column/Type/NumberColumnType.php
index e1fadc3b..55fbcea6 100755
--- a/src/Column/Type/NumberColumnType.php
+++ b/src/Column/Type/NumberColumnType.php
@@ -9,6 +9,11 @@
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\Formatter\IntlFormatter;
+/**
+ * Represents a column with value displayed as a number.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/number
+ */
final class NumberColumnType extends AbstractColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
@@ -21,26 +26,22 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setDefaults([
- 'use_intl_formatter' => class_exists(IntlFormatter::class),
- 'intl_formatter_options' => function (OptionsResolver $resolver) {
- $resolver
- ->setDefaults([
- 'attrs' => [],
- 'style' => 'decimal',
- ])
- ->setAllowedTypes('attrs', 'array')
- ->setAllowedTypes('style', 'string')
- ;
- },
- ])
- ->setAllowedTypes('use_intl_formatter', 'bool')
+ $resolver->define('use_intl_formatter')
+ ->default(class_exists(IntlFormatter::class))
+ ->allowedTypes('bool')
;
- }
- public function getParent(): ?string
- {
- return TextColumnType::class;
+ $resolver->define('intl_formatter_options')
+ ->default(function (OptionsResolver $resolver) {
+ $resolver
+ ->setDefaults([
+ 'attrs' => [],
+ 'style' => 'decimal',
+ ])
+ ->setAllowedTypes('attrs', 'array')
+ ->setAllowedTypes('style', 'string')
+ ;
+ })
+ ;
}
}
diff --git a/src/Column/Type/ResolvedColumnType.php b/src/Column/Type/ResolvedColumnType.php
index ad5e8613..76e78d44 100755
--- a/src/Column/Type/ResolvedColumnType.php
+++ b/src/Column/Type/ResolvedColumnType.php
@@ -24,9 +24,9 @@ class ResolvedColumnType implements ResolvedColumnTypeInterface
* @param array $typeExtensions
*/
public function __construct(
- private readonly ColumnTypeInterface $innerType,
- private readonly array $typeExtensions = [],
- private readonly ?ResolvedColumnTypeInterface $parent = null,
+ private ColumnTypeInterface $innerType,
+ private array $typeExtensions = [],
+ private ?ResolvedColumnTypeInterface $parent = null,
) {
}
diff --git a/src/Column/Type/TemplateColumnType.php b/src/Column/Type/TemplateColumnType.php
index df80f5d2..cc95ff5c 100755
--- a/src/Column/Type/TemplateColumnType.php
+++ b/src/Column/Type/TemplateColumnType.php
@@ -8,6 +8,11 @@
use Kreyu\Bundle\DataTableBundle\Column\ColumnValueView;
use Symfony\Component\OptionsResolver\OptionsResolver;
+/**
+ * Represents a column rendered from a Twig template.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/template
+ */
final class TemplateColumnType extends AbstractColumnType
{
public function buildValueView(ColumnValueView $view, ColumnInterface $column, array $options): void
@@ -28,17 +33,16 @@ public function buildValueView(ColumnValueView $view, ColumnInterface $column, a
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver
- ->setRequired([
- 'template_path',
- ])
- ->setDefaults([
- 'template_vars' => [],
- ])
- ->setAllowedTypes('template_path', ['string', 'callable'])
- ->setAllowedTypes('template_vars', ['array', 'callable'])
- ->setInfo('template_path', 'A path to the template that should be rendered.')
- ->setInfo('template_vars', 'An array of variables passed to the template.')
+ $resolver->define('template_path')
+ ->required()
+ ->allowedTypes('string', 'callable')
+ ->info('A path to the template that should be rendered.')
+ ;
+
+ $resolver->define('template_vars')
+ ->default([])
+ ->allowedTypes('array', 'callable')
+ ->info('An array of variables passed to the template.')
;
}
}
diff --git a/src/Column/Type/TextColumnType.php b/src/Column/Type/TextColumnType.php
index 8704f364..e5bcab11 100755
--- a/src/Column/Type/TextColumnType.php
+++ b/src/Column/Type/TextColumnType.php
@@ -4,7 +4,11 @@
namespace Kreyu\Bundle\DataTableBundle\Column\Type;
+/**
+ * Represents a column with value displayed as a text.
+ *
+ * @see https://data-table-bundle.swroblewski.pl/reference/types/column/text
+ */
final class TextColumnType extends AbstractColumnType
{
- // ...
}
diff --git a/src/DataTable.php b/src/DataTable.php
index 6423d43f..be656b1a 100755
--- a/src/DataTable.php
+++ b/src/DataTable.php
@@ -172,37 +172,92 @@ public function getConfig(): DataTableConfigInterface
public function getColumns(): array
{
- $columns = $this->columns;
+ uasort($this->columns, function (ColumnInterface $columnA, ColumnInterface $columnB): int {
+ $priorityA = $columnA->getConfig()->getPriority();
+ $priorityB = $columnB->getConfig()->getPriority();
+
+ if ($this->getConfig()->isPersonalizationEnabled()) {
+ if ($columnA->getConfig()->isPersonalizable()) {
+ $priorityA = $this->personalizationData?->getColumn($columnA)?->getPriority() ?? $priorityA;
+ }
+
+ if ($columnB->getConfig()->isPersonalizable()) {
+ $priorityB = $this->personalizationData?->getColumn($columnB)?->getPriority() ?? $priorityB;
+ }
+ }
- uasort($columns, static function (ColumnInterface $columnA, ColumnInterface $columnB): int {
- return $columnB->getPriority() <=> $columnA->getPriority();
+ return $priorityB <=> $priorityA;
});
- return $columns;
+ return $this->columns;
}
public function getVisibleColumns(): array
{
- return array_filter(
- $this->getColumns(),
- static fn (ColumnInterface $column) => $column->isVisible(),
- );
+ return array_filter($this->getColumns(), function (ColumnInterface $column) {
+ $visible = $column->getConfig()->isVisible();
+
+ if ($this->getConfig()->isPersonalizationEnabled() && $column->getConfig()->isPersonalizable()) {
+ $visible = $this->personalizationData?->getColumn($column)?->isVisible() ?? $visible;
+ }
+
+ return $visible;
+ });
}
public function getHiddenColumns(): array
{
- return array_filter(
- $this->getColumns(),
- static fn (ColumnInterface $column) => !$column->isVisible(),
- );
+ return array_filter($this->getColumns(), function (ColumnInterface $column) {
+ $visible = $column->getConfig()->isVisible();
+
+ if ($this->getConfig()->isPersonalizationEnabled() && $column->getConfig()->isPersonalizable()) {
+ $visible = $this->personalizationData?->getColumn($column)?->isVisible() ?? $visible;
+ }
+
+ return !$visible;
+ });
}
public function getExportableColumns(): array
{
- return array_filter(
- $this->getVisibleColumns(),
- static fn (ColumnInterface $column) => $column->getConfig()->isExportable(),
- );
+ $columns = array_filter($this->columns, function (ColumnInterface $column) {
+ if (!$column->getConfig()->isExportable()) {
+ return false;
+ }
+
+ $exportOptions = $column->getConfig()->getOption('export') ?: [];
+
+ $columnPersonalizable = $exportOptions['personalizable'] ?? $column->getConfig()->isPersonalizable();
+ $columnVisible = $exportOptions['visible'] ?? $column->getConfig()->isVisible();
+
+ if ($this->getConfig()->isPersonalizationEnabled() && $columnPersonalizable) {
+ $columnVisible = $this->personalizationData?->getColumn($column)?->isVisible() ?? $columnVisible;
+ }
+
+ return $columnVisible;
+ });
+
+ uasort($columns, function (ColumnInterface $columnA, ColumnInterface $columnB): int {
+ $exportOptionsA = $columnA->getConfig()->getOption('export') ?: [];
+ $exportOptionsB = $columnB->getConfig()->getOption('export') ?: [];
+
+ $priorityA = $exportOptionsA['priority'] ?? $columnA->getConfig()->getPriority();
+ $priorityB = $exportOptionsB['priority'] ?? $columnB->getConfig()->getPriority();
+
+ if ($this->getConfig()->isPersonalizationEnabled()) {
+ if ($exportOptionsA['personalizable'] ?? $columnA->getConfig()->isPersonalizable()) {
+ $priorityA = $this->personalizationData?->getColumn($columnA)?->getPriority() ?? $priorityA;
+ }
+
+ if ($exportOptionsB['personalizable'] ?? $columnB->getConfig()->isPersonalizable()) {
+ $priorityB = $this->personalizationData?->getColumn($columnB)?->getPriority() ?? $priorityB;
+ }
+ }
+
+ return $priorityB <=> $priorityA;
+ });
+
+ return $columns;
}
public function getColumn(string $name): ColumnInterface
@@ -561,8 +616,6 @@ public function personalize(PersonalizationData $data, bool $persistence = true)
$this->setPersonalizationData($data);
- $data->apply($this->getColumns());
-
$this->dispatch(DataTableEvents::POST_PERSONALIZE, new DataTablePersonalizationEvent($this, $data));
}
@@ -588,7 +641,7 @@ public function export(?ExportData $data = null): ExportFile
}
if (!$data->includePersonalization) {
- $dataTable->resetPersonalization();
+ $this->personalizationData = null;
}
if (null === $data->exporter) {
@@ -962,16 +1015,4 @@ private function getPersistenceSubject(PersistenceContext $context): Persistence
return $provider->provide();
}
-
- private function resetPersonalization(): void
- {
- $this->personalizationData = null;
-
- foreach ($this->columns as $column) {
- $column
- ->setPriority($column->getConfig()->getOption('priority'))
- ->setVisible($column->getConfig()->getOption('visible'))
- ;
- }
- }
}
diff --git a/src/Filter/FilterClearUrlGenerator.php b/src/Filter/FilterClearUrlGenerator.php
index 762e95fa..aff9caa3 100644
--- a/src/Filter/FilterClearUrlGenerator.php
+++ b/src/Filter/FilterClearUrlGenerator.php
@@ -37,7 +37,7 @@ public function generate(DataTableView $dataTableView, FilterView ...$filterView
}
// Clearing the filters should reset the pagination to the first page.
- if ($dataTableView->vars['pagination_enabled']) {
+ if ($dataTableView->vars['pagination_enabled'] ?? false) {
$parameters[$dataTableView->vars['page_parameter_name']] = 1;
}
diff --git a/src/Personalization/PersonalizationColumnData.php b/src/Personalization/PersonalizationColumnData.php
index 60905ff1..ea16b8fb 100755
--- a/src/Personalization/PersonalizationColumnData.php
+++ b/src/Personalization/PersonalizationColumnData.php
@@ -47,8 +47,8 @@ public static function fromColumn(ColumnInterface $column): self
{
return new self(
$column->getName(),
- $column->getPriority(),
- $column->isVisible(),
+ $column->getConfig()->getPriority(),
+ $column->getConfig()->isVisible(),
);
}
diff --git a/src/Personalization/PersonalizationData.php b/src/Personalization/PersonalizationData.php
index 9b54524b..d037aae7 100755
--- a/src/Personalization/PersonalizationData.php
+++ b/src/Personalization/PersonalizationData.php
@@ -69,26 +69,6 @@ public static function fromDataTable(DataTableInterface $dataTable): self
));
}
- /**
- * @param array $columns
- */
- public function apply(array $columns): void
- {
- foreach ($columns as $column) {
- if (!$column->getConfig()->isPersonalizable()) {
- continue;
- }
-
- if (null === $data = $this->getColumn($column)) {
- continue;
- }
-
- $column
- ->setPriority($data->getPriority())
- ->setVisible($data->isVisible());
- }
- }
-
public function getColumns(): array
{
return $this->columns;
diff --git a/src/Resources/config/columns.php b/src/Resources/config/columns.php
index 039c72b8..18dd063f 100755
--- a/src/Resources/config/columns.php
+++ b/src/Resources/config/columns.php
@@ -17,7 +17,6 @@
use Kreyu\Bundle\DataTableBundle\Column\Type\DatePeriodColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Type\DateTimeColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Type\EnumColumnType;
-use Kreyu\Bundle\DataTableBundle\Column\Type\FormColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Type\HtmlColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Type\IconColumnType;
use Kreyu\Bundle\DataTableBundle\Column\Type\LinkColumnType;
@@ -86,11 +85,6 @@
->tag('kreyu_data_table.column.type')
;
- $services
- ->set('kreyu_data_table.column.type.form', FormColumnType::class)
- ->tag('kreyu_data_table.column.type')
- ;
-
$services
->set('kreyu_data_table.column.type.link', LinkColumnType::class)
->tag('kreyu_data_table.column.type')
diff --git a/src/Test/Column/Type/ColumnTypeTestCase.php b/src/Test/Column/Type/ColumnTypeTestCase.php
index df96e822..6dcf33f4 100644
--- a/src/Test/Column/Type/ColumnTypeTestCase.php
+++ b/src/Test/Column/Type/ColumnTypeTestCase.php
@@ -83,8 +83,8 @@ protected function createColumnRegistry(): ColumnRegistryInterface
{
return new ColumnRegistry(
types: [
- $this->getTestedColumnType(),
...$this->getAdditionalColumnTypes(),
+ $this->getTestedColumnType(),
],
typeExtensions: [],
resolvedTypeFactory: $this->getResolvedColumnTypeFactory(),
diff --git a/src/Test/DataTableIntegrationTestCase.php b/src/Test/DataTableIntegrationTestCase.php
new file mode 100644
index 00000000..c804a2aa
--- /dev/null
+++ b/src/Test/DataTableIntegrationTestCase.php
@@ -0,0 +1,109 @@
+dataTableFactory = $this->createDataTableFactory();
+ }
+
+ protected function createDataTableFactory(): DataTableFactoryInterface
+ {
+ return new DataTableFactory($this->createDataTableRegistry());
+ }
+
+ protected function createDataTableRegistry(): DataTableRegistry
+ {
+ return new DataTableRegistry(
+ types: $this->getDataTableTypes(),
+ typeExtensions: $this->getDataTableTypeExtensions(),
+ proxyQueryFactories: $this->getProxyQueryFactories(),
+ resolvedTypeFactory: $this->getResolvedDataTableTypeFactory(),
+ );
+ }
+
+ /**
+ * @return DataTableTypeInterface[]
+ */
+ protected function getDataTableTypes(): array
+ {
+ return [
+ new DataTableType([
+ 'column_factory' => $this->createColumnFactory(),
+ ]),
+ ];
+ }
+
+ protected function getDataTableTypeExtensions(): array
+ {
+ return [];
+ }
+
+ protected function getProxyQueryFactories(): array
+ {
+ return [
+ new ArrayProxyQueryFactory(),
+ ];
+ }
+
+ protected function getResolvedDataTableTypeFactory(): ResolvedDataTableTypeFactoryInterface
+ {
+ return new ResolvedDataTableTypeFactory();
+ }
+
+ protected function createColumnFactory(): ColumnFactoryInterface
+ {
+ return new ColumnFactory($this->createColumnRegistry());
+ }
+
+ protected function createColumnRegistry(): ColumnRegistryInterface
+ {
+ return new ColumnRegistry(
+ types: $this->getColumnTypes(),
+ typeExtensions: $this->getColumnTypeExtensions(),
+ resolvedTypeFactory: $this->getResolvedColumnTypeFactory(),
+ );
+ }
+
+ protected function getColumnTypes(): array
+ {
+ return [
+ new ColumnType(),
+ new TextColumnType(),
+ ];
+ }
+
+ protected function getColumnTypeExtensions(): array
+ {
+ return [];
+ }
+
+ protected function getResolvedColumnTypeFactory(): ResolvedColumnTypeFactoryInterface
+ {
+ return new ResolvedColumnTypeFactory();
+ }
+}
diff --git a/src/Test/DataTableTypeTestCase.php b/src/Test/DataTableTypeTestCase.php
new file mode 100644
index 00000000..76cd5241
--- /dev/null
+++ b/src/Test/DataTableTypeTestCase.php
@@ -0,0 +1,32 @@
+
+ */
+ abstract protected function getTestedType(): string;
+
+ protected function createDataTable(array $options = []): DataTableInterface
+ {
+ return $this->dataTableFactory->create($this->getTestedType(), $options);
+ }
+
+ protected function createNamedDataTable(string $name, array $options = []): DataTableInterface
+ {
+ return $this->dataTableFactory->createNamed($name, $this->getTestedType(), $options);
+ }
+
+ protected function createDataTableView(DataTableInterface $dataTable): DataTableView
+ {
+ return $dataTable->createView();
+ }
+}
diff --git a/tests/Unit/Bridge/Doctrine/Orm/Filter/ExpressionFactory/ExpressionFactoryTest.php b/tests/Unit/Bridge/Doctrine/Orm/Filter/ExpressionFactory/ExpressionFactoryTest.php
index e6fd5f9c..2bf7789d 100644
--- a/tests/Unit/Bridge/Doctrine/Orm/Filter/ExpressionFactory/ExpressionFactoryTest.php
+++ b/tests/Unit/Bridge/Doctrine/Orm/Filter/ExpressionFactory/ExpressionFactoryTest.php
@@ -45,7 +45,7 @@ public function testItCreatesExpression(string $queryPath, FilterData $data, arr
public static function expectedExpressionProvider(): iterable
{
yield 'equals' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::Equals),
'parameters' => [
new Parameter('bar', null),
@@ -54,7 +54,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'not equals' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::NotEquals),
'parameters' => [
new Parameter('bar', null),
@@ -63,7 +63,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'contains' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::Contains),
'parameters' => [
new Parameter('bar', null),
@@ -72,7 +72,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'not contains' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::NotContains),
'parameters' => [
new Parameter('bar', null),
@@ -81,7 +81,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'in' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::In),
'parameters' => [
new Parameter('bar', null),
@@ -90,7 +90,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'not in' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::NotIn),
'parameters' => [
new Parameter('bar', null),
@@ -99,7 +99,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'greater than' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::GreaterThan),
'parameters' => [
new Parameter('bar', null),
@@ -108,7 +108,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'greater than or equals' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::GreaterThanEquals),
'parameters' => [
new Parameter('bar', null),
@@ -117,7 +117,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'less than' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::LessThan),
'parameters' => [
new Parameter('bar', null),
@@ -126,7 +126,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'less than or equals' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::LessThanEquals),
'parameters' => [
new Parameter('bar', null),
@@ -135,7 +135,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'starts with' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::StartsWith),
'parameters' => [
new Parameter('bar', null),
@@ -144,7 +144,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'ends with' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::EndsWith),
'parameters' => [
new Parameter('bar', null),
@@ -153,7 +153,7 @@ public static function expectedExpressionProvider(): iterable
];
yield 'between' => [
- 'query_path' => 'foo',
+ 'queryPath' => 'foo',
'data' => new FilterData(null, Operator::Between),
'parameters' => [
'from' => new Parameter('bar', null),
diff --git a/tests/Unit/Column/ColumnBuilderTest.php b/tests/Unit/Column/ColumnBuilderTest.php
index ad3fab29..5580b744 100644
--- a/tests/Unit/Column/ColumnBuilderTest.php
+++ b/tests/Unit/Column/ColumnBuilderTest.php
@@ -16,34 +16,14 @@ class ColumnBuilderTest extends TestCase
public function testGetColumn(): void
{
$builder = $this->createBuilder();
- $builder->setPriority(100);
- $builder->setVisible(false);
$config = $builder->getColumnConfig();
$column = $builder->getColumn();
$this->assertEquals($config, $column->getConfig());
- $this->assertSame(100, $column->getPriority());
- $this->assertFalse($column->isVisible());
$this->assertTrue($this->getPrivatePropertyValue($config, 'locked'));
}
- public function testGetPriority()
- {
- $builder = $this->createBuilder();
-
- $this->assertEquals(0, $builder->getPriority());
- $this->assertEquals(100, $builder->setPriority(100)->getPriority());
- }
-
- public function testIsVisible()
- {
- $builder = $this->createBuilder();
-
- $this->assertTrue($builder->setVisible(true)->isVisible());
- $this->assertFalse($builder->setVisible(false)->isVisible());
- }
-
private function createBuilder(): ColumnBuilder
{
return new ColumnBuilder(
diff --git a/tests/Unit/Column/ColumnConfigBuilderTest.php b/tests/Unit/Column/ColumnConfigBuilderTest.php
index e1fe1cf4..f1777861 100644
--- a/tests/Unit/Column/ColumnConfigBuilderTest.php
+++ b/tests/Unit/Column/ColumnConfigBuilderTest.php
@@ -124,6 +124,22 @@ public function testIsPersonalizable()
$this->assertFalse($builder->setPersonalizable(false)->isPersonalizable());
}
+ public function testIsVisible()
+ {
+ $builder = $this->createBuilder();
+
+ $this->assertTrue($builder->setVisible(true)->isVisible());
+ $this->assertFalse($builder->setVisible(false)->isVisible());
+ }
+
+ public function testGetPriority()
+ {
+ $builder = $this->createBuilder();
+ $builder->setPriority(100);
+
+ $this->assertEquals(100, $builder->getPriority());
+ }
+
public function testGetColumnFactoryWithoutFactorySet()
{
$this->expectException(BadMethodCallException::class);
diff --git a/tests/Unit/Column/Type/ColumnTypeTest.php b/tests/Unit/Column/Type/ColumnTypeTest.php
index 8f5fce4e..857fe847 100644
--- a/tests/Unit/Column/Type/ColumnTypeTest.php
+++ b/tests/Unit/Column/Type/ColumnTypeTest.php
@@ -31,11 +31,6 @@ protected function getTestedColumnType(): ColumnTypeInterface
return new ColumnType($this->translator);
}
- protected function getAdditionalColumnTypes(): array
- {
- return [];
- }
-
public function testDefaultLabelInheritsFromName(): void
{
$column = $this->createNamedColumn('firstName');
@@ -795,7 +790,7 @@ public function testPassingPriorityOption(): void
'priority' => 10,
]);
- $this->assertEquals(10, $column->getPriority());
+ $this->assertEquals(10, $column->getConfig()->getPriority());
}
public function testPassingVisibleOption(): void
@@ -804,7 +799,7 @@ public function testPassingVisibleOption(): void
'visible' => false,
]);
- $this->assertFalse($column->isVisible());
+ $this->assertFalse($column->getConfig()->isVisible());
}
public function testPassingPersonalizableOption(): void
diff --git a/tests/Unit/DataTableTest.php b/tests/Unit/DataTableTest.php
new file mode 100644
index 00000000..22ef1fe4
--- /dev/null
+++ b/tests/Unit/DataTableTest.php
@@ -0,0 +1,361 @@
+createDataTableBuilder()
+ ->addColumn('first', options: ['priority' => 1])
+ ->addColumn('second', options: ['priority' => 2])
+ ->addColumn('third', options: ['priority' => 3])
+ ->addColumn('fourth', options: ['priority' => 4])
+ ->addColumn('fifth', options: ['priority' => 5])
+ ->getDataTable()
+ ;
+
+ $columns = array_keys($dataTable->getColumns());
+
+ $this->assertEquals(['fifth', 'fourth', 'third', 'second', 'first'], $columns);
+ }
+
+ public function testGetColumnsRespectsPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => true])
+ ->addColumn('first', options: ['priority' => 1])
+ ->addColumn('second', options: ['priority' => 2])
+ ->addColumn('third', options: ['priority' => 3])
+ ->addColumn('fourth', options: ['priority' => 4])
+ ->addColumn('fifth', options: ['priority' => 100, 'personalizable' => false])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5],
+ 'second' => ['priority' => 4],
+ 'third' => ['priority' => 3],
+ 'fourth' => ['priority' => 2],
+ // Should be ignored, because column has "personalizable" option set to false
+ 'fifth' => ['priority' => 1],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getColumns());
+
+ $this->assertEquals(['fifth', 'first', 'second', 'third', 'fourth'], $columns);
+ }
+
+ public function testGetColumnsIgnoresDisabledPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => false])
+ ->addColumn('first', options: ['priority' => 1])
+ ->addColumn('second', options: ['priority' => 2])
+ ->addColumn('third', options: ['priority' => 3])
+ ->addColumn('fourth', options: ['priority' => 4])
+ ->addColumn('fifth', options: ['priority' => 5])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5],
+ 'second' => ['priority' => 4],
+ 'third' => ['priority' => 3],
+ 'fourth' => ['priority' => 2],
+ 'fifth' => ['priority' => 1],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getColumns());
+
+ $this->assertEquals(['fifth', 'fourth', 'third', 'second', 'first'], $columns);
+ }
+
+ public function testGetVisibleColumns()
+ {
+ $dataTable = $this->createDataTableBuilder()
+ ->addColumn('first', options: ['priority' => 1, 'visible' => true])
+ ->addColumn('second', options: ['priority' => 2, 'visible' => false])
+ ->addColumn('third', options: ['priority' => 3, 'visible' => true])
+ ->addColumn('fourth', options: ['priority' => 4, 'visible' => false])
+ ->addColumn('fifth', options: ['priority' => 5, 'visible' => true])
+ ->getDataTable();
+
+ $columns = array_keys($dataTable->getVisibleColumns());
+
+ $this->assertEquals(['fifth', 'third', 'first'], $columns);
+ }
+
+ public function testGetVisibleColumnsRespectsPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => true])
+ ->addColumn('first', options: ['priority' => 1, 'visible' => true])
+ ->addColumn('second', options: ['priority' => 2, 'visible' => false])
+ ->addColumn('third', options: ['priority' => 3, 'visible' => true])
+ ->addColumn('fourth', options: ['priority' => 4, 'visible' => false])
+ ->addColumn('fifth', options: ['priority' => 100, 'visible' => true, 'personalizable' => false])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5, 'visible' => false],
+ 'second' => ['priority' => 4, 'visible' => true],
+ 'third' => ['priority' => 3, 'visible' => false],
+ 'fourth' => ['priority' => 2, 'visible' => true],
+ // Should be ignored, because column has "personalizable" option set to false
+ 'fifth' => ['priority' => 1, 'visible' => false],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getVisibleColumns());
+
+ $this->assertEquals(['fifth', 'second', 'fourth'], $columns);
+ }
+
+ public function testGetVisibleColumnsIgnoresDisabledPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => false])
+ ->addColumn('first', options: ['priority' => 1, 'visible' => true])
+ ->addColumn('second', options: ['priority' => 2, 'visible' => false])
+ ->addColumn('third', options: ['priority' => 3, 'visible' => true])
+ ->addColumn('fourth', options: ['priority' => 4, 'visible' => false])
+ ->addColumn('fifth', options: ['priority' => 5, 'visible' => true])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5, 'visible' => false],
+ 'second' => ['priority' => 4, 'visible' => true],
+ 'third' => ['priority' => 3, 'visible' => false],
+ 'fourth' => ['priority' => 2, 'visible' => true],
+ 'fifth' => ['priority' => 1, 'visible' => false],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getVisibleColumns());
+
+ $this->assertEquals(['fifth', 'third', 'first'], $columns);
+ }
+
+ public function testGetHiddenColumns()
+ {
+ $dataTable = $this->createDataTableBuilder()
+ ->addColumn('first', options: ['visible' => true])
+ ->addColumn('second', options: ['visible' => false])
+ ->addColumn('third', options: ['visible' => true])
+ ->addColumn('fourth', options: ['visible' => false])
+ ->addColumn('fifth', options: ['visible' => true])
+ ->getDataTable();
+
+ $columns = array_keys($dataTable->getHiddenColumns());
+
+ $this->assertEquals(['second', 'fourth'], $columns);
+ }
+
+ public function testGetHiddenColumnsRespectsPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => true])
+ ->addColumn('first', options: ['priority' => 1, 'visible' => true])
+ ->addColumn('second', options: ['priority' => 2, 'visible' => false])
+ ->addColumn('third', options: ['priority' => 3, 'visible' => true])
+ ->addColumn('fourth', options: ['priority' => 100, 'visible' => false, 'personalizable' => false])
+ ->addColumn('fifth', options: ['priority' => 5, 'visible' => true])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5, 'visible' => false],
+ 'second' => ['priority' => 4, 'visible' => true],
+ 'third' => ['priority' => 3, 'visible' => false],
+ // Should be ignored, because column has "personalizable" option set to false
+ 'fourth' => ['priority' => 2, 'visible' => true],
+ 'fifth' => ['priority' => 1, 'visible' => false],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getHiddenColumns());
+
+ $this->assertEquals(['fourth', 'first', 'third', 'fifth'], $columns);
+ }
+
+ public function testGetHiddenColumnsIgnoresDisabledPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => false])
+ ->addColumn('first', options: ['priority' => 1, 'visible' => true])
+ ->addColumn('second', options: ['priority' => 2, 'visible' => false])
+ ->addColumn('third', options: ['priority' => 3, 'visible' => true])
+ ->addColumn('fourth', options: ['priority' => 4, 'visible' => false])
+ ->addColumn('fifth', options: ['priority' => 5, 'visible' => true])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5, 'visible' => false],
+ 'second' => ['priority' => 4, 'visible' => true],
+ 'third' => ['priority' => 3, 'visible' => false],
+ 'fourth' => ['priority' => 2, 'visible' => true],
+ 'fifth' => ['priority' => 1, 'visible' => false],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getHiddenColumns());
+
+ $this->assertEquals(['fourth', 'second'], $columns);
+ }
+
+ public function testGetExportableColumns()
+ {
+ $dataTable = $this->createDataTableBuilder()
+ ->addColumn('first', options: [
+ 'priority' => 1,
+ 'visible' => true,
+ 'export' => true,
+ ])
+ ->addColumn('second', options: [
+ 'priority' => 2,
+ 'visible' => true,
+ 'export' => [
+ 'visible' => false,
+ ],
+ ])
+ ->addColumn('third', options: [
+ 'priority' => 3,
+ 'visible' => true,
+ 'export' => [
+ 'priority' => 100,
+ ],
+ ])
+ ->addColumn('fourth', options: [
+ 'priority' => 4,
+ 'visible' => false,
+ 'export' => [
+ 'visible' => true,
+ ],
+ ])
+ ->addColumn('fifth', options: [
+ 'priority' => 5,
+ 'visible' => true,
+ 'export' => false,
+ ])
+ ->getDataTable();
+
+ $columns = array_keys($dataTable->getExportableColumns());
+
+ $this->assertEquals(['third', 'fourth', 'first'], $columns);
+ }
+
+ public function testGetExportableColumnsRespectsPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => true])
+ ->addColumn('first', options: [
+ 'priority' => 1,
+ 'visible' => true,
+ 'export' => true,
+ ])
+ ->addColumn('second', options: [
+ 'priority' => 2,
+ 'visible' => true,
+ 'export' => [
+ 'visible' => false,
+ ],
+ ])
+ ->addColumn('third', options: [
+ 'priority' => 3,
+ 'visible' => true,
+ 'export' => [
+ 'priority' => 100,
+ ],
+ ])
+ ->addColumn('fourth', options: [
+ 'priority' => 4,
+ 'visible' => false,
+ 'export' => [
+ 'visible' => true,
+ 'personalizable' => false,
+ ],
+ ])
+ ->addColumn('fifth', options: [
+ 'priority' => 5,
+ 'visible' => true,
+ 'export' => false,
+ ])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5, 'visible' => false],
+ 'second' => ['priority' => 4, 'visible' => true],
+ 'third' => ['priority' => 3, 'visible' => false],
+ 'fourth' => ['priority' => 2, 'visible' => false],
+ 'fifth' => ['priority' => 1, 'visible' => false],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getExportableColumns());
+
+ $this->assertEquals(['second', 'fourth'], $columns);
+ }
+
+ public function testGetExportableColumnsIgnoresDisabledPersonalization()
+ {
+ $dataTable = $this->createDataTableBuilder(['personalization_enabled' => false])
+ ->addColumn('first', options: [
+ 'priority' => 1,
+ 'visible' => true,
+ 'export' => true,
+ ])
+ ->addColumn('second', options: [
+ 'priority' => 2,
+ 'visible' => true,
+ 'export' => [
+ 'visible' => false,
+ ],
+ ])
+ ->addColumn('third', options: [
+ 'priority' => 3,
+ 'visible' => true,
+ 'export' => [
+ 'priority' => 100,
+ ],
+ ])
+ ->addColumn('fourth', options: [
+ 'priority' => 4,
+ 'visible' => false,
+ 'export' => [
+ 'visible' => true,
+ ],
+ ])
+ ->addColumn('fifth', options: [
+ 'priority' => 5,
+ 'visible' => true,
+ 'export' => false,
+ ])
+ ->getDataTable();
+
+ $dataTable->setPersonalizationData(PersonalizationData::fromArray([
+ 'columns' => [
+ 'first' => ['priority' => 5, 'visible' => false],
+ 'second' => ['priority' => 4, 'visible' => true],
+ 'third' => ['priority' => 3, 'visible' => false],
+ 'fourth' => ['priority' => 2, 'visible' => false],
+ 'fifth' => ['priority' => 1, 'visible' => false],
+ ],
+ ]));
+
+ $columns = array_keys($dataTable->getExportableColumns());
+
+ $this->assertEquals(['third', 'fourth', 'first'], $columns);
+ }
+
+ private function createDataTableBuilder(array $options = []): DataTableBuilderInterface
+ {
+ return $this->dataTableFactory->createBuilder(DataTableType::class, [], $options);
+ }
+}