diff --git a/lib/Core/Exporter/JsonExporter.php b/lib/Core/Exporter/JsonExporter.php index 768a7a9d..d6c9431b 100644 --- a/lib/Core/Exporter/JsonExporter.php +++ b/lib/Core/Exporter/JsonExporter.php @@ -32,7 +32,32 @@ final class JsonExporter extends AbstractExporter { public function exportCondition(SearchCondition $condition): string { - return (string) json_encode($this->exportGroup($condition->getValuesGroup(), $condition->getFieldSet(), true)); + $fieldSet = $condition->getFieldSet(); + + return (string) json_encode( + array_merge( + $this->exportOrder($condition, $fieldSet), + $this->exportGroup($condition->getValuesGroup(), $fieldSet, true) + ), + \JSON_THROW_ON_ERROR + ); + } + + protected function exportOrder(SearchCondition $condition, FieldSet $fieldSet): array + { + $order = $condition->getOrder(); + + if ($order === null) { + return []; + } + + $result = []; + + foreach ($order->getFields() as $name => $direction) { + $result[mb_substr($name, 1)] = $this->modelToNorm($direction, $fieldSet->get($name)); + } + + return $result ? ['order' => $result] : []; } protected function exportGroup(ValuesGroup $valuesGroup, FieldSet $fieldSet, bool $isRoot = false): array diff --git a/lib/Core/Exporter/StringExporter.php b/lib/Core/Exporter/StringExporter.php index fbcaea16..1b640528 100644 --- a/lib/Core/Exporter/StringExporter.php +++ b/lib/Core/Exporter/StringExporter.php @@ -37,13 +37,31 @@ abstract class StringExporter extends AbstractExporter public function exportCondition(SearchCondition $condition): string { - $this->fields = $this->resolveLabels($condition->getFieldSet()); + $this->fields = $this->resolveLabels($fieldSet = $condition->getFieldSet()); - return $this->exportGroup($condition->getValuesGroup(), $condition->getFieldSet(), true); + return $this->exportOrder($condition, $fieldSet) . $this->exportGroup($condition->getValuesGroup(), $fieldSet, true); } abstract protected function resolveLabels(FieldSet $fieldSet): array; + protected function exportOrder(SearchCondition $condition, FieldSet $fieldSet): string + { + $order = $condition->getOrder(); + + if ($order === null) { + return ''; + } + + $result = ''; + + foreach ($order->getFields() as $name => $direction) { + $result .= $this->getFieldLabel($name); + $result .= ': ' . $this->modelToExported($direction, $fieldSet->get($name)) . '; '; + } + + return trim($result); + } + protected function exportGroup(ValuesGroup $valuesGroup, FieldSet $fieldSet, bool $isRoot = false): string { $result = ''; diff --git a/lib/Core/Extension/Core/DataTransformer/OrderToLocalizedTransformer.php b/lib/Core/Extension/Core/DataTransformer/OrderToLocalizedTransformer.php new file mode 100644 index 00000000..83aa0e10 --- /dev/null +++ b/lib/Core/Extension/Core/DataTransformer/OrderToLocalizedTransformer.php @@ -0,0 +1,99 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Rollerworks\Component\Search\Extension\Core\DataTransformer; + +use Rollerworks\Component\Search\DataTransformer; +use Rollerworks\Component\Search\Exception\TransformationFailedException; + +final class OrderToLocalizedTransformer implements DataTransformer +{ + private array $alias; + private array $viewLabel; + private string $case; + + public function __construct(array $alias, array $viewLabel, string $case = OrderTransformer::CASE_UPPERCASE) + { + $this->case = $case; + $this->alias = $alias; + $this->viewLabel = $viewLabel; + } + + public function transform($value) + { + if ($value === null) { + return ''; + } + + if (! \is_string($value)) { + throw new TransformationFailedException('Expected a string or null.'); + } + + switch ($this->case) { + case OrderTransformer::CASE_LOWERCASE: + $value = mb_strtolower($value); + + break; + + case OrderTransformer::CASE_UPPERCASE: + $value = mb_strtoupper($value); + + break; + } + + if (! isset($this->viewLabel[$value])) { + throw new TransformationFailedException(sprintf('No localized label configured for "%s".', $value)); + } + + return $this->viewLabel[$value]; + } + + public function reverseTransform($value) + { + if ($value !== null && ! \is_string($value)) { + throw new TransformationFailedException('Expected a string or null.'); + } + + if ($value === '') { + return null; + } + + switch ($this->case) { + case OrderTransformer::CASE_LOWERCASE: + $value = mb_strtolower($value); + + break; + + case OrderTransformer::CASE_UPPERCASE: + $value = mb_strtoupper($value); + + break; + } + + if (! isset($this->alias[$value])) { + throw new TransformationFailedException( + sprintf( + 'Invalid sort direction "%1$s" specified, expected one of: "%2$s"', + $value, + implode('", "', array_keys($this->alias)) + ), + 0, + null, + 'This value is not a valid sorting direction. Accepted directions are "{{ directions }}".', + ['{{ directions }}' => mb_strtolower(implode('", "', array_unique(array_keys($this->alias))))] + ); + } + + return $this->alias[$value]; + } +} diff --git a/lib/Core/Extension/Core/DataTransformer/OrderTransformer.php b/lib/Core/Extension/Core/DataTransformer/OrderTransformer.php index d9951fc8..5f89a7e2 100644 --- a/lib/Core/Extension/Core/DataTransformer/OrderTransformer.php +++ b/lib/Core/Extension/Core/DataTransformer/OrderTransformer.php @@ -34,16 +34,10 @@ final class OrderTransformer implements DataTransformer */ private $case; - /** - * @var string|null - */ - private $default; - - public function __construct(array $alias, string $case = self::CASE_UPPERCASE, ?string $default = null) + public function __construct(array $alias, string $case = self::CASE_UPPERCASE) { $this->alias = $alias; $this->case = $case; - $this->default = $default; } public function transform($value) @@ -87,7 +81,11 @@ public function reverseTransform($value) 'Invalid sort direction "%1$s" specified, expected one of: "%2$s"', $value, implode('", "', array_keys($this->alias)) - ) + ), + 0, + null, + 'This value is not a valid sorting direction. Accepted directions are "{{ directions }}".', + ['{{ directions }}' => mb_strtolower(implode('", "', array_unique(array_keys($this->alias))))] ); } diff --git a/lib/Core/Field/OrderFieldType.php b/lib/Core/Field/OrderFieldType.php index 4323216c..11addde4 100644 --- a/lib/Core/Field/OrderFieldType.php +++ b/lib/Core/Field/OrderFieldType.php @@ -13,7 +13,9 @@ namespace Rollerworks\Component\Search\Field; +use Rollerworks\Component\Search\Extension\Core\DataTransformer\OrderToLocalizedTransformer; use Rollerworks\Component\Search\Extension\Core\DataTransformer\OrderTransformer; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; /** @@ -32,6 +34,7 @@ public function configureOptions(OptionsResolver $resolver): void 'default' => null, 'case' => OrderTransformer::CASE_UPPERCASE, 'alias' => ['ASC' => 'ASC', 'DESC' => 'DESC'], + 'view_label' => ['ASC' => 'asc', 'DESC' => 'desc'], 'type' => null, 'type_options' => [], ]); @@ -41,17 +44,46 @@ public function configureOptions(OptionsResolver $resolver): void OrderTransformer::CASE_UPPERCASE, ]); $resolver->setAllowedTypes('alias', 'array'); + $resolver->setAllowedTypes('view_label', ['array']); $resolver->setAllowedTypes('default', ['null', 'string']); $resolver->setAllowedTypes('type', ['string', 'null']); $resolver->setAllowedTypes('type_options', ['array']); + + // Ensure view-labels are part of the alias list. + $resolver->addNormalizer('alias', static function (Options $options, array $value): mixed { + // Must always exist for interoperability, but it's still possible to overwrite. + $value = array_merge($value, + $options['case'] === OrderTransformer::CASE_LOWERCASE ? ['asc' => 'ASC', 'desc' => 'DESC'] : ['ASC' => 'ASC', 'DESC' => 'DESC'] + ); + + foreach ($options['view_label'] as $direction => $label) { + switch ($options['case']) { + case OrderTransformer::CASE_LOWERCASE: + $label = mb_strtolower($label); + + break; + + case OrderTransformer::CASE_UPPERCASE: + $label = mb_strtoupper($label); + + break; + } + + if (isset($value[$label])) { + continue; + } + + $value[$label] = $direction; + } + + return $value; + }); } public function buildType(FieldConfig $config, array $options): void { - $transformer = new OrderTransformer($options['alias'], $options['case'], $options['default']); - - $config->setNormTransformer($transformer); - $config->setViewTransformer($transformer); + $config->setNormTransformer(new OrderTransformer($options['alias'], $options['case'])); + $config->setViewTransformer(new OrderToLocalizedTransformer($options['alias'], $options['view_label'], $options['case'])); } public function buildView(SearchFieldView $view, FieldConfig $config, array $options): void diff --git a/lib/Core/Input/JsonInput.php b/lib/Core/Input/JsonInput.php index f9d1204f..ea55fcba 100644 --- a/lib/Core/Input/JsonInput.php +++ b/lib/Core/Input/JsonInput.php @@ -35,6 +35,8 @@ * Each entry must contain an array with 'fields' and/or 'groups' structures. * Optionally the array can contain 'logical-case' => 'OR' to make it OR-cased. * + * The 'order' setting can only be applied at root level, and must NOT begin with the @-sign. + * * The 'groups' array contains groups with the keys as described above ('fields' and/or 'groups'). * * The fields array is an hash-map where each key is the field-name diff --git a/lib/Core/Input/OrderStructureBuilder.php b/lib/Core/Input/OrderStructureBuilder.php index 875e9031..b1e2a0cd 100644 --- a/lib/Core/Input/OrderStructureBuilder.php +++ b/lib/Core/Input/OrderStructureBuilder.php @@ -59,13 +59,16 @@ final class OrderStructureBuilder implements StructureBuilder */ private $inputTransformer; - public function __construct(ProcessorConfig $config, Validator $validator, ErrorList $errorList, string $path = '') + private bool $viewFormat; + + public function __construct(ProcessorConfig $config, Validator $validator, ErrorList $errorList, string $path = '', bool $viewFormat = false) { $this->fieldSet = $config->getFieldSet(); $this->validator = $validator; $this->path = $path ?: 'order'; $this->errorList = $errorList; $this->valuesGroup = new ValuesGroup(); + $this->viewFormat = $viewFormat; } public function getErrors(): ErrorList @@ -100,6 +103,7 @@ public function field(string $name, string $path): void } $this->fieldConfig = $this->fieldSet->get($name); + $this->inputTransformer = ($this->viewFormat ? $this->fieldConfig->getViewTransformer() : $this->fieldConfig->getNormTransformer()) ?? false; $this->valuesBag = $this->valuesGroup->getField($name); @@ -182,10 +186,6 @@ private function addError(ConditionErrorMessage $error): void */ private function inputToNorm($value, string $path) { - if ($this->inputTransformer === null) { - $this->inputTransformer = $this->fieldConfig->getNormTransformer() ?? false; - } - if ($this->inputTransformer === false) { if ($value !== null && ! \is_scalar($value)) { $e = new \RuntimeException( diff --git a/lib/Core/Input/ProcessorConfig.php b/lib/Core/Input/ProcessorConfig.php index 4ab97f89..df6344cc 100644 --- a/lib/Core/Input/ProcessorConfig.php +++ b/lib/Core/Input/ProcessorConfig.php @@ -134,7 +134,6 @@ public function getCacheTTL(): \DateInterval|int|null return $this->cacheTTL; } - public function getDefaultField(bool $error = false): ?string { if ($this->defaultField === null && $error) { diff --git a/lib/Core/Input/StringQueryInput.php b/lib/Core/Input/StringQueryInput.php index a5d714ae..22e08095 100644 --- a/lib/Core/Input/StringQueryInput.php +++ b/lib/Core/Input/StringQueryInput.php @@ -71,7 +71,9 @@ protected function initForProcess(ProcessorConfig $config): void $this->orderStructureBuilder = new OrderStructureBuilder( $this->config, $this->validator, - $this->errors + $this->errors, + '', + true ); } } diff --git a/lib/Core/Resources/translations/validators.en.xlf b/lib/Core/Resources/translations/validators.en.xlf index 9dad85d2..8a1ced93 100644 --- a/lib/Core/Resources/translations/validators.en.xlf +++ b/lib/Core/Resources/translations/validators.en.xlf @@ -42,6 +42,10 @@ This value is not a valid datetime. This value is not a valid datetime. + + This value is not a valid sorting direction. Accepted directions are "{{ directions }}". + This value is not a valid sorting direction. Accepted directions are "{{ directions }}". + diff --git a/lib/Core/Resources/translations/validators.nl.xlf b/lib/Core/Resources/translations/validators.nl.xlf index f226ac56..e919a3ee 100644 --- a/lib/Core/Resources/translations/validators.nl.xlf +++ b/lib/Core/Resources/translations/validators.nl.xlf @@ -43,6 +43,10 @@ This value is not a valid datetime. Deze waarde is geen geldige datum en tijd. + + This value is not a valid sorting direction. Accepted directions are "{{ directions }}". + Deze waarde is geen geldige sorteer richting. De geaccepteerde richtingen zijn : "{{ directions }}". + diff --git a/lib/Core/Test/SearchConditionExporterTestCase.php b/lib/Core/Test/SearchConditionExporterTestCase.php index 1253187b..1bea5917 100644 --- a/lib/Core/Test/SearchConditionExporterTestCase.php +++ b/lib/Core/Test/SearchConditionExporterTestCase.php @@ -18,10 +18,12 @@ use Rollerworks\Component\Search\Extension\Core\Type\IntegerType; use Rollerworks\Component\Search\Extension\Core\Type\MoneyType; use Rollerworks\Component\Search\Extension\Core\Type\TextType; +use Rollerworks\Component\Search\Field\OrderFieldType; use Rollerworks\Component\Search\GenericFieldSetBuilder; use Rollerworks\Component\Search\Input\ProcessorConfig; use Rollerworks\Component\Search\InputProcessor; use Rollerworks\Component\Search\SearchCondition; +use Rollerworks\Component\Search\SearchOrder; use Rollerworks\Component\Search\Value\Compare; use Rollerworks\Component\Search\Value\ExcludedRange; use Rollerworks\Component\Search\Value\PatternMatch; @@ -53,6 +55,9 @@ protected function getFieldSet(bool $build = true) $fieldSet->add('date', DateType::class, ['pattern' => 'MM-dd-yyyy']); $fieldSet->set($priceField); + $fieldSet->add('@id', OrderFieldType::class); + $fieldSet->add('@status', OrderFieldType::class); + return $build ? $fieldSet->getFieldSet() : $fieldSet; } @@ -366,6 +371,28 @@ public function it_exporters_with_empty_group(): void abstract public function provideEmptyGroupTest(); + /** @test */ + public function it_exports_with_ordering(): void + { + $exporter = $this->getExporter(); + $config = new ProcessorConfig($this->getFieldSet()); + + $condition = new SearchCondition($config->getFieldSet(), new ValuesGroup()); + + $orderGroup = (new ValuesGroup()) + ->addField('@id', (new ValuesBag())->addSimpleValue('DESC')) + ->addField('@status', (new ValuesBag())->addSimpleValue('ASC')) + ; + $condition->setOrder(new SearchOrder($orderGroup)); + + $this->assertExportEquals($this->provideOrderTest(), $exporter->exportCondition($condition)); + + $processor = $this->getInputProcessor(); + $this->assertConditionEquals($this->provideOrderTest(), $condition, $processor, $config); + } + + abstract public function provideOrderTest(); + protected function assertExportEquals($expected, $actual): void { self::assertEquals($expected, $actual); diff --git a/lib/Core/Tests/Exporter/JsonExporterTest.php b/lib/Core/Tests/Exporter/JsonExporterTest.php index 82936ffc..3d728a40 100644 --- a/lib/Core/Tests/Exporter/JsonExporterTest.php +++ b/lib/Core/Tests/Exporter/JsonExporterTest.php @@ -236,6 +236,11 @@ public function provideEmptyGroupTest() return json_encode(['groups' => [[]]]); } + public function provideOrderTest() + { + return json_encode(['order' => ['id' => 'desc', 'status' => 'asc']], \JSON_THROW_ON_ERROR); + } + protected function getExporter(): ConditionExporter { return new JsonExporter(); diff --git a/lib/Core/Tests/Exporter/NormStringQueryExporterTest.php b/lib/Core/Tests/Exporter/NormStringQueryExporterTest.php index 943734fd..ec70e821 100644 --- a/lib/Core/Tests/Exporter/NormStringQueryExporterTest.php +++ b/lib/Core/Tests/Exporter/NormStringQueryExporterTest.php @@ -115,6 +115,11 @@ public function provideEmptyGroupTest() return '( );'; } + public function provideOrderTest() + { + return '@id: desc; @status: asc;'; + } + protected function getExporter(): ConditionExporter { return new NormStringQueryExporter(); diff --git a/lib/Core/Tests/Exporter/StringQueryExporterTest.php b/lib/Core/Tests/Exporter/StringQueryExporterTest.php index 37f832d8..ff005e44 100644 --- a/lib/Core/Tests/Exporter/StringQueryExporterTest.php +++ b/lib/Core/Tests/Exporter/StringQueryExporterTest.php @@ -16,10 +16,12 @@ use Rollerworks\Component\Search\ConditionExporter; use Rollerworks\Component\Search\Exporter\StringQueryExporter; use Rollerworks\Component\Search\Field\FieldConfig; +use Rollerworks\Component\Search\Field\OrderFieldType; use Rollerworks\Component\Search\Input\ProcessorConfig; use Rollerworks\Component\Search\Input\StringQueryInput; use Rollerworks\Component\Search\InputProcessor; use Rollerworks\Component\Search\SearchCondition; +use Rollerworks\Component\Search\SearchOrder; use Rollerworks\Component\Search\Test\SearchConditionExporterTestCase; use Rollerworks\Component\Search\Value\ValuesBag; use Rollerworks\Component\Search\Value\ValuesGroup; @@ -93,6 +95,35 @@ public function it_exporters_values(): void $this->assertConditionEquals($this->provideSingleValuePairTest(), $condition, $processor, $config); } + /** @test */ + public function it_exports_with_ordering_aliases(): void + { + $exporter = $this->getExporter(); + $fieldSet = $this->getFieldSet(false) + ->add('@id', OrderFieldType::class) + ->add('@status', OrderFieldType::class, ['view_label' => ['ASC' => 'down', 'DESC' => 'up']]) + ->getFieldSet() + ; + + $config = new ProcessorConfig($fieldSet); + + $condition = new SearchCondition($config->getFieldSet(), new ValuesGroup()); + + $orderGroup = (new ValuesGroup()) + ->addField('@id', (new ValuesBag())->addSimpleValue('DESC')) + ->addField('@status', (new ValuesBag())->addSimpleValue('ASC')) + ; + $condition->setOrder(new SearchOrder($orderGroup)); + + $this->assertExportEquals('@id: desc; @status: down;', $exporter->exportCondition($condition)); + + $processor = $this->getInputProcessor(); + $this->assertConditionEquals('@id: desc; @status: down;', $condition, $processor, $config); + + $processor = $this->getInputProcessor(); + $this->assertConditionEquals('@id: desc; @status: asc;', $condition, $processor, $config); + } + public function provideSingleValuePairTest() { return 'name: "value ", -value2, value2-, 10.00, "10,00", hÌ, ٤٤٤٦٥٤٦٠٠, "doctor""who""""", !value3; price: € 12.00, "12,00 $", $ 12.00;'; @@ -148,6 +179,11 @@ public function provideEmptyGroupTest() return '( );'; } + public function provideOrderTest() + { + return '@id: desc; @status: asc;'; + } + protected function getExporter(?callable $labelResolver = null): ConditionExporter { return new StringQueryExporter($labelResolver); diff --git a/lib/Core/Tests/Field/OrderFieldTest.php b/lib/Core/Tests/Field/OrderFieldTest.php index ca258427..01de7afc 100644 --- a/lib/Core/Tests/Field/OrderFieldTest.php +++ b/lib/Core/Tests/Field/OrderFieldTest.php @@ -136,13 +136,13 @@ public function it_allows_setting_a__view_transformer(): void } /** @test */ - public function it_has_no__norm_transformer_by_default(): void + public function it_has_no_norm_transformer_by_default(): void { self::assertNull($this->field->getNormTransformer()); } /** @test */ - public function it_allows_setting_a__norm_transformer(): void + public function it_allows_setting_a_norm_transformer(): void { $normTransformer = $this->getMockBuilder(DataTransformer::class)->getMock(); $this->field->setNormTransformer($normTransformer); diff --git a/lib/Core/Tests/Field/OrderFieldTypeTest.php b/lib/Core/Tests/Field/OrderFieldTypeTest.php new file mode 100644 index 00000000..28065bf5 --- /dev/null +++ b/lib/Core/Tests/Field/OrderFieldTypeTest.php @@ -0,0 +1,243 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Rollerworks\Component\Search\Tests\Field; + +use Rollerworks\Component\Search\Exception\TransformationFailedException; +use Rollerworks\Component\Search\Extension\Core\DataTransformer\OrderTransformer; +use Rollerworks\Component\Search\Field\OrderField; +use Rollerworks\Component\Search\Field\OrderFieldType; +use Rollerworks\Component\Search\Test\FieldTransformationAssertion; +use Rollerworks\Component\Search\Test\SearchIntegrationTestCase; + +/** + * @internal + */ +final class OrderFieldTypeTest extends SearchIntegrationTestCase +{ + /** @test */ + public function it_transforms_with_default_configuration(): void + { + /** @var OrderField $field */ + $field = $this->getFactory()->createField('@id', OrderFieldType::class); + + FieldTransformationAssertion::assertThat($field) + ->withInput('DESC') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('desc', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('desc') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('desc', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('ASC') + ->successfullyTransformsTo('ASC') + ->andReverseTransformsTo('asc', 'ASC') + ; + } + + /** @test */ + public function it_transforms_with_alias(): void + { + /** @var OrderField $field */ + $field = $this->getFactory()->createField('@id', OrderFieldType::class, ['alias' => [ + 'UP' => 'ASC', + 'DOWN' => 'DESC', + + 'OMHOOR' => 'ASC', + 'NEER' => 'DESC', + ]]); + + FieldTransformationAssertion::assertThat($field) + ->withInput('down') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('desc', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('NEER') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('desc', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('desc') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('desc', 'DESC') + ; + } + + /** @test */ + public function it_transforms_with_view_label(): void + { + /** @var OrderField $field */ + $field = $this->getFactory()->createField('@id', OrderFieldType::class, ['view_label' => [ + 'ASC' => 'up', + 'DESC' => 'down', + ]]); + + FieldTransformationAssertion::assertThat($field) + ->withInput('down') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('Up') + ->successfullyTransformsTo('ASC') + ->andReverseTransformsTo('up', 'ASC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('desc') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + } + + /** @test */ + public function it_transforms_with_alias_and_view_label(): void + { + /** @var OrderField $field */ + $field = $this->getFactory()->createField('@id', OrderFieldType::class, [ + 'view_label' => [ + 'ASC' => 'up', + 'DESC' => 'down', + ], + 'alias' => [ + 'UP' => 'ASC', + 'DOWN' => 'DESC', + 'OMHOOR' => 'ASC', + 'NEER' => 'DESC', + ], + ]); + + FieldTransformationAssertion::assertThat($field) + ->withInput('down') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('Up') + ->successfullyTransformsTo('ASC') + ->andReverseTransformsTo('up', 'ASC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('desc') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('down') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('NEER') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('desc') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + } + + /** @test */ + public function it_transforms_with_alias_and_view_label_and_lowercase(): void + { + /** @var OrderField $field */ + $field = $this->getFactory()->createField('@id', OrderFieldType::class, [ + 'view_label' => [ + 'asc' => 'up', + 'desc' => 'down', + ], + 'alias' => [ + 'up' => 'ASC', + 'down' => 'DESC', + 'omhoor' => 'ASC', + 'neer' => 'DESC', + ], + 'case' => OrderTransformer::CASE_LOWERCASE, + ]); + + FieldTransformationAssertion::assertThat($field) + ->withInput('down') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('Up') + ->successfullyTransformsTo('ASC') + ->andReverseTransformsTo('up', 'ASC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('desc') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('down') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC') + ; + + FieldTransformationAssertion::assertThat($field) + ->withInput('NEER') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC'); + + FieldTransformationAssertion::assertThat($field) + ->withInput('desc') + ->successfullyTransformsTo('DESC') + ->andReverseTransformsTo('down', 'DESC'); + } + + /** @test */ + public function it_fails_to_transform_with_invalid_direction(): void + { + /** @var OrderField $field */ + $field = $this->getFactory()->createField('@id', OrderFieldType::class, [ + 'view_label' => [ + 'asc' => 'up', + 'desc' => 'down', + ], + ]); + + FieldTransformationAssertion::assertThat($field) + ->withInput('neer') + ->failsToTransforms( + new TransformationFailedException( + 'Invalid sort direction "NEER" specified, expected one of: "ASC", "DESC", "UP", "DOWN"', + 0, + null, + 'This value is not a valid sorting direction. Accepted directions are "{{ directions }}".', + ['{{ directions }}' => 'asc", "desc", "up", "down'] + ) + ) + ; + } +} diff --git a/lib/Core/Tests/Input/InputProcessorTestCase.php b/lib/Core/Tests/Input/InputProcessorTestCase.php index b0a91c8d..90e5f8bf 100644 --- a/lib/Core/Tests/Input/InputProcessorTestCase.php +++ b/lib/Core/Tests/Input/InputProcessorTestCase.php @@ -60,8 +60,8 @@ protected function getFieldSet(bool $build = true, bool $order = false) $fieldSet->add('date', DateType::class, ['pattern' => 'MM-dd-yyyy']); if ($order) { - $fieldSet->add('@date', OrderFieldType::class, ['case' => OrderTransformer::CASE_LOWERCASE, 'alias' => ['up' => 'ASC', 'down' => 'DESC'], 'default' => 'down']); - $fieldSet->add('@id', OrderFieldType::class, ['default' => 'up']); + $fieldSet->add('@date', OrderFieldType::class, ['case' => OrderTransformer::CASE_LOWERCASE, 'alias' => ['up' => 'ASC', 'down' => 'DESC'], 'default' => 'down']); + $fieldSet->add('@id', OrderFieldType::class, ['default' => 'ASC']); } $fieldSet->set( $this->getFactory()->createField('no-range-field', IntegerType::class) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 37e0ba03..8408a772 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -10,11 +10,6 @@ parameters: count: 1 path: lib/Core/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php - - - message: "#^Property Rollerworks\\\\Component\\\\Search\\\\Extension\\\\Core\\\\DataTransformer\\\\OrderTransformer\\:\\:\\$default is never read, only written\\.$#" - count: 1 - path: lib/Core/Extension/Core/DataTransformer/OrderTransformer.php - - message: "#^Property Rollerworks\\\\Component\\\\Search\\\\Field\\\\OrderField\\:\\:\\$valueComparator is never written, only read\\.$#" count: 1