├── LICENSE ├── README.md ├── composer.json ├── lib ├── ApiPlatform │ ├── ArrayKeysValidator.php │ ├── Doctrine │ │ └── Orm │ │ │ └── Extension │ │ │ └── SearchExtension.php │ ├── Elasticsearch │ │ └── Extension │ │ │ └── SearchExtension.php │ ├── EventListener │ │ ├── InvalidSearchConditionExceptionListener.php │ │ └── SearchConditionListener.php │ ├── LICENSE │ ├── Metadata-reference.md │ ├── Metadata │ │ └── DefaultConfigurationMetadataFactory.php │ ├── README.md │ ├── SearchConditionEvent.php │ ├── Serializer │ │ └── InvalidSearchConditionNormalizer.php │ └── composer.json ├── Core │ ├── AbstractExtension.php │ ├── ConditionErrorMessage.php │ ├── ConditionExporter.php │ ├── DataTransformer.php │ ├── ErrorList.php │ ├── Exception │ │ ├── BadMethodCallException.php │ │ ├── GroupsNestingException.php │ │ ├── GroupsOverflowException.php │ │ ├── InputProcessorException.php │ │ ├── InvalidArgumentException.php │ │ ├── InvalidConfigurationException.php │ │ ├── InvalidSearchConditionException.php │ │ ├── OrderStructureException.php │ │ ├── SearchException.php │ │ ├── StringLexerException.php │ │ ├── TransformationFailedException.php │ │ ├── TranslatedArgument.php │ │ ├── UnexpectedTypeException.php │ │ ├── UnknownFieldException.php │ │ ├── UnsupportedFieldSetException.php │ │ ├── UnsupportedValueTypeException.php │ │ └── ValuesOverflowException.php │ ├── Exporter │ │ ├── AbstractExporter.php │ │ ├── JsonExporter.php │ │ ├── NormStringQueryExporter.php │ │ ├── StringExporter.php │ │ └── StringQueryExporter.php │ ├── Extension │ │ ├── Core │ │ │ ├── ChoiceList │ │ │ │ ├── ArrayChoiceList.php │ │ │ │ ├── ChoiceList.php │ │ │ │ ├── ChoiceLoaderTrait.php │ │ │ │ ├── Factory │ │ │ │ │ ├── CachingFactoryDecorator.php │ │ │ │ │ ├── ChoiceListFactory.php │ │ │ │ │ ├── DefaultChoiceListFactory.php │ │ │ │ │ └── PropertyAccessDecorator.php │ │ │ │ ├── LazyChoiceList.php │ │ │ │ ├── Loader │ │ │ │ │ ├── CallbackChoiceLoader.php │ │ │ │ │ └── ChoiceLoader.php │ │ │ │ └── View │ │ │ │ │ ├── ChoiceGroupView.php │ │ │ │ │ ├── ChoiceListView.php │ │ │ │ │ └── ChoiceView.php │ │ │ ├── DataTransformer │ │ │ │ ├── BaseDateTimeTransformer.php │ │ │ │ ├── BirthdayTransformer.php │ │ │ │ ├── ChoiceToLabelTransformer.php │ │ │ │ ├── ChoiceToValueTransformer.php │ │ │ │ ├── DateIntervalTransformer.php │ │ │ │ ├── DateTimeToLocalizedStringTransformer.php │ │ │ │ ├── DateTimeToRfc3339Transformer.php │ │ │ │ ├── DateTimeToStringTransformer.php │ │ │ │ ├── DateTimeToTimestampTransformer.php │ │ │ │ ├── IntegerToLocalizedStringTransformer.php │ │ │ │ ├── IntegerToStringTransformer.php │ │ │ │ ├── LocalizedBirthdayTransformer.php │ │ │ │ ├── MoneyToLocalizedStringTransformer.php │ │ │ │ ├── MoneyToStringTransformer.php │ │ │ │ ├── MultiTypeDataTransformer.php │ │ │ │ ├── NumberToLocalizedStringTransformer.php │ │ │ │ ├── NumberToStringTransformer.php │ │ │ │ ├── OrderToLocalizedTransformer.php │ │ │ │ └── OrderTransformer.php │ │ │ ├── Model │ │ │ │ └── MoneyValue.php │ │ │ ├── Type │ │ │ │ ├── BaseDateTimeType.php │ │ │ │ ├── BirthdayType.php │ │ │ │ ├── ChoiceType.php │ │ │ │ ├── CountryType.php │ │ │ │ ├── CurrencyType.php │ │ │ │ ├── DateTimeType.php │ │ │ │ ├── DateType.php │ │ │ │ ├── IntegerType.php │ │ │ │ ├── LanguageType.php │ │ │ │ ├── LocaleType.php │ │ │ │ ├── MoneyType.php │ │ │ │ ├── NumberType.php │ │ │ │ ├── SearchFieldType.php │ │ │ │ ├── TextType.php │ │ │ │ ├── TimeType.php │ │ │ │ ├── TimestampType.php │ │ │ │ └── TimezoneType.php │ │ │ └── ValueComparator │ │ │ │ ├── BirthdayValueComparator.php │ │ │ │ ├── DateTimeIntervalValueComparator.php │ │ │ │ ├── DateTimeValueComparator.php │ │ │ │ ├── DateValueComparator.php │ │ │ │ ├── MoneyValueComparator.php │ │ │ │ ├── NumberValueComparator.php │ │ │ │ └── SimpleValueComparator.php │ │ └── LazyExtension.php │ ├── Field │ │ ├── AbstractFieldType.php │ │ ├── AbstractFieldTypeExtension.php │ │ ├── FieldConfig.php │ │ ├── FieldType.php │ │ ├── FieldTypeExtension.php │ │ ├── GenericResolvedFieldType.php │ │ ├── GenericResolvedFieldTypeFactory.php │ │ ├── GenericTypeRegistry.php │ │ ├── OrderField.php │ │ ├── OrderFieldType.php │ │ ├── ResolvedFieldType.php │ │ ├── ResolvedFieldTypeFactory.php │ │ ├── SearchField.php │ │ ├── SearchFieldView.php │ │ └── TypeRegistry.php │ ├── FieldSet.php │ ├── FieldSetBuilder.php │ ├── FieldSetConfigurator.php │ ├── FieldSetRegistry.php │ ├── FieldSetView.php │ ├── FieldSetWithView.php │ ├── GenericFieldSet.php │ ├── GenericFieldSetBuilder.php │ ├── GenericSearchFactory.php │ ├── Input │ │ ├── AbstractInput.php │ │ ├── CachingInputProcessor.php │ │ ├── ConditionStructureBuilder.php │ │ ├── ConditionStructureByViewBuilder.php │ │ ├── ErrorPathTranslator.php │ │ ├── JsonInput.php │ │ ├── NormStringQueryInput.php │ │ ├── NullValidator.php │ │ ├── OrderStructureBuilder.php │ │ ├── ProcessorConfig.php │ │ ├── StringInput.php │ │ ├── StringLexer.php │ │ ├── StringQueryInput.php │ │ └── Validator.php │ ├── InputProcessor.php │ ├── LICENSE │ ├── LazyFieldSetRegistry.php │ ├── Loader │ │ ├── ClosureContainer.php │ │ ├── ConditionExporterLoader.php │ │ ├── InputProcessorLoader.php │ │ └── ServiceNotFoundException.php │ ├── ParameterBag.php │ ├── PreloadedExtension.php │ ├── README.md │ ├── Resources │ │ └── translations │ │ │ ├── RollerworksSearch.en.xlf │ │ │ ├── RollerworksSearch.nl.xlf │ │ │ ├── validators.en.xlf │ │ │ └── validators.nl.xlf │ ├── SearchCondition.php │ ├── SearchConditionBuilder.php │ ├── SearchConditionSerializer.php │ ├── SearchExtension.php │ ├── SearchFactory.php │ ├── SearchFactoryBuilder.php │ ├── SearchOrder.php │ ├── SearchPrimaryCondition.php │ ├── Searches.php │ ├── StructureBuilder.php │ ├── Test │ │ ├── CarbonIntervalComparator.php │ │ ├── FieldTransformationAssertion.php │ │ ├── SearchConditionExporterTestCase.php │ │ └── SearchIntegrationTestCase.php │ ├── Util │ │ └── StringUtil.php │ ├── Value │ │ ├── Compare.php │ │ ├── ExcludedRange.php │ │ ├── PatternMatch.php │ │ ├── Range.php │ │ ├── RequiresComparatorValueHolder.php │ │ ├── ValueHolder.php │ │ ├── ValuesBag.php │ │ └── ValuesGroup.php │ ├── ValueComparator.php │ ├── ValuesBagBuilder.php │ └── composer.json ├── Doctrine │ ├── Dbal │ │ ├── AbstractCachedConditionGenerator.php │ │ ├── CachedConditionGenerator.php │ │ ├── ColumnConversion.php │ │ ├── ConditionGenerator.php │ │ ├── ConversionHints.php │ │ ├── DoctrineDbalFactory.php │ │ ├── Extension │ │ │ ├── Conversion │ │ │ │ ├── AgeDateConversion.php │ │ │ │ ├── ChildCountConversion.php │ │ │ │ ├── DateIntervalConversion.php │ │ │ │ └── MoneyValueConversion.php │ │ │ ├── DoctrineDbalExtension.php │ │ │ └── Type │ │ │ │ ├── BirthdayTypeExtension.php │ │ │ │ ├── ChildCountType.php │ │ │ │ ├── DateTimeTypeExtension.php │ │ │ │ ├── FieldTypeExtension.php │ │ │ │ └── MoneyTypeExtension.php │ │ ├── FieldConfigurationSet.php │ │ ├── LICENSE │ │ ├── Query │ │ │ ├── QueryField.php │ │ │ └── QueryGenerator.php │ │ ├── QueryPlatform │ │ │ ├── AbstractQueryPlatform.php │ │ │ ├── MssqlQueryPlatform.php │ │ │ └── SqlQueryPlatform.php │ │ ├── README.md │ │ ├── SqlConditionGenerator.php │ │ ├── Test │ │ │ └── QueryBuilderAssertion.php │ │ ├── UPGRADE.md │ │ ├── ValueConversion.php │ │ └── composer.json │ └── Orm │ │ ├── CachedDqlConditionGenerator.php │ │ ├── ColumnConversion.php │ │ ├── ConditionGenerator.php │ │ ├── DoctrineOrmFactory.php │ │ ├── DqlConditionGenerator.php │ │ ├── Extension │ │ ├── Conversion │ │ │ ├── AgeDateConversion.php │ │ │ ├── ChildCountConversion.php │ │ │ ├── DateIntervalConversion.php │ │ │ └── MoneyValueConversion.php │ │ ├── DoctrineOrmExtension.php │ │ ├── Functions │ │ │ ├── AgeFunction.php │ │ │ ├── CastFunction.php │ │ │ ├── CastIntervalFunction.php │ │ │ ├── CountChildrenFunction.php │ │ │ ├── MoneyCastFunction.php │ │ │ └── PlatformSpecificFunction.php │ │ └── Type │ │ │ ├── BirthdayTypeExtension.php │ │ │ ├── ChildCountType.php │ │ │ ├── DateTimeTypeExtension.php │ │ │ ├── FieldTypeExtension.php │ │ │ └── MoneyTypeExtension.php │ │ ├── FieldConfigBuilder.php │ │ ├── LICENSE │ │ ├── OrmQueryField.php │ │ ├── QueryBuilderConditionGenerator.php │ │ ├── QueryPlatform │ │ └── DqlQueryPlatform.php │ │ ├── README.md │ │ ├── UPGRADE.md │ │ ├── ValueConversion.php │ │ ├── composer.json │ │ └── mysql.travis.xml ├── Elasticsearch │ ├── CachedConditionGenerator.php │ ├── ChildOrderConversion.php │ ├── ConditionGenerator.php │ ├── ElasticsearchFactory.php │ ├── Extension │ │ ├── Conversion │ │ │ ├── CurrencyConversion.php │ │ │ ├── DateChildOrderConversion.php │ │ │ ├── DateConversion.php │ │ │ └── DateTimeConversion.php │ │ ├── ElasticsearchExtension.php │ │ └── Type │ │ │ ├── BirthdayTypeExtension.php │ │ │ ├── DateTimeTypeExtension.php │ │ │ ├── DateTypeExtension.php │ │ │ ├── FieldTypeExtension.php │ │ │ ├── MoneyTypeExtension.php │ │ │ └── OrderTypeExtension.php │ ├── FieldMapping.php │ ├── LICENSE │ ├── QueryConditionGenerator.php │ ├── QueryConversion.php │ ├── QueryPreparationHints.php │ ├── README.md │ ├── ValueConversion.php │ └── composer.json └── Symfony │ ├── SearchBundle │ ├── DependencyInjection │ │ ├── Compiler │ │ │ ├── DoctrineOrmPass.php │ │ │ ├── ElasticaClientPass.php │ │ │ ├── ExporterPass.php │ │ │ ├── ExtensionPass.php │ │ │ ├── FieldSetRegistryPass.php │ │ │ └── InputProcessorPass.php │ │ ├── Configuration.php │ │ └── RollerworksSearchExtension.php │ ├── LICENSE │ ├── README.md │ ├── Resources │ │ └── config │ │ │ ├── api_platform.xml │ │ │ ├── api_platform_doctrine_orm.xml │ │ │ ├── api_platform_elasticsearch.xml │ │ │ ├── condition_exporter.xml │ │ │ ├── doctrine_dbal.xml │ │ │ ├── doctrine_orm.xml │ │ │ ├── elasticsearch.xml │ │ │ ├── input_processor.xml │ │ │ ├── input_validator.xml │ │ │ ├── services.xml │ │ │ └── translator_alias_resolver.xml │ ├── RollerworksSearchBundle.php │ ├── TranslatorBasedAliasResolver.php │ ├── Type │ │ ├── TranslatableFieldTypeExtension.php │ │ └── TranslatableOrderFieldTypeExtension.php │ ├── UPGRADE.md │ └── composer.json │ └── Validator │ ├── InputValidator.php │ ├── LICENSE │ ├── README.md │ ├── Type │ └── FieldTypeValidatorExtension.php │ ├── ValidatorExtension.php │ └── composer.json └── phpunit ├── mysql.xml └── pgsql.xml /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/ApiPlatform/ArrayKeysValidator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\ApiPlatform; 15 | 16 | use ApiPlatform\Exception\RuntimeException; 17 | 18 | final class ArrayKeysValidator 19 | { 20 | public static function assertKeysExists(array $input, array $required, string $path): void 21 | { 22 | if ([] !== $missing = array_diff_key(array_flip($required), $input)) { 23 | throw new RuntimeException( 24 | \sprintf( 25 | 'Config "%s" is missing "%s", got "%s".', 26 | $path, 27 | implode('", "', array_flip($missing)), 28 | implode('", "', array_keys($input)) 29 | ) 30 | ); 31 | } 32 | } 33 | 34 | public static function assertOnlyKeys(array $input, array $accepted, string $path): void 35 | { 36 | if (array_diff(array_keys($input), $accepted) !== []) { 37 | throw new RuntimeException( 38 | \sprintf( 39 | 'Config "%s" accepts only "%s", got "%s".', 40 | $path, 41 | implode('", "', $accepted), 42 | implode('", "', array_keys($input)) 43 | ) 44 | ); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/ApiPlatform/EventListener/InvalidSearchConditionExceptionListener.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\ApiPlatform\EventListener; 15 | 16 | use ApiPlatform\Util\ErrorFormatGuesser; 17 | use Rollerworks\Component\Search\Exception\InvalidSearchConditionException; 18 | use Symfony\Component\HttpFoundation\Response; 19 | use Symfony\Component\HttpKernel\Event\ExceptionEvent; 20 | use Symfony\Component\Serializer\SerializerInterface; 21 | 22 | /** 23 | * @deprecated use the API-Platform error handler instead 24 | */ 25 | final class InvalidSearchConditionExceptionListener 26 | { 27 | private $serializer; 28 | private $errorFormats; 29 | 30 | public function __construct(SerializerInterface $serializer, array $errorFormats) 31 | { 32 | $this->serializer = $serializer; 33 | $this->errorFormats = $errorFormats; 34 | } 35 | 36 | /** 37 | * Returns a list of errors normalized in the Hydra format. 38 | */ 39 | public function onKernelException(ExceptionEvent $event): void 40 | { 41 | $exception = $event->getThrowable(); 42 | 43 | if (! $exception instanceof InvalidSearchConditionException) { 44 | return; 45 | } 46 | 47 | $format = ErrorFormatGuesser::guessErrorFormat($event->getRequest(), $this->errorFormats); 48 | 49 | $event->setResponse( 50 | new Response( 51 | $this->serializer->serialize($exception, $format['key']), 52 | Response::HTTP_BAD_REQUEST, 53 | [ 54 | 'Content-Type' => \sprintf('%s; charset=utf-8', $format['value'][0]), 55 | 'X-Content-Type-Options' => 'nosniff', 56 | 'X-Frame-Options' => 'deny', 57 | ] 58 | ) 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/ApiPlatform/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /lib/ApiPlatform/Metadata-reference.md: -------------------------------------------------------------------------------- 1 | Class: 2 | 3 | ```php 4 | "rollerworks_search" = { 5 | "contexts" = { 6 | "_defaults" { 7 | # Set defaults, merged with more specific configuration (and _any) 8 | }, 9 | "ContextName | _any" = { # ContextName is provided using event listeners (request#attributes[_search_context]) 10 | "fieldset" = "...", # Required 11 | "processor" = { 12 | "cache_ttl" = "60" # time in seconds 13 | }, # Options for the processor 14 | "doctrine_orm" = { 15 | "relations" = { 16 | "alias" = { "type" = "(left | right | inner)", "entity" "join", "conditionType" = null, "condition" = null, "indexBy" = null } 17 | }, 18 | "mappings" = { 19 | "mapping-name" = { "property" = "...", "alias" = "...", "db_type" = null } 20 | }, 21 | }, 22 | } 23 | } 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /lib/ApiPlatform/README.md: -------------------------------------------------------------------------------- 1 | RollerworksSearch API-Platform Bridge 2 | ===================================== 3 | 4 | [API-Platform](https://github.com/api-platform) integration bridge for [RollerworksSearch][1]. 5 | 6 | If you'd like to contribute to this project, please see the [contributing guide lines][2] 7 | for more information. 8 | 9 | Installation 10 | ------------ 11 | 12 | To install this extension, add the `search-api-platform` to your composer.json 13 | 14 | ```bash 15 | $ php composer.phar require rollerworks/search-api-platform 16 | ``` 17 | 18 | License 19 | ------- 20 | 21 | The source of this package is subject to the MIT license that is bundled 22 | with this source code in the file [LICENSE](LICENSE). 23 | 24 | [1]: https://github.com/rollerworks/RollerworksSearch 25 | [2]: https://github.com/rollerworks/RollerworksSearch#contributing 26 | -------------------------------------------------------------------------------- /lib/ApiPlatform/SearchConditionEvent.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\ApiPlatform; 15 | 16 | use Rollerworks\Component\Search\SearchCondition; 17 | use Symfony\Component\HttpFoundation\Request; 18 | use Symfony\Contracts\EventDispatcher\Event; 19 | 20 | /** 21 | * The SearchConditionEvent allows to set a primary-condition. 22 | * 23 | * Call getSearchCondition()->setPrimaryCondition() to set a primary-condition. 24 | * 25 | * @author Sebastiaan Stok 26 | */ 27 | final class SearchConditionEvent extends Event 28 | { 29 | /** 30 | * @Event 31 | */ 32 | public const SEARCH_CONDITION_EVENT = 'rollerworks_search.process.primary_condition'; 33 | 34 | private $searchCondition; 35 | private $resourceClass; 36 | private $request; 37 | 38 | public function __construct(?SearchCondition $searchCondition, string $resourceClass, Request $request) 39 | { 40 | $this->searchCondition = $searchCondition; 41 | $this->resourceClass = $resourceClass; 42 | $this->request = $request; 43 | } 44 | 45 | public function getSearchCondition(): ?SearchCondition 46 | { 47 | return $this->searchCondition; 48 | } 49 | 50 | public function getResourceClass(): string 51 | { 52 | return $this->resourceClass; 53 | } 54 | 55 | public function getRequest(): Request 56 | { 57 | return $this->request; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/ApiPlatform/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollerworks/search-api-platform", 3 | "description": "API-Platform integration bridge for RollerworksSearch", 4 | "license": "MIT", 5 | "type": "library", 6 | "authors": [ 7 | { 8 | "name": "Sebastiaan Stok", 9 | "email": "s.stok@rollerscapes.net" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=8.1", 14 | "api-platform/core": "^2.0.10 || ^3.2", 15 | "rollerworks/search": ">=2.0.0-BETA2 ^2.0@dev", 16 | "rollerworks/uri-encoder": "^1.1.0 || ^2.0", 17 | "symfony/http-foundation": "^6.4 || ^7.0" 18 | }, 19 | "require-dev": { 20 | "doctrine/orm": "^2.5.6", 21 | "phpunit/phpunit": "^6.3", 22 | "rollerworks/search-doctrine-orm": ">=2.0.0-BETA2 ^2.0@dev", 23 | "symfony/phpunit-bridge": "^6.0" 24 | }, 25 | "minimum-stability": "dev", 26 | "prefer-stable": true, 27 | "autoload": { 28 | "psr-4": { 29 | "Rollerworks\\Component\\Search\\ApiPlatform\\": "" 30 | }, 31 | "exclude-from-classmap": [ 32 | "Tests/" 33 | ] 34 | }, 35 | "autoload-dev": { 36 | "psr-4": { 37 | "Rollerworks\\Component\\Search\\ApiPlatform\\Tests\\": "Tests/" 38 | } 39 | }, 40 | "extra": { 41 | "branch-alias": { 42 | "dev-main": "2.0-dev" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Core/ConditionExporter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | /** 17 | * ConditionExporter defines the interface for SearchCondition exporters. 18 | * 19 | * @author Sebastiaan Stok 20 | */ 21 | interface ConditionExporter 22 | { 23 | /** 24 | * Exports the SearchCondition to a portable format. 25 | * 26 | * The returned format can be anything, as long as it's possible 27 | * to 're-import' the exported search condition with a compatible 28 | * input processor. 29 | */ 30 | public function exportCondition(SearchCondition $condition); 31 | } 32 | -------------------------------------------------------------------------------- /lib/Core/ErrorList.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class ErrorList extends \ArrayObject 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Exception/BadMethodCallException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class BadMethodCallException extends \BadMethodCallException implements SearchException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Exception/GroupsNestingException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class GroupsNestingException extends InputProcessorException 20 | { 21 | public function __construct(int $max, string $path) 22 | { 23 | parent::__construct($path, 'This group exceeds maximum nesting level of {{ max }}.', ['{{ max }}' => $max]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Core/Exception/GroupsOverflowException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class GroupsOverflowException extends InputProcessorException 20 | { 21 | public function __construct(int $max, string $path) 22 | { 23 | parent::__construct($path, 'This group exceeds maximum number of groups {{ max }}.', ['{{ max }}' => $max]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Core/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | class InvalidArgumentException extends \InvalidArgumentException implements SearchException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Exception/InvalidConfigurationException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | class InvalidConfigurationException extends InvalidArgumentException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Exception/InvalidSearchConditionException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | use Rollerworks\Component\Search\ConditionErrorMessage; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | final class InvalidSearchConditionException extends \InvalidArgumentException implements SearchException 22 | { 23 | private $errors; 24 | 25 | public function __construct(array $errors) 26 | { 27 | parent::__construct('The search-condition contains one or more errors.'); 28 | 29 | $this->errors = $errors; 30 | } 31 | 32 | /** 33 | * @return ConditionErrorMessage[] 34 | */ 35 | public function getErrors(): array 36 | { 37 | return $this->errors; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Core/Exception/OrderStructureException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | final class OrderStructureException extends InputProcessorException 17 | { 18 | public static function invalidValue(string $fieldName): self 19 | { 20 | return new self('', 'The field {{ field }} only accepts a single simple value.', ['{{ field }}' => $fieldName]); 21 | } 22 | 23 | public static function noGrouping(): self 24 | { 25 | return new self('', 'Order clauses cannot be placed in a group.'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Core/Exception/SearchException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | interface SearchException 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Exception/TransformationFailedException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | final class TransformationFailedException extends \RuntimeException implements SearchException 17 | { 18 | private $invalidMessage; 19 | private $invalidMessageParameters; 20 | private mixed $value; 21 | 22 | public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null, ?string $invalidMessage = null, array $invalidMessageParameters = [], mixed $value = null) 23 | { 24 | parent::__construct($message, $code, $previous); 25 | 26 | $this->setInvalidMessage($invalidMessage, $invalidMessageParameters); 27 | $this->value = $value; 28 | } 29 | 30 | /** 31 | * Sets the message that will be shown to the user. 32 | * 33 | * @param string|null $invalidMessage The message or message key 34 | * @param array $invalidMessageParameters Data to be passed into the translator 35 | */ 36 | public function setInvalidMessage(?string $invalidMessage = null, array $invalidMessageParameters = []): void 37 | { 38 | $this->invalidMessage = $invalidMessage; 39 | $this->invalidMessageParameters = $invalidMessageParameters; 40 | } 41 | 42 | public function getInvalidMessage(): ?string 43 | { 44 | return $this->invalidMessage; 45 | } 46 | 47 | public function getInvalidMessageParameters(): array 48 | { 49 | return $this->invalidMessageParameters; 50 | } 51 | 52 | public function getInvalidValue(): mixed 53 | { 54 | return $this->value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/Core/Exception/TranslatedArgument.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | use Symfony\Contracts\Translation\TranslatableInterface; 17 | use Symfony\Contracts\Translation\TranslatorInterface; 18 | 19 | final class TranslatedArgument implements TranslatableInterface 20 | { 21 | private string $message; 22 | private array $parameters; 23 | private ?string $domain; 24 | 25 | public function __construct(string $message, array $parameters = [], ?string $domain = null) 26 | { 27 | $this->message = $message; 28 | $this->parameters = $parameters; 29 | $this->domain = $domain; 30 | } 31 | 32 | public function __toString(): string 33 | { 34 | return $this->getMessage(); 35 | } 36 | 37 | public function getMessage(): string 38 | { 39 | return $this->message; 40 | } 41 | 42 | public function getParameters(): array 43 | { 44 | return $this->parameters; 45 | } 46 | 47 | public function getDomain(): ?string 48 | { 49 | return $this->domain; 50 | } 51 | 52 | public function trans(TranslatorInterface $translator, ?string $locale = null): string 53 | { 54 | return $translator->trans($this->getMessage(), array_map( 55 | static fn ($parameter) => $parameter instanceof TranslatableInterface ? $parameter->trans($translator, $locale) : $parameter, 56 | $this->getParameters() 57 | ), $this->getDomain(), $locale); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/Core/Exception/UnexpectedTypeException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | class UnexpectedTypeException extends \InvalidArgumentException implements SearchException 20 | { 21 | /** 22 | * @param array|string $expectedType 23 | */ 24 | public function __construct($value, $expectedType) 25 | { 26 | if (\is_array($expectedType)) { 27 | $expectedType = implode('", "', $expectedType); 28 | } 29 | 30 | parent::__construct( 31 | \sprintf( 32 | 'Expected argument of type "%s", "%s" given', 33 | $expectedType, 34 | \is_object($value) ? $value::class : \gettype($value) 35 | ) 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/Core/Exception/UnknownFieldException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class UnknownFieldException extends InputProcessorException 20 | { 21 | public function __construct(string $fieldName) 22 | { 23 | parent::__construct( 24 | '', 25 | 'The field {{ field }} is not registered in the FieldSet or available as alias.', 26 | ['{{ field }}' => $fieldName] 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Core/Exception/UnsupportedFieldSetException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class UnsupportedFieldSetException extends InvalidArgumentException 20 | { 21 | public function __construct(array $expected, string $provided) 22 | { 23 | parent::__construct( 24 | \sprintf('FieldSet "%s" was not expected, expected one of "%s"', $provided, implode('", "', $expected)) 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Core/Exception/UnsupportedValueTypeException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * UnsupportedValueTypeException. 18 | * 19 | * Throw this exception when the value-type is not supported for the field. 20 | */ 21 | final class UnsupportedValueTypeException extends InputProcessorException 22 | { 23 | public function __construct(string $fieldName, string $valueType) 24 | { 25 | parent::__construct( 26 | '', 27 | 'The field {{ field }} does not accept {{ type }} values.', 28 | [ 29 | '{{ field }}' => $fieldName, 30 | '{{ type }}' => str_contains($valueType, '\\') ? mb_substr($valueType, mb_strrpos($valueType, '\\') + 1) : $valueType, 31 | ] 32 | ); 33 | 34 | $this->setTranslatedParameters(['{{ type }}']); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Core/Exception/ValuesOverflowException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exception; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class ValuesOverflowException extends InputProcessorException 20 | { 21 | public function __construct(string $fieldName, int $max, string $path) 22 | { 23 | parent::__construct( 24 | $path, 25 | 'This value exceeds the maximum number of values. Maximum values are {{ max }}.', 26 | [ 27 | '{{ field }}' => $fieldName, 28 | '{{ max }}' => $max, 29 | ], 30 | $max 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/Core/Exporter/NormStringQueryExporter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exporter; 15 | 16 | use Rollerworks\Component\Search\Field\FieldConfig; 17 | use Rollerworks\Component\Search\FieldSet; 18 | use Rollerworks\Component\Search\Input\NormStringQueryInput; 19 | 20 | /** 21 | * Exports the SearchCondition as StringQuery string. 22 | * 23 | * @author Sebastiaan Stok 24 | */ 25 | final class NormStringQueryExporter extends StringExporter 26 | { 27 | protected function modelToExported($value, FieldConfig $field, string $allowedNext = ',;)'): string 28 | { 29 | $valueExporter = $field->getOption(NormStringQueryInput::VALUE_EXPORTER_OPTION_NAME); 30 | 31 | if ($valueExporter === true) { 32 | return $this->modelToNorm($value, $field); 33 | } 34 | 35 | if (\is_callable($valueExporter)) { 36 | return $valueExporter($value, [$this, 'modelToNorm'], $field); 37 | } 38 | 39 | return $this->exportValueAsString($this->modelToNorm($value, $field)); 40 | } 41 | 42 | protected function resolveLabels(FieldSet $fieldSet): array 43 | { 44 | $labels = []; 45 | 46 | foreach ($fieldSet->all() as $name => $field) { 47 | $labels[$name] = $name; 48 | } 49 | 50 | return $labels; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/Core/Exporter/StringQueryExporter.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Exporter; 15 | 16 | use Rollerworks\Component\Search\Field\FieldConfig; 17 | use Rollerworks\Component\Search\FieldSet; 18 | 19 | /** 20 | * Exports the SearchCondition as StringQuery string. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | final class StringQueryExporter extends StringExporter 25 | { 26 | private $labelResolver; 27 | 28 | /** 29 | * @param callable|null $labelResolver a callable to resolve the actual label 30 | * of the field, receives a FieldConfig instance. 31 | * If the resolver is null, the `label` option value 32 | * of the field is tried instead 33 | */ 34 | public function __construct(?callable $labelResolver = null) 35 | { 36 | $this->labelResolver = $labelResolver ?? static fn (FieldConfig $field) => $field->getOption('label') ?? $field->getName(); 37 | } 38 | 39 | protected function resolveLabels(FieldSet $fieldSet): array 40 | { 41 | $labels = []; 42 | $callable = $this->labelResolver; 43 | 44 | foreach ($fieldSet->all() as $name => $field) { 45 | $labels[$name] = $callable($field); 46 | } 47 | 48 | return $labels; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ChoiceList/ChoiceLoaderTrait.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ChoiceList; 15 | 16 | /** 17 | * The ChoiceLoaderTrait can used for optimizing empty 18 | * choices/values in ChoiceLoaders. 19 | * 20 | * @author Sebastiaan Stok 21 | * @author Bernhard Schussek 22 | */ 23 | trait ChoiceLoaderTrait 24 | { 25 | /** 26 | * @var ArrayChoiceList|null 27 | */ 28 | protected $choiceList; 29 | 30 | public function loadChoicesForValues(array $values, ?callable $value = null): array 31 | { 32 | // Optimize 33 | if (empty($values)) { 34 | return []; 35 | } 36 | 37 | // If no callable is set, values are the same as choices 38 | if ($value === null) { 39 | return $values; 40 | } 41 | 42 | return $this->loadChoiceList($value)->getChoicesForValues($values); 43 | } 44 | 45 | public function loadValuesForChoices(array $choices, ?callable $value = null): array 46 | { 47 | // Optimize 48 | if (empty($choices)) { 49 | return []; 50 | } 51 | 52 | // If no callable is set, choices are the same as values 53 | if ($value === null) { 54 | return $choices; 55 | } 56 | 57 | return $this->loadChoiceList($value)->getValuesForChoices($choices); 58 | } 59 | 60 | /** 61 | * Loads a list of choices. 62 | * 63 | * @see \Rollerworks\Component\Search\Extension\Core\ChoiceList\Loader\ChoiceLoader::loadChoiceList 64 | */ 65 | abstract public function loadChoiceList(?callable $value = null): ChoiceList; 66 | } 67 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ChoiceList/Loader/CallbackChoiceLoader.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ChoiceList\Loader; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ArrayChoiceList; 17 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceList; 18 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceLoaderTrait; 19 | 20 | /** 21 | * Loads an {@link ArrayChoiceList} instance from a callable returning an array of choices. 22 | * 23 | * @author Jules Pietri 24 | */ 25 | final class CallbackChoiceLoader implements ChoiceLoader 26 | { 27 | use ChoiceLoaderTrait; 28 | 29 | /** 30 | * @var callable 31 | */ 32 | private $callback; 33 | 34 | /** 35 | * @var bool 36 | */ 37 | private $valuesAreConstant; 38 | 39 | /** 40 | * @param callable $callback The callable returning an array of choices 41 | * @param bool $valuesAreConstant Indicate whether values are constant 42 | * (not dependent of there position) 43 | */ 44 | public function __construct(callable $callback, bool $valuesAreConstant = false) 45 | { 46 | $this->callback = $callback; 47 | $this->valuesAreConstant = $valuesAreConstant; 48 | } 49 | 50 | public function loadChoiceList(?callable $value = null): ChoiceList 51 | { 52 | if ($this->choiceList !== null) { 53 | return $this->choiceList; 54 | } 55 | 56 | return $this->choiceList = new ArrayChoiceList(\call_user_func($this->callback), $value); 57 | } 58 | 59 | public function isValuesConstant(): bool 60 | { 61 | return $this->valuesAreConstant; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ChoiceList/View/ChoiceGroupView.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ChoiceList\View; 15 | 16 | /** 17 | * Represents a group of choices in templates. 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | class ChoiceGroupView implements \IteratorAggregate 22 | { 23 | /** 24 | * @var string 25 | */ 26 | public $label; 27 | 28 | /** 29 | * @var ChoiceGroupView[]|ChoiceView[] 30 | */ 31 | public $choices; 32 | 33 | /** 34 | * @param string $label The label of the group 35 | * @param ChoiceGroupView[]|ChoiceView[] $choices the choice views in the 36 | * group 37 | */ 38 | public function __construct(string $label, array $choices = []) 39 | { 40 | $this->label = $label; 41 | $this->choices = $choices; 42 | } 43 | 44 | /** 45 | * @return \ArrayIterator|ChoiceGroupView[]|ChoiceView[] 46 | */ 47 | public function getIterator(): \Traversable 48 | { 49 | return new \ArrayIterator($this->choices); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ChoiceList/View/ChoiceView.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ChoiceList\View; 15 | 16 | /** 17 | * Represents a choice in templates. 18 | * 19 | * @author Bernhard Schussek 20 | */ 21 | class ChoiceView 22 | { 23 | /** 24 | * The label displayed to humans. 25 | * 26 | * @var string 27 | */ 28 | public $label; 29 | 30 | /** 31 | * The view representation of the choice. 32 | * 33 | * @var string 34 | */ 35 | public $value; 36 | 37 | /** 38 | * The original choice value. 39 | */ 40 | public $data; 41 | 42 | /** 43 | * Additional attributes for the HTML tag. 44 | * 45 | * @var array 46 | */ 47 | public $attr; 48 | 49 | /** 50 | * @param mixed $data The original choice 51 | * @param string $value The view representation of the choice 52 | * @param string $label The label displayed to humans 53 | * @param array $attr Additional attributes for the HTML tag 54 | */ 55 | public function __construct($data, string $value, string $label, array $attr = []) 56 | { 57 | $this->data = $data; 58 | $this->value = $value; 59 | $this->label = $label; 60 | $this->attr = $attr; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/DataTransformer/ChoiceToValueTransformer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\DataTransformer; 15 | 16 | use Rollerworks\Component\Search\DataTransformer; 17 | use Rollerworks\Component\Search\Exception\TransformationFailedException; 18 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceList; 19 | 20 | /** 21 | * @author Bernhard Schussek 22 | */ 23 | final class ChoiceToValueTransformer implements DataTransformer 24 | { 25 | private $choiceList; 26 | 27 | public function __construct(ChoiceList $choiceList) 28 | { 29 | $this->choiceList = $choiceList; 30 | } 31 | 32 | public function transform($choice) 33 | { 34 | $value = $this->choiceList->getValuesForChoices([$choice]); 35 | 36 | return (string) current($value); 37 | } 38 | 39 | public function reverseTransform($value) 40 | { 41 | if ($value !== null && ! \is_string($value)) { 42 | throw new TransformationFailedException('Expected a string or null.'); 43 | } 44 | 45 | $choices = $this->choiceList->getChoicesForValues([(string) $value]); 46 | 47 | if (\count($choices) !== 1) { 48 | if ($value === null || $value === '') { 49 | return; 50 | } 51 | 52 | throw new TransformationFailedException(\sprintf('The choice "%s" does not exist or is not unique', $value)); 53 | } 54 | 55 | return current($choices); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\DataTransformer; 15 | 16 | use Rollerworks\Component\Search\Exception\TransformationFailedException; 17 | 18 | /** 19 | * Transforms between an integer and a localized number with grouping 20 | * (each thousand) and comma separators. 21 | * 22 | * @author Bernhard Schussek 23 | */ 24 | final class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransformer 25 | { 26 | /** 27 | * @param bool $grouping Whether thousands should be grouped 28 | * @param int $roundingMode One of the ROUND_ constants in this class 29 | */ 30 | public function __construct(?bool $grouping = false, ?int $roundingMode = self::ROUND_DOWN) 31 | { 32 | parent::__construct(0, $grouping, $roundingMode); 33 | } 34 | 35 | public function reverseTransform($value): ?int 36 | { 37 | $decimalSeparator = $this->getNumberFormatter()->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL); 38 | 39 | if (\is_string($value) && mb_strpos($value, $decimalSeparator) !== false) { 40 | throw new TransformationFailedException(\sprintf('The value "%s" is not a valid integer.', $value)); 41 | } 42 | 43 | $result = parent::reverseTransform($value); 44 | 45 | return $result !== null ? (int) $result : null; 46 | } 47 | 48 | /** 49 | * @internal 50 | */ 51 | protected function castParsedValue($value) 52 | { 53 | return $value; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/DataTransformer/IntegerToStringTransformer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\DataTransformer; 15 | 16 | /** 17 | * Transforms between an integer and a localized number with grouping 18 | * (each thousand) and comma separators. 19 | * 20 | * @author Bernhard Schussek 21 | */ 22 | final class IntegerToStringTransformer extends NumberToStringTransformer 23 | { 24 | /** 25 | * @param int|null $roundingMode One of the ROUND_ constants in this class 26 | */ 27 | public function __construct(?int $roundingMode = null, bool $grouping = false) 28 | { 29 | parent::__construct(0, $grouping, $roundingMode ?? self::ROUND_DOWN); 30 | } 31 | 32 | public function reverseTransform($value): ?int 33 | { 34 | $result = parent::reverseTransform($value); 35 | 36 | return $result !== null ? (int) $result : null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/Model/MoneyValue.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\Model; 15 | 16 | use Money\Money; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | class MoneyValue 22 | { 23 | /** 24 | * @var Money 25 | */ 26 | public $value; 27 | 28 | /** 29 | * @var bool 30 | */ 31 | public $withCurrency; 32 | 33 | /** 34 | * @param bool $withCurrency indicate the input was provided with a currency. 35 | * This is only used for exporting 36 | */ 37 | public function __construct(Money $value, bool $withCurrency = true) 38 | { 39 | $this->withCurrency = $withCurrency; 40 | $this->value = $value; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/Type/CountryType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ArrayChoiceList; 17 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceList; 18 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceLoaderTrait; 19 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\Loader\ChoiceLoader; 20 | use Rollerworks\Component\Search\Field\AbstractFieldType; 21 | use Symfony\Component\Intl\Countries; 22 | use Symfony\Component\OptionsResolver\OptionsResolver; 23 | 24 | /** 25 | * @author Sebastiaan Stok 26 | */ 27 | final class CountryType extends AbstractFieldType implements ChoiceLoader 28 | { 29 | use ChoiceLoaderTrait; 30 | 31 | public function configureOptions(OptionsResolver $resolver): void 32 | { 33 | $resolver->setDefaults([ 34 | 'choice_loader' => $this, 35 | 'choice_translation_domain' => false, 36 | ]); 37 | } 38 | 39 | public function getParent(): ?string 40 | { 41 | return ChoiceType::class; 42 | } 43 | 44 | public function loadChoiceList(?callable $value = null): ChoiceList 45 | { 46 | if ($this->choiceList !== null) { 47 | return $this->choiceList; 48 | } 49 | 50 | return $this->choiceList = new ArrayChoiceList(array_flip(Countries::getNames()), $value); 51 | } 52 | 53 | public function isValuesConstant(): bool 54 | { 55 | return true; 56 | } 57 | 58 | public function getBlockPrefix(): string 59 | { 60 | return 'country'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/Type/CurrencyType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ArrayChoiceList; 17 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceList; 18 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceLoaderTrait; 19 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\Loader\ChoiceLoader; 20 | use Rollerworks\Component\Search\Field\AbstractFieldType; 21 | use Symfony\Component\Intl\Currencies; 22 | use Symfony\Component\OptionsResolver\OptionsResolver; 23 | 24 | /** 25 | * @author Sebastiaan Stok 26 | */ 27 | final class CurrencyType extends AbstractFieldType implements ChoiceLoader 28 | { 29 | use ChoiceLoaderTrait; 30 | 31 | public function configureOptions(OptionsResolver $resolver): void 32 | { 33 | $resolver->setDefaults([ 34 | 'choice_loader' => $this, 35 | 'choice_translation_domain' => false, 36 | 'view_format' => 'value', 37 | ]); 38 | } 39 | 40 | public function getParent(): ?string 41 | { 42 | return ChoiceType::class; 43 | } 44 | 45 | public function loadChoiceList(?callable $value = null): ChoiceList 46 | { 47 | if ($this->choiceList !== null) { 48 | return $this->choiceList; 49 | } 50 | 51 | return $this->choiceList = new ArrayChoiceList(array_flip(Currencies::getNames()), $value); 52 | } 53 | 54 | public function isValuesConstant(): bool 55 | { 56 | return true; 57 | } 58 | 59 | public function getBlockPrefix(): string 60 | { 61 | return 'currency'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/Type/LanguageType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ArrayChoiceList; 17 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceList; 18 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceLoaderTrait; 19 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\Loader\ChoiceLoader; 20 | use Rollerworks\Component\Search\Field\AbstractFieldType; 21 | use Symfony\Component\Intl\Languages; 22 | use Symfony\Component\OptionsResolver\OptionsResolver; 23 | 24 | /** 25 | * @author Sebastiaan Stok 26 | */ 27 | final class LanguageType extends AbstractFieldType implements ChoiceLoader 28 | { 29 | use ChoiceLoaderTrait; 30 | 31 | public function configureOptions(OptionsResolver $resolver): void 32 | { 33 | $resolver->setDefaults([ 34 | 'choice_loader' => $this, 35 | 'choice_translation_domain' => false, 36 | ]); 37 | } 38 | 39 | public function getParent(): ?string 40 | { 41 | return ChoiceType::class; 42 | } 43 | 44 | public function loadChoiceList(?callable $value = null): ChoiceList 45 | { 46 | if ($this->choiceList !== null) { 47 | return $this->choiceList; 48 | } 49 | 50 | return $this->choiceList = new ArrayChoiceList(array_flip(Languages::getNames()), $value); 51 | } 52 | 53 | public function isValuesConstant(): bool 54 | { 55 | return true; 56 | } 57 | 58 | public function getBlockPrefix(): string 59 | { 60 | return 'language'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/Type/LocaleType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ArrayChoiceList; 17 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceList; 18 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\ChoiceLoaderTrait; 19 | use Rollerworks\Component\Search\Extension\Core\ChoiceList\Loader\ChoiceLoader; 20 | use Rollerworks\Component\Search\Field\AbstractFieldType; 21 | use Symfony\Component\Intl\Locales; 22 | use Symfony\Component\OptionsResolver\OptionsResolver; 23 | 24 | /** 25 | * @author Sebastiaan Stok 26 | */ 27 | final class LocaleType extends AbstractFieldType implements ChoiceLoader 28 | { 29 | use ChoiceLoaderTrait; 30 | 31 | public function configureOptions(OptionsResolver $resolver): void 32 | { 33 | $resolver->setDefaults([ 34 | 'choice_loader' => $this, 35 | 'choice_translation_domain' => false, 36 | 'view_format' => 'value', 37 | ]); 38 | } 39 | 40 | public function getParent(): ?string 41 | { 42 | return ChoiceType::class; 43 | } 44 | 45 | public function loadChoiceList(?callable $value = null): ChoiceList 46 | { 47 | if ($this->choiceList !== null) { 48 | return $this->choiceList; 49 | } 50 | 51 | return $this->choiceList = new ArrayChoiceList(array_flip(Locales::getNames()), $value); 52 | } 53 | 54 | public function isValuesConstant(): bool 55 | { 56 | return true; 57 | } 58 | 59 | public function getBlockPrefix(): string 60 | { 61 | return 'locale'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/Type/TextType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\Type; 15 | 16 | use Rollerworks\Component\Search\Field\AbstractFieldType; 17 | use Rollerworks\Component\Search\Field\FieldConfig; 18 | use Rollerworks\Component\Search\Value\PatternMatch; 19 | 20 | /** 21 | * @author Sebastiaan Stok 22 | */ 23 | final class TextType extends AbstractFieldType 24 | { 25 | public function buildType(FieldConfig $config, array $options): void 26 | { 27 | $config->setValueTypeSupport(PatternMatch::class, true); 28 | } 29 | 30 | public function getBlockPrefix(): string 31 | { 32 | return 'text'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ValueComparator/BirthdayValueComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ValueComparator; 15 | 16 | use Rollerworks\Component\Search\ValueComparator; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | final class BirthdayValueComparator implements ValueComparator 22 | { 23 | /** 24 | * @param \DateTimeImmutable|int $higher 25 | * @param \DateTimeImmutable|int $lower 26 | */ 27 | public function isHigher($higher, $lower, array $options): bool 28 | { 29 | if (! \is_object($higher) xor ! \is_object($lower)) { 30 | return false; 31 | } 32 | 33 | return $higher > $lower; 34 | } 35 | 36 | /** 37 | * @param \DateTimeImmutable|int $lower 38 | * @param \DateTimeImmutable|int $higher 39 | */ 40 | public function isLower($lower, $higher, array $options): bool 41 | { 42 | if (! \is_object($higher) xor ! \is_object($lower)) { 43 | return false; 44 | } 45 | 46 | return $higher < $lower; 47 | } 48 | 49 | /** 50 | * @param \DateTimeImmutable|int $value 51 | * @param \DateTimeImmutable|int $nextValue 52 | */ 53 | public function isEqual($value, $nextValue, array $options): bool 54 | { 55 | if (! \is_object($value) xor ! \is_object($nextValue)) { 56 | return false; 57 | } 58 | 59 | return $value == $nextValue; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ValueComparator/DateTimeValueComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ValueComparator; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class DateTimeValueComparator extends DateValueComparator 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ValueComparator/DateValueComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ValueComparator; 15 | 16 | use Rollerworks\Component\Search\ValueComparator; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | class DateValueComparator implements ValueComparator 22 | { 23 | /** 24 | * @param \DateTimeImmutable $higher 25 | * @param \DateTimeImmutable $lower 26 | */ 27 | public function isHigher($higher, $lower, array $options): bool 28 | { 29 | return $higher > $lower; 30 | } 31 | 32 | /** 33 | * @param \DateTimeImmutable $lower 34 | * @param \DateTimeImmutable $higher 35 | */ 36 | public function isLower($lower, $higher, array $options): bool 37 | { 38 | return $lower < $higher; 39 | } 40 | 41 | /** 42 | * @param \DateTimeImmutable $value 43 | * @param \DateTimeImmutable $nextValue 44 | */ 45 | public function isEqual($value, $nextValue, array $options): bool 46 | { 47 | return $value->getTimestamp() === $nextValue->getTimestamp(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ValueComparator/MoneyValueComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ValueComparator; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Model\MoneyValue; 17 | use Rollerworks\Component\Search\ValueComparator; 18 | 19 | /** 20 | * @author Sebastiaan Stok 21 | */ 22 | final class MoneyValueComparator implements ValueComparator 23 | { 24 | /** 25 | * @param MoneyValue $higher 26 | * @param MoneyValue $lower 27 | */ 28 | public function isHigher($higher, $lower, array $options): bool 29 | { 30 | if (! $higher->value->isSameCurrency($lower->value)) { 31 | return false; 32 | } 33 | 34 | return $higher->value->greaterThan($lower->value); 35 | } 36 | 37 | /** 38 | * @param MoneyValue $lower 39 | * @param MoneyValue $higher 40 | */ 41 | public function isLower($lower, $higher, array $options): bool 42 | { 43 | if (! $higher->value->isSameCurrency($lower->value)) { 44 | return false; 45 | } 46 | 47 | return $lower->value->lessThan($higher->value); 48 | } 49 | 50 | /** 51 | * @param MoneyValue $value 52 | * @param MoneyValue $nextValue 53 | */ 54 | public function isEqual($value, $nextValue, array $options): bool 55 | { 56 | return $value->value->equals($nextValue->value); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ValueComparator/NumberValueComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ValueComparator; 15 | 16 | use Rollerworks\Component\Search\ValueComparator; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | final class NumberValueComparator implements ValueComparator 22 | { 23 | /** 24 | * @param float|int $higher 25 | * @param float|int $lower 26 | */ 27 | public function isHigher($higher, $lower, array $options): bool 28 | { 29 | return $higher > $lower; 30 | } 31 | 32 | /** 33 | * @param float|int $lower 34 | * @param float|int $higher 35 | */ 36 | public function isLower($lower, $higher, array $options): bool 37 | { 38 | return $lower < $higher; 39 | } 40 | 41 | /** 42 | * @param float|int $value 43 | * @param float|int $nextValue 44 | */ 45 | public function isEqual($value, $nextValue, array $options): bool 46 | { 47 | return $value === $nextValue; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/Core/Extension/Core/ValueComparator/SimpleValueComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Core\ValueComparator; 15 | 16 | use Rollerworks\Component\Search\ValueComparator; 17 | 18 | /** 19 | * Default ValueComparator implementation, only able to compare equality. 20 | * 21 | * @author Sebastiaan Stok 22 | */ 23 | final class SimpleValueComparator implements ValueComparator 24 | { 25 | public function isHigher($value, $nextValue, array $options): bool 26 | { 27 | return false; 28 | } 29 | 30 | public function isLower($lower, $higher, array $options): bool 31 | { 32 | return false; 33 | } 34 | 35 | public function isEqual($value, $nextValue, array $options): bool 36 | { 37 | // This does not work for objects, so they should have 38 | // there own comparison classes. 39 | return $value === $nextValue; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Core/Field/AbstractFieldType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\SearchFieldType; 17 | use Rollerworks\Component\Search\Util\StringUtil; 18 | use Symfony\Component\OptionsResolver\OptionsResolver; 19 | 20 | /** 21 | * The AbstractFieldType can be used as a base class implementation for FieldTypes. 22 | * 23 | * An added bonus for extending this class rather then the implementing the the 24 | * {@link FieldType} is that any new methods added the FieldType interface will 25 | * not break existing implementations. 26 | * 27 | * @author Sebastiaan Stok 28 | */ 29 | abstract class AbstractFieldType implements FieldType 30 | { 31 | public function buildType(FieldConfig $config, array $options): void 32 | { 33 | } 34 | 35 | public function buildView(SearchFieldView $view, FieldConfig $config, array $options): void 36 | { 37 | } 38 | 39 | public function configureOptions(OptionsResolver $resolver): void 40 | { 41 | } 42 | 43 | public function getParent(): ?string 44 | { 45 | return SearchFieldType::class; 46 | } 47 | 48 | public function getBlockPrefix(): string 49 | { 50 | return StringUtil::fqcnToBlockPrefix(static::class); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/Core/Field/AbstractFieldTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Symfony\Component\OptionsResolver\OptionsResolver; 17 | 18 | /** 19 | * The AbstractFieldTypeExtension can be used as a base implementation 20 | * for FieldTypeExtensions. 21 | * 22 | * An added bonus for extending this class rather then the implementing the the 23 | * {@link FieldTypeExtension} is that any new methods added the FieldTypeExtension 24 | * Interface will not break existing implementations. 25 | */ 26 | abstract class AbstractFieldTypeExtension implements FieldTypeExtension 27 | { 28 | public function buildType(FieldConfig $builder, array $options): void 29 | { 30 | } 31 | 32 | public function buildView(FieldConfig $config, SearchFieldView $view): void 33 | { 34 | } 35 | 36 | public function configureOptions(OptionsResolver $resolver): void 37 | { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Core/Field/FieldType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Symfony\Component\OptionsResolver\OptionsResolver; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | interface FieldType 22 | { 23 | /** 24 | * Returns the name (FQCN) of the parent type. 25 | */ 26 | public function getParent(): ?string; 27 | 28 | /** 29 | * Sets the default options for this type. 30 | */ 31 | public function configureOptions(OptionsResolver $resolver): void; 32 | 33 | /** 34 | * This configures the {@link FieldConfig}. 35 | * 36 | * This method is called for each type in the hierarchy starting from the 37 | * top most type. Type extensions can further modify the field. 38 | */ 39 | public function buildType(FieldConfig $config, array $options): void; 40 | 41 | /** 42 | * Configures the SearchFieldView instance. 43 | * 44 | * This method is called for each type in the hierarchy starting from the 45 | * top most type. Type extensions can further modify the view. 46 | * 47 | * @see FieldTypeExtension::buildView() 48 | */ 49 | public function buildView(SearchFieldView $view, FieldConfig $config, array $options): void; 50 | 51 | /** 52 | * Returns the prefix of the template block name for this type. 53 | * 54 | * The block prefix defaults to the underscored short class name with 55 | * the "Type" suffix removed (e.g. "UserIdType" => "user_id"). 56 | */ 57 | public function getBlockPrefix(): string; 58 | } 59 | -------------------------------------------------------------------------------- /lib/Core/Field/FieldTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Symfony\Component\OptionsResolver\OptionsResolver; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | interface FieldTypeExtension 22 | { 23 | /** 24 | * This method is called after the extended type has built the type to 25 | * further modify it. 26 | * 27 | * @see SearchFieldType::buildType() 28 | */ 29 | public function buildType(FieldConfig $builder, array $options): void; 30 | 31 | /** 32 | * This method is called after the extended type has built the view to 33 | * further modify it. 34 | */ 35 | public function buildView(FieldConfig $config, SearchFieldView $view): void; 36 | 37 | /** 38 | * Overrides the default options from the extended type. 39 | */ 40 | public function configureOptions(OptionsResolver $resolver): void; 41 | 42 | /** 43 | * @return string FQCN of the type class 44 | */ 45 | public function getExtendedType(): string; 46 | } 47 | -------------------------------------------------------------------------------- /lib/Core/Field/GenericResolvedFieldTypeFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | final class GenericResolvedFieldTypeFactory implements ResolvedFieldTypeFactory 17 | { 18 | public function createResolvedType(FieldType $type, array $typeExtensions, ?ResolvedFieldType $parent = null): ResolvedFieldType 19 | { 20 | return new GenericResolvedFieldType($type, $typeExtensions, $parent); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Core/Field/ResolvedFieldType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Rollerworks\Component\Search\FieldSetView; 17 | use Symfony\Component\OptionsResolver\OptionsResolver; 18 | 19 | /** 20 | * A wrapper for a field type and its extensions. 21 | */ 22 | interface ResolvedFieldType 23 | { 24 | public function getParent(): ?self; 25 | 26 | /** 27 | * Returns the wrapped field type. 28 | */ 29 | public function getInnerType(): FieldType; 30 | 31 | /** 32 | * Returns the extensions of the wrapped field type. 33 | * 34 | * @return FieldTypeExtension[] 35 | */ 36 | public function getTypeExtensions(): array; 37 | 38 | public function createField(string $name, array $options = []): FieldConfig; 39 | 40 | /** 41 | * This configures the {@link FieldConfig}. 42 | * 43 | * This method is called for each type in the hierarchy starting from the 44 | * top most type. Type extensions can further modify the field. 45 | */ 46 | public function buildType(FieldConfig $config, array $options): void; 47 | 48 | /** 49 | * Creates a new SearchFieldView for a field of this type. 50 | */ 51 | public function createFieldView(FieldConfig $config, FieldSetView $view): SearchFieldView; 52 | 53 | /** 54 | * Configures a SearchFieldView for the type hierarchy. 55 | */ 56 | public function buildFieldView(SearchFieldView $view, FieldConfig $config, array $options): void; 57 | 58 | /** 59 | * Returns the prefix of the template block name for this type. 60 | */ 61 | public function getBlockPrefix(): string; 62 | 63 | /** 64 | * Returns the configured options resolver used for this type. 65 | */ 66 | public function getOptionsResolver(): OptionsResolver; 67 | } 68 | -------------------------------------------------------------------------------- /lib/Core/Field/ResolvedFieldTypeFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Rollerworks\Component\Search\Exception\InvalidArgumentException; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | interface ResolvedFieldTypeFactory 22 | { 23 | /** 24 | * Resolves a field type. 25 | * 26 | * @param FieldTypeExtension[] $typeExtensions 27 | * 28 | * @throws InvalidArgumentException if the types parent cannot be retrieved from any extension 29 | */ 30 | public function createResolvedType(FieldType $type, array $typeExtensions, ?ResolvedFieldType $parent = null): ResolvedFieldType; 31 | } 32 | -------------------------------------------------------------------------------- /lib/Core/Field/SearchFieldView.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Rollerworks\Component\Search\FieldSetView; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | class SearchFieldView 22 | { 23 | /** 24 | * The variables assigned to this view. 25 | * 26 | * @var array 27 | */ 28 | public $vars = [ 29 | 'attr' => [], 30 | ]; 31 | 32 | /** 33 | * @var FieldSetView 34 | */ 35 | public $fieldSet; 36 | 37 | public function __construct(FieldSetView $fieldSet) 38 | { 39 | $this->fieldSet = $fieldSet; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Core/Field/TypeRegistry.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Field; 15 | 16 | use Rollerworks\Component\Search\Exception\InvalidArgumentException; 17 | use Rollerworks\Component\Search\SearchExtension; 18 | 19 | /** 20 | * @author Sebastiaan Stok 21 | */ 22 | interface TypeRegistry 23 | { 24 | /** 25 | * Returns a field type by name. 26 | * 27 | * @throws InvalidArgumentException if the type cannot be retrieved from any extension 28 | */ 29 | public function getType(string $name): ResolvedFieldType; 30 | 31 | /** 32 | * Returns whether the given field type is supported. 33 | */ 34 | public function hasType(string $name): bool; 35 | 36 | /** 37 | * Returns the extensions loaded on the registry. 38 | * 39 | * @return SearchExtension[] 40 | */ 41 | public function getExtensions(): array; 42 | } 43 | -------------------------------------------------------------------------------- /lib/Core/FieldSet.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Exception\UnknownFieldException; 17 | use Rollerworks\Component\Search\Field\FieldConfig; 18 | 19 | /** 20 | * A FieldSet holds all the search fields and there configuration. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | interface FieldSet 25 | { 26 | /** 27 | * Returns the name of the set. 28 | */ 29 | public function getSetName(): ?string; 30 | 31 | /** 32 | * Returns the field as a {@link FieldConfig} instance. 33 | * 34 | * @throws UnknownFieldException When the field is not registered at this Fieldset 35 | */ 36 | public function get(string $name): FieldConfig; 37 | 38 | /** 39 | * Returns all the registered fields in the set. 40 | * 41 | * @return FieldConfig[] [name] => {FieldConfig instance}) 42 | */ 43 | public function all(): array; 44 | 45 | /** 46 | * Returns whether the field is registered in the set. 47 | */ 48 | public function has(string $name): bool; 49 | 50 | /** 51 | * Returns whether the field is registered to be used for ordering. 52 | */ 53 | public function isOrder(string $name): bool; 54 | 55 | /** 56 | * Returns whether the field is a private field (primary condition only). 57 | */ 58 | public function isPrivate(string $name): bool; 59 | } 60 | -------------------------------------------------------------------------------- /lib/Core/FieldSetBuilder.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Exception\BadMethodCallException; 17 | use Rollerworks\Component\Search\Field\FieldConfig; 18 | 19 | /** 20 | * @author Sebastiaan Stok 21 | */ 22 | interface FieldSetBuilder 23 | { 24 | /** 25 | * @param string $name Name of search field 26 | * @param string $type The FQCN of the type 27 | * @param array $options Array of options for building the field 28 | * 29 | * @return static The builder 30 | */ 31 | public function add(string $name, string $type, array $options = []); 32 | 33 | /** 34 | * @return static The builder 35 | */ 36 | public function set(FieldConfig $field); 37 | 38 | /** 39 | * @return static The builder 40 | * 41 | * @throws BadMethodCallException When the FieldSet has been already turned into a FieldSet instance 42 | */ 43 | public function remove(string $name); 44 | 45 | public function has(string $name): bool; 46 | 47 | public function get(string $name): FieldConfig; 48 | 49 | /** 50 | * Create the FieldSet using the fields set on the builder. 51 | */ 52 | public function getFieldSet(?string $name = null): FieldSet; 53 | } 54 | -------------------------------------------------------------------------------- /lib/Core/FieldSetConfigurator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | /** 17 | * A FieldSetConfigurator configures a FieldSetBuilder instance. 18 | * 19 | * The purpose of a configurator is to allow re-usage of a FieldSet. 20 | * 21 | * And provide support for name expectations (to allow only specific FieldSets) 22 | * using {@link \Rollerworks\Component\Search\SearchCondition::assertFieldSetName}. 23 | * 24 | * @author Sebastiaan Stok 25 | */ 26 | interface FieldSetConfigurator 27 | { 28 | /** 29 | * Configures the FieldSet builder. 30 | */ 31 | public function buildFieldSet(FieldSetBuilder $builder): void; 32 | } 33 | -------------------------------------------------------------------------------- /lib/Core/FieldSetRegistry.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Exception\InvalidArgumentException; 17 | 18 | interface FieldSetRegistry 19 | { 20 | /** 21 | * Returns a FieldSetConfiguratorInterface by name. 22 | * 23 | * @throws InvalidArgumentException if the configurator can not be retrieved 24 | */ 25 | public function getConfigurator(string $name): FieldSetConfigurator; 26 | 27 | /** 28 | * Returns whether the given FieldSetConfigurator is supported. 29 | */ 30 | public function hasConfigurator(string $name): bool; 31 | } 32 | -------------------------------------------------------------------------------- /lib/Core/FieldSetView.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Field\SearchFieldView; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | class FieldSetView 22 | { 23 | /** 24 | * The variables assigned to this view. 25 | * 26 | * @var array 27 | */ 28 | public $vars = [ 29 | 'attr' => [], 30 | ]; 31 | 32 | /** 33 | * @var SearchFieldView[] 34 | */ 35 | public $fields; 36 | } 37 | -------------------------------------------------------------------------------- /lib/Core/FieldSetWithView.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | interface FieldSetWithView extends FieldSet 20 | { 21 | /** 22 | * Create a new FieldSetView instance of the FieldSet. 23 | */ 24 | public function createView(): FieldSetView; 25 | } 26 | -------------------------------------------------------------------------------- /lib/Core/Input/NormStringQueryInput.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Input; 15 | 16 | /** 17 | * Processes input in the StringInput syntax using the Normalized value format. 18 | */ 19 | final class NormStringQueryInput extends StringInput 20 | { 21 | public const FIELD_LEXER_OPTION_NAME = 'norm_string_query.value_lexer'; 22 | public const VALUE_EXPORTER_OPTION_NAME = 'norm_string_query.value_exporter'; 23 | 24 | protected function initForProcess(ProcessorConfig $config): void 25 | { 26 | $names = []; 27 | $fieldSet = $config->getFieldSet(); 28 | 29 | foreach ($fieldSet->all() as $name => $field) { 30 | if ($fieldSet->isPrivate($name)) { 31 | continue; 32 | } 33 | 34 | $names[$name] = $name; 35 | 36 | if (null !== $customerMatcher = $field->getOption(self::FIELD_LEXER_OPTION_NAME)) { 37 | $this->valueLexers[$name] = $customerMatcher; 38 | } 39 | } 40 | 41 | $this->fields = $names; 42 | $this->structureBuilder = new ConditionStructureBuilder($this->config, $this->validator, $this->errors); 43 | $this->orderStructureBuilder = new OrderStructureBuilder($this->config, $this->validator, $this->errors, ''); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Core/Input/NullValidator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Input; 15 | 16 | use Rollerworks\Component\Search\ErrorList; 17 | use Rollerworks\Component\Search\Field\FieldConfig; 18 | 19 | final class NullValidator implements Validator 20 | { 21 | public function initializeContext(FieldConfig $field, ErrorList $errorList): void 22 | { 23 | // no-op 24 | } 25 | 26 | public function validate($value, string $type, $originalValue, string $path): bool 27 | { 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/Core/Input/Validator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Input; 15 | 16 | use Rollerworks\Component\Search\ErrorList; 17 | use Rollerworks\Component\Search\Field\FieldConfig; 18 | 19 | /** 20 | * The Validator validates input values according to a set of 21 | * rules (constraints). 22 | * 23 | * @author Sebastiaan Stok 24 | */ 25 | interface Validator 26 | { 27 | /** 28 | * Initialize the validator context for the field. 29 | * 30 | * Whenever calling validate(), this context needs to be used. 31 | */ 32 | public function initializeContext(FieldConfig $field, ErrorList $errorList): void; 33 | 34 | /** 35 | * Validates and returns whether the value is valid. 36 | */ 37 | public function validate($value, string $type, $originalValue, string $path): bool; 38 | } 39 | -------------------------------------------------------------------------------- /lib/Core/InputProcessor.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Input\ProcessorConfig; 17 | 18 | /** 19 | * The InputProcessor must be implemented by all input-processor. 20 | * 21 | * @author Sebastiaan Stok 22 | */ 23 | interface InputProcessor 24 | { 25 | /** 26 | * Process the input and returns the result. 27 | * 28 | * The processor should only handle fields that are registered in 29 | * the FieldSet. 30 | * 31 | * @param ProcessorConfig $config Configuration for the processor 32 | * @param mixed $input Input to process, actual format depends 33 | * on the processor implementation 34 | * 35 | * @throws Exception\InvalidSearchConditionException When there are errors in the input 36 | * this can be a failed transformation 37 | * or processing error 38 | */ 39 | public function process(ProcessorConfig $config, $input): SearchCondition; 40 | } 41 | -------------------------------------------------------------------------------- /lib/Core/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/Core/Loader/ClosureContainer.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Loader; 15 | 16 | use Psr\Container\ContainerInterface; 17 | 18 | /** 19 | * Helps with lazily loading dependencies. 20 | * 21 | * This class is provided for easy of use, it should not be used 22 | * directly within your own code. 23 | * 24 | * @internal 25 | */ 26 | final class ClosureContainer implements ContainerInterface 27 | { 28 | private $factories; 29 | private $values = []; 30 | 31 | /** 32 | * @param array|\Closure[] $factories 33 | */ 34 | public function __construct(array $factories) 35 | { 36 | $this->factories = $factories; 37 | } 38 | 39 | public function has(string $id): bool 40 | { 41 | return isset($this->factories[$id]); 42 | } 43 | 44 | public function get(string $id): mixed 45 | { 46 | if (! isset($this->factories[$id])) { 47 | throw new ServiceNotFoundException($id); 48 | } 49 | 50 | if (true !== $factory = $this->factories[$id]) { 51 | $this->factories[$id] = true; 52 | $this->values[$id] = $factory(); 53 | } 54 | 55 | return $this->values[$id]; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Core/Loader/ServiceNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Loader; 15 | 16 | use Psr\Container\NotFoundExceptionInterface; 17 | use Rollerworks\Component\Search\Exception\InvalidArgumentException; 18 | 19 | final class ServiceNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface 20 | { 21 | public function __construct(string $id) 22 | { 23 | parent::__construct(\sprintf('You have requested a non-existent service "%s".', $id)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Core/ParameterBag.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | class ParameterBag 17 | { 18 | private $parameters = []; 19 | 20 | public function __construct(array $parameters = []) 21 | { 22 | foreach ($parameters as $name => $value) { 23 | $this->setParameter($name, $value); 24 | } 25 | } 26 | 27 | public function setParameter(string $name, $value): void 28 | { 29 | $this->parameters['{' . $name . '}'] = $value; 30 | } 31 | 32 | public function injectParameters($template): string 33 | { 34 | return str_replace(array_keys($this->parameters), array_values($this->parameters), (string) $template); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Core/PreloadedExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Exception\InvalidArgumentException; 17 | use Rollerworks\Component\Search\Field\FieldType; 18 | 19 | /** 20 | * @author Sebastiaan Stok 21 | */ 22 | final class PreloadedExtension implements SearchExtension 23 | { 24 | private $types = []; 25 | private $typeExtensions = []; 26 | 27 | /** 28 | * Constructor. 29 | * 30 | * @param FieldType[] $types The types that the extension should support 31 | * @param array[] $typeExtensions The type extensions that the extension should support 32 | */ 33 | public function __construct(array $types, array $typeExtensions = []) 34 | { 35 | $this->types = $types; 36 | $this->typeExtensions = $typeExtensions; 37 | } 38 | 39 | public function getType(string $name): FieldType 40 | { 41 | if (! isset($this->types[$name])) { 42 | throw new InvalidArgumentException( 43 | \sprintf('Type "%s" can not be loaded by this extension', $name) 44 | ); 45 | } 46 | 47 | return $this->types[$name]; 48 | } 49 | 50 | public function hasType(string $name): bool 51 | { 52 | return isset($this->types[$name]); 53 | } 54 | 55 | public function getTypeExtensions(string $name): array 56 | { 57 | return $this->typeExtensions[$name] ?? []; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/Core/README.md: -------------------------------------------------------------------------------- 1 | RollerworksSearch 2 | ================= 3 | 4 | This package provides the core system of [RollerworksSearch][1]. 5 | *See the main repository for full details and examples.* 6 | 7 | If you'd like to contribute to this project, please see the [contributing guide lines][2] 8 | for more information. 9 | 10 | Installation 11 | ------------ 12 | 13 | To install this package, add the `rollerworks/search` to your composer.json 14 | 15 | ```bash 16 | $ php composer.phar require rollerworks/search 17 | ``` 18 | 19 | License 20 | ------- 21 | 22 | The source of this package is subject to the MIT license that is bundled 23 | with this source code in the file [LICENSE](LICENSE). 24 | 25 | [1]: https://github.com/rollerworks/RollerworksSearch 26 | [2]: https://github.com/rollerworks/RollerworksSearch#contributing 27 | -------------------------------------------------------------------------------- /lib/Core/SearchExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Field\FieldType; 17 | use Rollerworks\Component\Search\Field\FieldTypeExtension; 18 | 19 | /** 20 | * @author Sebastiaan Stok 21 | */ 22 | interface SearchExtension 23 | { 24 | /** 25 | * Returns a type by name. 26 | * 27 | * @throws Exception\InvalidArgumentException if the given type is not supported by this extension 28 | */ 29 | public function getType(string $name): FieldType; 30 | 31 | /** 32 | * Returns whether the given type is supported. 33 | */ 34 | public function hasType(string $name): bool; 35 | 36 | /** 37 | * Returns the extensions for the given type. 38 | * 39 | * @return FieldTypeExtension[] 40 | */ 41 | public function getTypeExtensions(string $name): array; 42 | } 43 | -------------------------------------------------------------------------------- /lib/Core/SearchFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Field\FieldConfig; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | interface SearchFactory 22 | { 23 | /** 24 | * Create a new FieldSet instance with the configurator name 25 | * as FieldSet name. 26 | * 27 | * @param FieldSetConfigurator|string $configurator Configurator for building the FieldSet, 28 | * a string will be resolved to a configurator 29 | */ 30 | public function createFieldSet($configurator): FieldSet; 31 | 32 | /** 33 | * @param string $name Name of the field 34 | * @param string $type Type of the field 35 | * @param array $options Array of options for building the field 36 | */ 37 | public function createField(string $name, string $type, array $options = []): FieldConfig; 38 | 39 | public function createFieldSetBuilder(): FieldSetBuilder; 40 | 41 | public function getSerializer(): SearchConditionSerializer; 42 | } 43 | -------------------------------------------------------------------------------- /lib/Core/SearchOrder.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Exception\InvalidArgumentException; 17 | use Rollerworks\Component\Search\Value\ValuesGroup; 18 | 19 | /** 20 | * @author Dalibor Karlović 21 | */ 22 | final class SearchOrder 23 | { 24 | private $values; 25 | 26 | public function __construct(ValuesGroup $valuesGroup) 27 | { 28 | if ($valuesGroup->hasGroups()) { 29 | throw new InvalidArgumentException('A SearchOrder must have a single-level structure. Only fields with single values are accepted.'); 30 | } 31 | 32 | $this->values = $valuesGroup; 33 | } 34 | 35 | public function getValuesGroup(): ValuesGroup 36 | { 37 | return $this->values; 38 | } 39 | 40 | /** 41 | * @return array 42 | */ 43 | public function getFields(): array 44 | { 45 | $fields = []; 46 | 47 | foreach ($this->values->getFields() as $fieldName => $valuesBag) { 48 | $direction = mb_strtolower(current($valuesBag->getSimpleValues())); 49 | \assert($direction === 'desc' || $direction === 'asc'); 50 | 51 | $fields[$fieldName] = $direction; 52 | } 53 | 54 | return $fields; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/Core/SearchPrimaryCondition.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Value\ValuesGroup; 17 | 18 | /** 19 | * SearchPrimaryCondition contains a condition that must be fulfilled at all times. 20 | * 21 | * A SearchPrimaryCondition is applied as `(SearchPrimaryCondition) AND (SearchCondition)`. 22 | * 23 | * Caution: It's important for a QueryGenerator to always apply 24 | * the primary-condition even if the search-condition itself is empty! 25 | * 26 | * @author Sebastiaan Stok 27 | */ 28 | final class SearchPrimaryCondition 29 | { 30 | private $values; 31 | private $order; 32 | 33 | public function __construct(ValuesGroup $valuesGroup) 34 | { 35 | $this->values = $valuesGroup; 36 | } 37 | 38 | public function getValuesGroup(): ValuesGroup 39 | { 40 | return $this->values; 41 | } 42 | 43 | public function setOrder(?SearchOrder $order): void 44 | { 45 | $this->order = $order; 46 | } 47 | 48 | public function getOrder(): ?SearchOrder 49 | { 50 | return $this->order; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/Core/Test/CarbonIntervalComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Test; 15 | 16 | use Carbon\CarbonInterval; 17 | use SebastianBergmann\Comparator\Comparator; 18 | use SebastianBergmann\Comparator\ComparisonFailure; 19 | use SebastianBergmann\Exporter\Exporter; 20 | 21 | final class CarbonIntervalComparator extends Comparator 22 | { 23 | public function accepts(mixed $expected, mixed $actual): bool 24 | { 25 | return $expected instanceof CarbonInterval && $actual instanceof CarbonInterval; 26 | } 27 | 28 | /** 29 | * @param CarbonInterval $expected 30 | * @param CarbonInterval $actual 31 | */ 32 | public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void 33 | { 34 | if ($expected->invert === $actual->invert && $expected->forHumans() === $actual->forHumans()) { 35 | return; 36 | } 37 | 38 | $exporter = new Exporter(); 39 | 40 | throw new ComparisonFailure( 41 | $expected, 42 | $actual, 43 | $exportedExpected = $exporter->export($expected), 44 | $exportedActual = $exporter->export($actual), 45 | false, 46 | \sprintf( 47 | 'Failed asserting that %s matches expected %s.', 48 | $exportedActual, 49 | $exportedExpected 50 | ) 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Core/Util/StringUtil.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Util; 15 | 16 | /** 17 | * @author Issei Murasawa 18 | * @author Bernhard Schussek 19 | */ 20 | final class StringUtil 21 | { 22 | /** 23 | * This class should not be instantiated. 24 | */ 25 | private function __construct() 26 | { 27 | } 28 | 29 | /** 30 | * Converts a fully-qualified class name to a block prefix. 31 | * 32 | * @param string $fqcn The fully-qualified class name 33 | * 34 | * @return string|null The block prefix or null if not a valid FQCN 35 | */ 36 | public static function fqcnToBlockPrefix(string $fqcn): ?string 37 | { 38 | // Non-greedy ("+?") to match "type" suffix, if present 39 | if (preg_match('~([^\\\]+?)(type)?$~i', $fqcn, $matches)) { 40 | return mb_strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\1_\2', '\1_\2'], $matches[1])); 41 | } 42 | 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Core/Value/Compare.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Value; 15 | 16 | final class Compare implements RequiresComparatorValueHolder 17 | { 18 | private $operator; 19 | private $value; 20 | 21 | public const OPERATORS = ['>=', '<=', '<>', '<', '>']; 22 | 23 | /** 24 | * @param string $operator 25 | */ 26 | public function __construct($value, $operator) 27 | { 28 | if (! \in_array($operator, self::OPERATORS, true)) { 29 | throw new \InvalidArgumentException( 30 | \sprintf('Unknown operator "%s".', $operator) 31 | ); 32 | } 33 | 34 | $this->value = $value; 35 | $this->operator = $operator; 36 | } 37 | 38 | public function getOperator(): string 39 | { 40 | return $this->operator; 41 | } 42 | 43 | public function getValue() 44 | { 45 | return $this->value; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Core/Value/ExcludedRange.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Value; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | final class ExcludedRange extends Range 20 | { 21 | } 22 | -------------------------------------------------------------------------------- /lib/Core/Value/Range.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Value; 15 | 16 | /** 17 | * @author Sebastiaan Stok 18 | */ 19 | class Range implements RequiresComparatorValueHolder 20 | { 21 | private $lower; 22 | private $upper; 23 | private $inclusiveLower; 24 | private $inclusiveUpper; 25 | 26 | public function __construct($lower, $upper, bool $inclusiveLower = true, bool $inclusiveUpper = true) 27 | { 28 | $this->lower = $lower; 29 | $this->upper = $upper; 30 | $this->inclusiveLower = $inclusiveLower; 31 | $this->inclusiveUpper = $inclusiveUpper; 32 | } 33 | 34 | public function getLower() 35 | { 36 | return $this->lower; 37 | } 38 | 39 | public function getUpper() 40 | { 41 | return $this->upper; 42 | } 43 | 44 | public function isLowerInclusive(): bool 45 | { 46 | return $this->inclusiveLower; 47 | } 48 | 49 | public function isUpperInclusive(): bool 50 | { 51 | return $this->inclusiveUpper; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Core/Value/RequiresComparatorValueHolder.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Value; 15 | 16 | /** 17 | * RequiresComparatorValueHolder indicates the value-holder 18 | * requires a comparator in the field's type. 19 | */ 20 | interface RequiresComparatorValueHolder extends ValueHolder 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /lib/Core/Value/ValueHolder.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Value; 15 | 16 | interface ValueHolder 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /lib/Core/ValueComparator.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | /** 17 | * ValueComparator. 18 | * 19 | * Each ValueComparator class must implement this interface. 20 | * 21 | * @author Sebastiaan Stok 22 | */ 23 | interface ValueComparator 24 | { 25 | /** 26 | * Returns whether the first value is higher then the second value. 27 | */ 28 | public function isHigher($higher, $lower, array $options): bool; 29 | 30 | /** 31 | * Returns whether the first value is lower then the second value. 32 | */ 33 | public function isLower($lower, $higher, array $options): bool; 34 | 35 | /** 36 | * Returns whether the first value equals the second value. 37 | */ 38 | public function isEqual($value, $nextValue, array $options): bool; 39 | } 40 | -------------------------------------------------------------------------------- /lib/Core/ValuesBagBuilder.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search; 15 | 16 | use Rollerworks\Component\Search\Value\ValuesBag; 17 | 18 | /** 19 | * Helper class for the SearchConditionBuilder. 20 | */ 21 | class ValuesBagBuilder extends ValuesBag 22 | { 23 | private $parent; 24 | 25 | public function __construct(SearchConditionBuilder $parent) 26 | { 27 | $this->parent = $parent; 28 | } 29 | 30 | public function end(): SearchConditionBuilder 31 | { 32 | return $this->parent; 33 | } 34 | 35 | /** 36 | * @internal 37 | */ 38 | public function toValuesBag(): ValuesBag 39 | { 40 | $valuesBag = new ValuesBag(); 41 | $valuesBag->__unserialize($this->__serialize()); 42 | 43 | return $valuesBag; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Core/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollerworks/search", 3 | "description": "RollerworksSearch provides you with a powerful search system", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "rollerworks", 8 | "filter", 9 | "search" 10 | ], 11 | "authors": [ 12 | { 13 | "name": "Sebastiaan Stok", 14 | "email": "s.stok@rollercapes.net" 15 | }, 16 | { 17 | "name": "Community contributions", 18 | "homepage": "https://github.com/rollerworks/search/contributors" 19 | } 20 | ], 21 | "homepage": "https://rollerworks.github.io/", 22 | "require": { 23 | "php": ">=8.1", 24 | "nesbot/carbon": "^2.38 || ^3.0", 25 | "psr/container": "^1.1 || ^2.0", 26 | "symfony/intl": "^6.0 || ^7.0", 27 | "symfony/options-resolver": "^6.4 || ^7.0", 28 | "symfony/property-access": "^6.4 || ^7.0", 29 | "symfony/string": "^6.4 || ^7.0", 30 | "symfony/translation-contracts": "^3.4" 31 | }, 32 | "require-dev": { 33 | "moneyphp/money": "^3.2.0 || ^4.0", 34 | "phpunit/phpunit": "^9.5", 35 | "symfony/phpunit-bridge": "^6.4 || ^7.0", 36 | "symfony/var-dumper": "^6.4 || ^7.0" 37 | }, 38 | "conflict": { 39 | "moneyphp/money": "<3.2.0" 40 | }, 41 | "suggest": { 42 | "moneyphp/money": "To use the MoneyType" 43 | }, 44 | "autoload": { 45 | "psr-4": { 46 | "Rollerworks\\Component\\Search\\": "" 47 | }, 48 | "exclude-from-classmap": [ 49 | "Test/" 50 | ] 51 | }, 52 | "autoload-dev": { 53 | "psr-4": { 54 | "Rollerworks\\Component\\Search\\Tests\\": "Tests/" 55 | } 56 | }, 57 | "extra": { 58 | "branch-alias": { 59 | "dev-main": "2.0-dev" 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/ColumnConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Dbal; 15 | 16 | /** 17 | * A ColumnConversion allows to wrap the query's column in a custom 18 | * SQL statement (as-is). 19 | * 20 | * This interface can be combined with the ValueConversion interface. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | interface ColumnConversion 25 | { 26 | /** 27 | * Return the $column wrapped inside an SQL statement like: MY_FUNCTION(column). 28 | * 29 | * The returned result must a be a platform specific SQL statement 30 | * that can be used as a column in query. 31 | * 32 | * @param string $column The column name and table alias, eg. i.id 33 | * @param array $options Options of the Field configuration 34 | * @param ConversionHints $hints Special information for the conversion process 35 | */ 36 | public function convertColumn(string $column, array $options, ConversionHints $hints): string; 37 | } 38 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/Extension/Conversion/ChildCountConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Dbal\Conversion; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\ColumnConversion; 17 | use Rollerworks\Component\Search\Doctrine\Dbal\ConversionHints; 18 | 19 | /** 20 | * Allows counting the number of parent/children references. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | class ChildCountConversion implements ColumnConversion 25 | { 26 | public function convertColumn(string $column, array $options, ConversionHints $hints): string 27 | { 28 | return '(SELECT COUNT(*) FROM ' . $options['table_name'] . ' WHERE ' . $options['table_column'] . " = {$column})"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/Extension/DoctrineDbalExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Dbal; 15 | 16 | use Rollerworks\Component\Search\AbstractExtension; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Dbal\Conversion\AgeDateConversion; 18 | use Rollerworks\Component\Search\Extension\Doctrine\Dbal\Conversion\MoneyValueConversion; 19 | 20 | class DoctrineDbalExtension extends AbstractExtension 21 | { 22 | protected function loadTypesExtensions(): array 23 | { 24 | return [ 25 | new Type\FieldTypeExtension(), 26 | new Type\DateTimeTypeExtension(), 27 | new Type\BirthdayTypeExtension(new AgeDateConversion()), 28 | new Type\MoneyTypeExtension(new MoneyValueConversion()), 29 | ]; 30 | } 31 | 32 | protected function loadTypes(): array 33 | { 34 | return [ 35 | new Type\ChildCountType(), 36 | ]; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/Extension/Type/BirthdayTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Dbal\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\BirthdayType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Dbal\Conversion\AgeDateConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | /** 22 | * Configures the AgeConversion for Doctrine ORM. 23 | * 24 | * @author Sebastiaan Stok 25 | */ 26 | class BirthdayTypeExtension extends AbstractFieldTypeExtension 27 | { 28 | /** 29 | * @var AgeDateConversion 30 | */ 31 | private $conversion; 32 | 33 | public function __construct(AgeDateConversion $conversion) 34 | { 35 | $this->conversion = $conversion; 36 | } 37 | 38 | public function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setDefaults( 41 | ['doctrine_dbal_conversion' => $this->conversion] 42 | ); 43 | } 44 | 45 | public function getExtendedType(): string 46 | { 47 | return BirthdayType::class; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/Extension/Type/ChildCountType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Dbal\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\IntegerType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Dbal\Conversion\ChildCountConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldType; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | /** 22 | * ItemCountType allows a parent/children-reference counting. 23 | * 24 | * @author Sebastiaan Stok 25 | */ 26 | class ChildCountType extends AbstractFieldType 27 | { 28 | protected $conversion; 29 | 30 | public function __construct() 31 | { 32 | $this->conversion = new ChildCountConversion(); 33 | } 34 | 35 | public function configureOptions(OptionsResolver $resolver): void 36 | { 37 | $conversion = $this->conversion; 38 | 39 | $resolver->setRequired(['table_name', 'table_column']); 40 | $resolver->setDefaults( 41 | [ 42 | 'doctrine_dbal_conversion' => static fn () => $conversion, 43 | ] 44 | ); 45 | } 46 | 47 | public function getParent(): ?string 48 | { 49 | return IntegerType::class; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/Extension/Type/DateTimeTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Dbal\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\DateTimeType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Dbal\Conversion\DateIntervalConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\Options; 20 | use Symfony\Component\OptionsResolver\OptionsResolver; 21 | 22 | class DateTimeTypeExtension extends AbstractFieldTypeExtension 23 | { 24 | /** 25 | * @var DateIntervalConversion 26 | */ 27 | private $conversion; 28 | 29 | public function __construct() 30 | { 31 | $this->conversion = new DateIntervalConversion(); 32 | } 33 | 34 | public function configureOptions(OptionsResolver $resolver): void 35 | { 36 | $resolver->setDefaults( 37 | ['doctrine_dbal_conversion' => function (Options $options) { 38 | if ($options['allow_relative']) { 39 | return $this->conversion; 40 | } 41 | 42 | return null; 43 | }] 44 | ); 45 | } 46 | 47 | public function getExtendedType(): string 48 | { 49 | return DateTimeType::class; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/Extension/Type/FieldTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Dbal\Type; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\ColumnConversion; 17 | use Rollerworks\Component\Search\Doctrine\Dbal\ValueConversion; 18 | use Rollerworks\Component\Search\Extension\Core\Type\SearchFieldType; 19 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 20 | use Symfony\Component\OptionsResolver\OptionsResolver; 21 | 22 | /** 23 | * Allow to configure Doctrine ORM conversions. 24 | * 25 | * @author Sebastiaan Stok 26 | */ 27 | class FieldTypeExtension extends AbstractFieldTypeExtension 28 | { 29 | public function configureOptions(OptionsResolver $resolver): void 30 | { 31 | $resolver->setDefaults(['doctrine_dbal_conversion' => null]); 32 | $resolver->setAllowedTypes( 33 | 'doctrine_dbal_conversion', 34 | [ 35 | 'null', 36 | \Closure::class, 37 | ColumnConversion::class, 38 | ValueConversion::class, 39 | ] 40 | ); 41 | } 42 | 43 | public function getExtendedType(): string 44 | { 45 | return SearchFieldType::class; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/Extension/Type/MoneyTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Dbal\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\MoneyType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Dbal\Conversion\MoneyValueConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | /** 22 | * Configures the MoneyValueConversion for Doctrine DBAL. 23 | * 24 | * Note: Due to technical limitations currently the option 25 | * "doctrine_dbal_with_currency" has no effect and the 26 | * currency comparisons is always ignored. 27 | * 28 | * @author Sebastiaan Stok 29 | */ 30 | class MoneyTypeExtension extends AbstractFieldTypeExtension 31 | { 32 | /** 33 | * @var MoneyValueConversion 34 | */ 35 | private $conversion; 36 | 37 | public function __construct(MoneyValueConversion $conversion) 38 | { 39 | $this->conversion = $conversion; 40 | } 41 | 42 | public function configureOptions(OptionsResolver $resolver): void 43 | { 44 | $resolver->setDefaults( 45 | [ 46 | 'doctrine_dbal_conversion' => $this->conversion, 47 | 'doctrine_dbal_with_currency' => false, 48 | ] 49 | ); 50 | 51 | $resolver->setAllowedTypes('doctrine_dbal_with_currency', ['bool']); 52 | } 53 | 54 | public function getExtendedType(): string 55 | { 56 | return MoneyType::class; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/FieldConfigurationSet.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Dbal; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\Query\QueryField; 17 | use Rollerworks\Component\Search\FieldSet; 18 | 19 | /** 20 | * @internal 21 | */ 22 | final class FieldConfigurationSet 23 | { 24 | private FieldSet $fieldSet; 25 | /** @var array> */ 26 | public $fields = []; 27 | 28 | public function __construct(FieldSet $fieldSet) 29 | { 30 | $this->fieldSet = $fieldSet; 31 | } 32 | 33 | public function setField(string $fieldName, string $column, ?string $alias = null, string $type = 'string'): void 34 | { 35 | $mappingIdx = null; 36 | 37 | if (mb_strpos($fieldName, '#') !== false) { 38 | [$fieldName, $mappingIdx] = explode('#', $fieldName, 2); 39 | unset($this->fields[$fieldName][null]); 40 | } else { 41 | $this->fields[$fieldName] = []; 42 | } 43 | 44 | $this->fields[$fieldName][$mappingIdx] = new QueryField( 45 | $fieldName . ($mappingIdx !== null ? "#{$mappingIdx}" : ''), 46 | $this->fieldSet->get($fieldName), 47 | $type, 48 | $column, 49 | $alias 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/QueryPlatform/MssqlQueryPlatform.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Dbal\QueryPlatform; 15 | 16 | class MssqlQueryPlatform extends AbstractQueryPlatform 17 | { 18 | protected function getLikeEscapeChars(): string 19 | { 20 | return '%_[]'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/QueryPlatform/SqlQueryPlatform.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Dbal\QueryPlatform; 15 | 16 | final class SqlQueryPlatform extends AbstractQueryPlatform 17 | { 18 | } 19 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/README.md: -------------------------------------------------------------------------------- 1 | RollerworksSearch Doctrine DBAL extension 2 | ========================================= 3 | 4 | This package provides the Doctrine DBAL extension for [RollerworksSearch][1]. 5 | 6 | If you'd like to contribute to this project, please see the [contributing guide lines][2] 7 | for more information. 8 | 9 | Installation 10 | ------------ 11 | 12 | To install this extension, add the `search-doctrine-dbal` to your composer.json 13 | 14 | ```bash 15 | $ php composer.phar require rollerworks/search-doctrine-dbal 16 | ``` 17 | 18 | License 19 | ------- 20 | 21 | The source of this package is subject to the MIT license that is bundled 22 | with this source code in the file [LICENSE](LICENSE). 23 | 24 | [1]: https://github.com/rollerworks/RollerworksSearch 25 | [2]: https://github.com/rollerworks/RollerworksSearch#contributing 26 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/ValueConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Dbal; 15 | 16 | /** 17 | * A ValueConversion allows to convert the value "model" to a valid 18 | * SQL statement to be used a column value. 19 | * 20 | * @author Sebastiaan Stok 21 | */ 22 | interface ValueConversion 23 | { 24 | /** 25 | * Returns the converted value as an SQL statement. 26 | * 27 | * The returned result must a be a platform specific SQL statement 28 | * that can be used as a column's value. 29 | * 30 | * Used values must be registered as parameters using `$hints->createParamReferenceFor($value)` 31 | * with an option DBAL Type as second argument (converted afterwards). 32 | * 33 | * @param mixed $value The "model" value format 34 | * @param array $options Options of the Field configuration 35 | * @param ConversionHints $hints Special information for the conversion process 36 | */ 37 | public function convertValue($value, array $options, ConversionHints $hints): string; 38 | } 39 | -------------------------------------------------------------------------------- /lib/Doctrine/Dbal/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollerworks/search-doctrine-dbal", 3 | "description": "Doctrine DBAL extension for RollerworksSearch", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "rollerworks", 8 | "search", 9 | "doctrine", 10 | "dbal" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Sebastiaan Stok", 15 | "email": "s.stok@rollercapes.net" 16 | }, 17 | { 18 | "name": "Community contributions", 19 | "homepage": "https://github.com/Rollerworks/rollerworks-search-doctrine-dbal/contributors" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=8.1", 24 | "doctrine/dbal": "^3.8.0 || ^4.0", 25 | "psr/simple-cache": "^1.0.0 || ^2.0.0 || ^3.0.0", 26 | "rollerworks/search": ">=2.0.0-BETA9 ^2.0@dev" 27 | }, 28 | "require-dev": { 29 | "moneyphp/money": "^3.0.7", 30 | "symfony/phpunit-bridge": "^6.4 || ^7.0", 31 | "symfony/var-dumper": "^6.4 || ^7.0" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Rollerworks\\Component\\Search\\Doctrine\\Dbal\\": "", 36 | "Rollerworks\\Component\\Search\\Extension\\Doctrine\\Dbal\\": "Extension/" 37 | }, 38 | "exclude-from-classmap": [ 39 | "Tests/" 40 | ] 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "Rollerworks\\Component\\Search\\Tests\\Doctrine\\Dbal\\": "Tests/" 45 | } 46 | }, 47 | "config": { 48 | "preferred-install": { 49 | "doctrine/dbal": "source", 50 | "*": "dist" 51 | }, 52 | "sort-packages": true 53 | }, 54 | "extra": { 55 | "branch-alias": { 56 | "dev-main": "2.0-dev" 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/ColumnConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\ConversionHints; 17 | 18 | /** 19 | * A ColumnConversion allows to wrap the query's column in a custom 20 | * DQL statement (as-is). 21 | * 22 | * This interface can be combined with the ValueConversion interface. 23 | * 24 | * @author Sebastiaan Stok 25 | */ 26 | interface ColumnConversion 27 | { 28 | /** 29 | * Return the $column wrapped inside an DQL statement like: MY_FUNCTION(column). 30 | * 31 | * @param string $column The column name and table alias, eg. i.id 32 | * @param array $options Options of the Field configuration 33 | * @param ConversionHints $hints Special information for the conversion process 34 | */ 35 | public function convertColumn(string $column, array $options, ConversionHints $hints): string; 36 | } 37 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/DoctrineOrmFactory.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm; 15 | 16 | use Doctrine\ORM\QueryBuilder; 17 | use Psr\SimpleCache\CacheInterface as Cache; 18 | use Rollerworks\Component\Search\SearchCondition; 19 | 20 | /** 21 | * @final 22 | */ 23 | class DoctrineOrmFactory 24 | { 25 | /** 26 | * @var Cache 27 | */ 28 | private $cacheDriver; 29 | 30 | public function __construct(?Cache $cacheDriver = null) 31 | { 32 | $this->cacheDriver = $cacheDriver; 33 | } 34 | 35 | /** 36 | * Creates a new ConditionGenerator for the SearchCondition. 37 | * 38 | * Conversions are applied using the 'doctrine_dbal_conversion' option (when present). 39 | */ 40 | public function createConditionGenerator(QueryBuilder $query, SearchCondition $searchCondition): QueryBuilderConditionGenerator 41 | { 42 | return new QueryBuilderConditionGenerator($query, $searchCondition); 43 | } 44 | 45 | /** 46 | * Creates a new CachedConditionGenerator for the SearchCondition. 47 | * 48 | * @param \DateInterval|int|null $ttl Optional. The TTL value of this item. If no value is sent and 49 | * the driver supports TTL then the library may set a default value 50 | * for it or let the driver take care of that. 51 | */ 52 | public function createCachedConditionGenerator(QueryBuilder $query, SearchCondition $searchCondition, $ttl = null): ConditionGenerator 53 | { 54 | if ($this->cacheDriver === null) { 55 | return $this->createConditionGenerator($query, $searchCondition); 56 | } 57 | 58 | return new CachedDqlConditionGenerator($query, $searchCondition, $this->cacheDriver, $ttl); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Conversion/AgeDateConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Conversion; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\ConversionHints; 17 | use Rollerworks\Component\Search\Doctrine\Orm\ColumnConversion; 18 | use Rollerworks\Component\Search\Doctrine\Orm\ValueConversion; 19 | 20 | final class AgeDateConversion implements ColumnConversion, ValueConversion 21 | { 22 | public function convertColumn(string $column, array $options, ConversionHints $hints): string 23 | { 24 | if ($hints->getProcessingValue() instanceof \DateTimeInterface) { 25 | return "SEARCH_CONVERSION_CAST({$column}, 'DATE')"; 26 | } 27 | 28 | return "SEARCH_CONVERSION_AGE({$column})"; 29 | } 30 | 31 | public function convertValue($value, array $options, ConversionHints $hints): string 32 | { 33 | if ($value instanceof \DateTimeImmutable) { 34 | return $hints->createParamReferenceFor($value, 'date_immutable'); 35 | } 36 | 37 | if ($value instanceof \DateTime) { 38 | return $hints->createParamReferenceFor($value, 'date'); 39 | } 40 | 41 | return $hints->createParamReferenceFor($value, 'integer'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Conversion/ChildCountConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Conversion; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\ConversionHints; 17 | use Rollerworks\Component\Search\Doctrine\Orm\ColumnConversion; 18 | 19 | final class ChildCountConversion implements ColumnConversion 20 | { 21 | public function convertColumn(string $column, array $options, ConversionHints $hints): string 22 | { 23 | return \sprintf('SEARCH_COUNT_CHILDREN(%s, %s, %s)', $options['table_name'], $options['table_column'], $column); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Conversion/DateIntervalConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Conversion; 15 | 16 | use Carbon\CarbonInterval; 17 | use Doctrine\DBAL\Types\Types; 18 | use Rollerworks\Component\Search\Doctrine\Dbal\ConversionHints; 19 | use Rollerworks\Component\Search\Doctrine\Orm\ValueConversion; 20 | 21 | final class DateIntervalConversion implements ValueConversion 22 | { 23 | /** 24 | * @param CarbonInterval|\DateTimeImmutable $value 25 | */ 26 | public function convertValue($value, array $options, ConversionHints $hints): string 27 | { 28 | if ($value instanceof \DateTimeImmutable) { 29 | return $hints->createParamReferenceFor($value, Types::DATETIME_IMMUTABLE); 30 | } 31 | 32 | $value = clone $value; 33 | $value->locale('en'); 34 | 35 | return \sprintf("SEARCH_CAST_INTERVAL('%s', %s)", $value->forHumans(), $value->invert === 1 ? 'true' : 'false'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Functions/AgeFunction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm\Extension\Functions; 15 | 16 | use Doctrine\ORM\Query\Parser; 17 | use Doctrine\ORM\Query\SqlWalker; 18 | use Doctrine\ORM\Query\TokenType; 19 | 20 | /** 21 | * "SEARCH_CONVERSION_AGE" "(" StringPrimary ")". 22 | */ 23 | final class AgeFunction extends PlatformSpecificFunction 24 | { 25 | public $stringPrimary; 26 | 27 | public function getSql(SqlWalker $sqlWalker): string 28 | { 29 | $platform = $this->getPlatformName($sqlWalker->getConnection()); 30 | $expression = $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary); 31 | 32 | $convertMap = []; 33 | $convertMap['pgsql'] = "to_char(age(%1\$s), 'YYYY'::text)::integer"; 34 | $convertMap['mysql'] = "(DATE_FORMAT(NOW(), '%%Y') - DATE_FORMAT(%1\$s, '%%Y') - (DATE_FORMAT(NOW(), '00-%%m-%%d') < DATE_FORMAT(%1\$s, '00-%%m-%%d')))"; 35 | $convertMap['mssql'] = 'DATEDIFF(hour, %1$s, GETDATE())/8766'; 36 | $convertMap['oci'] = 'trunc((months_between(sysdate, (sysdate - %1$s)))/12)'; 37 | $convertMap['mock'] = $convertMap['pgsql']; 38 | 39 | if (isset($convertMap[$platform])) { 40 | return \sprintf($convertMap[$platform], $expression); 41 | } 42 | 43 | throw new \RuntimeException( 44 | \sprintf('Unsupported platform "%s" for SEARCH_CONVERSION_AGE.', $platform) 45 | ); 46 | } 47 | 48 | public function parse(Parser $parser): void 49 | { 50 | $parser->match(TokenType::T_IDENTIFIER); 51 | $parser->match(TokenType::T_OPEN_PARENTHESIS); 52 | 53 | $this->stringPrimary = $parser->StringPrimary(); 54 | 55 | $parser->match(TokenType::T_CLOSE_PARENTHESIS); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Functions/CastFunction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm\Extension\Functions; 15 | 16 | use Doctrine\ORM\Query\AST\Functions\FunctionNode; 17 | use Doctrine\ORM\Query\Parser; 18 | use Doctrine\ORM\Query\SqlWalker; 19 | use Doctrine\ORM\Query\TokenType; 20 | 21 | /** 22 | * "SEARCH_CONVERSION_CAST" "(" StringPrimary ", " StringPrimary ")". 23 | */ 24 | final class CastFunction extends FunctionNode 25 | { 26 | public $stringPrimary; 27 | 28 | /** 29 | * @var string 30 | */ 31 | public $type; 32 | 33 | public function getSql(SqlWalker $sqlWalker): string 34 | { 35 | $expression = $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary); 36 | 37 | return \sprintf('CAST(%s AS %s)', $expression, $this->type); 38 | } 39 | 40 | public function parse(Parser $parser): void 41 | { 42 | $parser->match(TokenType::T_IDENTIFIER); 43 | $parser->match(TokenType::T_OPEN_PARENTHESIS); 44 | 45 | $this->stringPrimary = $parser->StringPrimary(); 46 | 47 | $parser->match(TokenType::T_COMMA); 48 | 49 | $this->type = (string) $parser->Literal()->value; 50 | 51 | $parser->match(TokenType::T_CLOSE_PARENTHESIS); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Functions/CountChildrenFunction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm\Extension\Functions; 15 | 16 | use Doctrine\ORM\Query\AST\Functions\FunctionNode; 17 | use Doctrine\ORM\Query\Parser; 18 | use Doctrine\ORM\Query\SqlWalker; 19 | use Doctrine\ORM\Query\TokenType; 20 | 21 | /** 22 | * "SEARCH_COUNT_CHILDREN" "(" StringPrimary "," StringPrimary "," StringPrimary ")". 23 | */ 24 | final class CountChildrenFunction extends FunctionNode 25 | { 26 | public $stringPrimary; 27 | public $field; 28 | public $column; 29 | 30 | public function getSql(SqlWalker $sqlWalker): string 31 | { 32 | $table = $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary); 33 | $field = $sqlWalker->walkSimpleArithmeticExpression($this->field); 34 | $column = $sqlWalker->walkSimpleArithmeticExpression($this->column); 35 | 36 | return '(SELECT COUNT(*) FROM ' . $table . ' WHERE ' . $field . " = {$column})"; 37 | } 38 | 39 | public function parse(Parser $parser): void 40 | { 41 | $parser->match(TokenType::T_IDENTIFIER); 42 | $parser->match(TokenType::T_OPEN_PARENTHESIS); 43 | 44 | $this->stringPrimary = $parser->StringPrimary(); 45 | 46 | $parser->match(TokenType::T_COMMA); 47 | 48 | $this->field = $parser->StringPrimary(); 49 | $parser->match(TokenType::T_COMMA); 50 | 51 | $this->column = $parser->StringPrimary(); 52 | 53 | $parser->match(TokenType::T_CLOSE_PARENTHESIS); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Functions/MoneyCastFunction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm\Extension\Functions; 15 | 16 | use Doctrine\ORM\Query\Parser; 17 | use Doctrine\ORM\Query\SqlWalker; 18 | use Doctrine\ORM\Query\TokenType; 19 | 20 | /** 21 | * "SEARCH_MONEY_AS_NUMERIC" "(" StringPrimary ", " Literal ")". 22 | */ 23 | final class MoneyCastFunction extends PlatformSpecificFunction 24 | { 25 | public $stringPrimary; 26 | 27 | /** 28 | * @var int 29 | */ 30 | public $scale; 31 | 32 | public function getSql(SqlWalker $sqlWalker): string 33 | { 34 | $connection = $sqlWalker->getConnection(); 35 | 36 | $expression = $sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary); 37 | $scale = $this->scale; 38 | 39 | if ($this->getPlatformName($connection) === 'mysql') { 40 | $castType = "DECIMAL(10, {$scale})"; 41 | } else { 42 | $castType = $connection->getDatabasePlatform()->getDecimalTypeDeclarationSQL( 43 | ['scale' => $scale, 'precision' => 10, 'name' => $expression] 44 | ); 45 | } 46 | 47 | return \sprintf('CAST(%s AS %s)', $expression, $castType); 48 | } 49 | 50 | public function parse(Parser $parser): void 51 | { 52 | $parser->match(TokenType::T_IDENTIFIER); 53 | $parser->match(TokenType::T_OPEN_PARENTHESIS); 54 | 55 | $this->stringPrimary = $parser->StringPrimary(); 56 | 57 | $parser->match(TokenType::T_COMMA); 58 | 59 | $this->scale = (int) $parser->Literal()->value; 60 | 61 | $parser->match(TokenType::T_CLOSE_PARENTHESIS); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Functions/PlatformSpecificFunction.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm\Extension\Functions; 15 | 16 | use Doctrine\DBAL\Connection; 17 | use Doctrine\ORM\Query\AST\Functions\FunctionNode; 18 | 19 | /** 20 | * Extend this class class for platform specific functionality. 21 | */ 22 | abstract class PlatformSpecificFunction extends FunctionNode 23 | { 24 | /** 25 | * Returns the platform name, returns a class-name if the platform could not be detected. 26 | * 27 | * @return 'mysql'|'sqlite'|'pgsql'|'oci'|'sqlsrv'|'mock'|string 28 | */ 29 | protected function getPlatformName(Connection $connection): string 30 | { 31 | $platform = $connection->getDatabasePlatform(); 32 | 33 | return match (true) { 34 | $platform instanceof \Doctrine\DBAL\Platforms\AbstractMySQLPlatform => 'mysql', 35 | $platform instanceof \Doctrine\DBAL\Platforms\SQLitePlatform => 'sqlite', 36 | $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform => 'pgsql', 37 | $platform instanceof \Doctrine\DBAL\Platforms\OraclePlatform => 'oci', 38 | $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform => 'sqlsrv', 39 | $platform instanceof \Rollerworks\Component\Search\Tests\Doctrine\Dbal\Mocks\DatabasePlatformMock => 'mock', 40 | default => $platform::class, 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Type/BirthdayTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\BirthdayType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Orm\Conversion\AgeDateConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | final class BirthdayTypeExtension extends AbstractFieldTypeExtension 22 | { 23 | /** @var AgeDateConversion */ 24 | private $conversion; 25 | 26 | public function __construct() 27 | { 28 | $this->conversion = new AgeDateConversion(); 29 | } 30 | 31 | public function configureOptions(OptionsResolver $resolver): void 32 | { 33 | $resolver->setDefault('doctrine_orm_conversion', $this->conversion); 34 | } 35 | 36 | public function getExtendedType(): string 37 | { 38 | return BirthdayType::class; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Type/ChildCountType.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Doctrine\Dbal\Type\ChildCountType as DbalChildCountType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Orm\Conversion\ChildCountConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | class ChildCountType extends AbstractFieldTypeExtension 22 | { 23 | private $conversion; 24 | 25 | public function __construct() 26 | { 27 | $this->conversion = new ChildCountConversion(); 28 | } 29 | 30 | public function configureOptions(OptionsResolver $resolver): void 31 | { 32 | $resolver->setDefault('doctrine_orm_conversion', $this->conversion); 33 | } 34 | 35 | public function getExtendedType(): string 36 | { 37 | return DbalChildCountType::class; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Type/DateTimeTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\DateTimeType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Orm\Conversion\DateIntervalConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | final class DateTimeTypeExtension extends AbstractFieldTypeExtension 22 | { 23 | /** @var DateIntervalConversion */ 24 | private $conversion; 25 | 26 | public function __construct() 27 | { 28 | $this->conversion = new DateIntervalConversion(); 29 | } 30 | 31 | public function configureOptions(OptionsResolver $resolver): void 32 | { 33 | $resolver->setDefault('doctrine_orm_conversion', $this->conversion); 34 | } 35 | 36 | public function getExtendedType(): string 37 | { 38 | return DateTimeType::class; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Type/FieldTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Type; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Orm\ColumnConversion; 17 | use Rollerworks\Component\Search\Doctrine\Orm\ValueConversion; 18 | use Rollerworks\Component\Search\Extension\Core\Type\SearchFieldType; 19 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 20 | use Symfony\Component\OptionsResolver\OptionsResolver; 21 | 22 | class FieldTypeExtension extends AbstractFieldTypeExtension 23 | { 24 | public function configureOptions(OptionsResolver $resolver): void 25 | { 26 | $resolver->setDefaults(['doctrine_orm_conversion' => null]); 27 | $resolver->setAllowedTypes( 28 | 'doctrine_orm_conversion', 29 | [ 30 | 'null', 31 | \Closure::class, 32 | ColumnConversion::class, 33 | ValueConversion::class, 34 | ] 35 | ); 36 | } 37 | 38 | public function getExtendedType(): string 39 | { 40 | return SearchFieldType::class; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/Extension/Type/MoneyTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Doctrine\Orm\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\MoneyType; 17 | use Rollerworks\Component\Search\Extension\Doctrine\Orm\Conversion\MoneyValueConversion; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | final class MoneyTypeExtension extends AbstractFieldTypeExtension 22 | { 23 | /** 24 | * @var MoneyValueConversion 25 | */ 26 | private $conversion; 27 | 28 | public function __construct() 29 | { 30 | $this->conversion = new MoneyValueConversion(); 31 | } 32 | 33 | public function configureOptions(OptionsResolver $resolver): void 34 | { 35 | $resolver->setDefault('doctrine_orm_conversion', $this->conversion); 36 | } 37 | 38 | public function getExtendedType(): string 39 | { 40 | return MoneyType::class; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/OrmQueryField.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\Query\QueryField; 17 | use Rollerworks\Component\Search\Field\FieldConfig; 18 | 19 | /** 20 | * @property ColumnConversion|null $columnConversion 21 | * @property ValueConversion|null $valueConversion 22 | */ 23 | final class OrmQueryField extends QueryField 24 | { 25 | public string $entity; 26 | 27 | public function __construct(string $mappingName, FieldConfig $fieldConfig, string $dbType, string $column, string $alias, string $entity) 28 | { 29 | parent::__construct( 30 | $mappingName, 31 | $fieldConfig, 32 | $dbType, 33 | $column, 34 | $alias 35 | ); 36 | 37 | $this->entity = $entity; 38 | } 39 | 40 | protected function initConversions(FieldConfig $fieldConfig): void 41 | { 42 | $converter = $fieldConfig->getOption('doctrine_orm_conversion'); 43 | 44 | if ($converter instanceof \Closure) { 45 | $converter = $converter(); 46 | } 47 | 48 | if ($converter instanceof ColumnConversion) { 49 | $this->columnConversion = $converter; 50 | } 51 | 52 | if ($converter instanceof ValueConversion) { 53 | $this->valueConversion = $converter; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/README.md: -------------------------------------------------------------------------------- 1 | RollerworksSearch Doctrine ORM extension 2 | ======================================== 3 | 4 | This package provides the Doctrine ORM extension for [RollerworksSearch][1]. 5 | 6 | If you'd like to contribute to this project, please see the [contributing guide lines][2] 7 | for more information. 8 | 9 | Installation 10 | ------------ 11 | 12 | To install this extension, add the `search-doctrine-orm` to your composer.json 13 | 14 | ```bash 15 | $ php composer.phar require rollerworks/search-doctrine-orm 16 | ``` 17 | 18 | License 19 | ------- 20 | 21 | The source of this package is subject to the MIT license that is bundled 22 | with this source code in the file [LICENSE](LICENSE). 23 | 24 | [1]: https://github.com/rollerworks/RollerworksSearch 25 | [2]: https://github.com/rollerworks/RollerworksSearch#contributing 26 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/ValueConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Doctrine\Orm; 15 | 16 | use Rollerworks\Component\Search\Doctrine\Dbal\ConversionHints; 17 | 18 | /** 19 | * A ValueConversion allows to convert the value "model" to a valid 20 | * DQL statement to be used a column value. 21 | * 22 | * @author Sebastiaan Stok 23 | */ 24 | interface ValueConversion 25 | { 26 | /** 27 | * Returns the converted value as an DQL statement. 28 | * 29 | * The returned result must a be a valid DQL statement that can be used 30 | * as a column's value. 31 | * 32 | * When using custom functions these need to be registered before usage. 33 | * 34 | * Used values must be registered as parameters using `$hints->createParamReferenceFor($value)` 35 | * with an option DBAL Type as second argument (converted afterwards). 36 | * 37 | * @param mixed $value The "model" value format 38 | * @param array $options Options of the Field configuration 39 | * @param ConversionHints $hints Special information for the conversion process 40 | */ 41 | public function convertValue($value, array $options, ConversionHints $hints): string; 42 | } 43 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollerworks/search-doctrine-orm", 3 | "description": "Doctrine ORM extension for RollerworksSearch", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "rollerworks", 8 | "search", 9 | "doctrine", 10 | "orm" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Sebastiaan Stok", 15 | "email": "s.stok@rollercapes.net" 16 | }, 17 | { 18 | "name": "Community contributions", 19 | "homepage": "https://github.com/Rollerworks/rollerworks-search-doctrine-orm/contributors" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=8.1", 24 | "doctrine/orm": "^2.6 || ^3.3", 25 | "rollerworks/search": ">=2.0.0-BETA1 ^2.0@dev", 26 | "rollerworks/search-doctrine-dbal": ">=2.0.0-BETA9 ^2.0@dev" 27 | }, 28 | "require-dev": { 29 | "moneyphp/money": "^3.0.7 || ^4.0", 30 | "phpunit/phpunit": "^6.5.4", 31 | "symfony/phpunit-bridge": "^6.4 || ^7.0", 32 | "symfony/var-dumper": "^6.4 || ^7.0" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Rollerworks\\Component\\Search\\Doctrine\\Orm\\": "", 37 | "Rollerworks\\Component\\Search\\Extension\\Doctrine\\Orm\\": "Extension/" 38 | }, 39 | "exclude-from-classmap": [ 40 | "Tests/" 41 | ] 42 | }, 43 | "autoload-dev": { 44 | "psr-4": { 45 | "Rollerworks\\Component\\Search\\Tests\\Doctrine\\Dbal\\": "vendor/rollerworks/search-doctrine-dbal/tests/", 46 | "Rollerworks\\Component\\Search\\Tests\\Doctrine\\Orm\\": "Tests/" 47 | } 48 | }, 49 | "config": { 50 | "preferred-install": { 51 | "rollerworks/search-doctrine-dbal": "source", 52 | "*": "dist" 53 | }, 54 | "sort-packages": true 55 | }, 56 | "extra": { 57 | "branch-alias": { 58 | "dev-main": "2.0-dev" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/Doctrine/Orm/mysql.travis.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ./tests/ 38 | 39 | 40 | 41 | 42 | 43 | ./src 44 | 45 | ./vendor/ 46 | ./tests/ 47 | ./doc/ 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /lib/Elasticsearch/ChildOrderConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch; 15 | 16 | /** 17 | * The ChildOrderConversion converts a has_child QueryScript 18 | * to an usable format, for example dates are not natively 19 | * supported for sorting, and need to be converted milliseconds. 20 | * 21 | * Caution: This must not be combined with other Elasticsearch conversions. 22 | */ 23 | interface ChildOrderConversion 24 | { 25 | /** 26 | * Returns the query-script in transformed format. 27 | * 28 | * @param string $script either doc["field-name"].value 29 | * 30 | * @return string either doc["field-name"].value.millis 31 | */ 32 | public function convert(string $property, string $script): string; 33 | } 34 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Conversion/CurrencyConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Conversion; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\ValueConversion; 17 | use Rollerworks\Component\Search\Extension\Core\Model\MoneyValue; 18 | 19 | class CurrencyConversion implements ValueConversion 20 | { 21 | /** 22 | * Returns the converted value as a valid Elasticsearch value. 23 | * 24 | * @param MoneyValue $value 25 | */ 26 | public function convertValue($value): string 27 | { 28 | return $value->value->getAmount(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Conversion/DateChildOrderConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Conversion; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\ChildOrderConversion; 17 | 18 | final class DateChildOrderConversion implements ChildOrderConversion 19 | { 20 | public function convert(string $property, string $script): string 21 | { 22 | return \sprintf('%s.millis', $script); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/ElasticsearchExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension; 15 | 16 | use Rollerworks\Component\Search\AbstractExtension; 17 | 18 | class ElasticsearchExtension extends AbstractExtension 19 | { 20 | protected function loadTypesExtensions(): array 21 | { 22 | return [ 23 | new Type\FieldTypeExtension(), 24 | new Type\OrderTypeExtension(), 25 | new Type\DateTypeExtension(new Conversion\DateConversion()), 26 | new Type\DateTimeTypeExtension(new Conversion\DateTimeConversion()), 27 | new Type\MoneyTypeExtension(new Conversion\CurrencyConversion()), 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Type/BirthdayTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Type; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\Extension\Conversion\DateChildOrderConversion; 17 | use Rollerworks\Component\Search\Elasticsearch\Extension\Conversion\DateConversion; 18 | use Rollerworks\Component\Search\Extension\Core\Type\BirthdayType; 19 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 20 | use Symfony\Component\OptionsResolver\OptionsResolver; 21 | 22 | class BirthdayTypeExtension extends AbstractFieldTypeExtension 23 | { 24 | /** 25 | * @var DateConversion 26 | */ 27 | private $conversion; 28 | 29 | public function __construct(DateConversion $conversion) 30 | { 31 | $this->conversion = $conversion; 32 | } 33 | 34 | public function configureOptions(OptionsResolver $resolver): void 35 | { 36 | $resolver->setDefaults( 37 | [ 38 | 'elasticsearch_conversion' => $this->conversion, 39 | 'elasticsearch_child_order_conversion' => new DateChildOrderConversion(), 40 | ] 41 | ); 42 | } 43 | 44 | /** 45 | * Returns the name of the type being extended. 46 | */ 47 | public function getExtendedType(): string 48 | { 49 | return BirthdayType::class; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Type/DateTimeTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Type; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\Extension\Conversion\DateChildOrderConversion; 17 | use Rollerworks\Component\Search\Elasticsearch\Extension\Conversion\DateTimeConversion; 18 | use Rollerworks\Component\Search\Extension\Core\Type\DateTimeType; 19 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 20 | use Symfony\Component\OptionsResolver\OptionsResolver; 21 | 22 | class DateTimeTypeExtension extends AbstractFieldTypeExtension 23 | { 24 | /** 25 | * @var DateTimeConversion 26 | */ 27 | private $conversion; 28 | 29 | public function __construct(DateTimeConversion $conversion) 30 | { 31 | $this->conversion = $conversion; 32 | } 33 | 34 | public function configureOptions(OptionsResolver $resolver): void 35 | { 36 | $resolver->setDefaults( 37 | [ 38 | 'elasticsearch_conversion' => $this->conversion, 39 | 'elasticsearch_child_order_conversion' => new DateChildOrderConversion(), 40 | ] 41 | ); 42 | } 43 | 44 | public function getExtendedType(): string 45 | { 46 | return DateTimeType::class; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Type/DateTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Type; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\Extension\Conversion\DateChildOrderConversion; 17 | use Rollerworks\Component\Search\Elasticsearch\Extension\Conversion\DateConversion; 18 | use Rollerworks\Component\Search\Extension\Core\Type\DateType; 19 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 20 | use Symfony\Component\OptionsResolver\OptionsResolver; 21 | 22 | class DateTypeExtension extends AbstractFieldTypeExtension 23 | { 24 | /** 25 | * @var DateConversion 26 | */ 27 | private $conversion; 28 | 29 | public function __construct(DateConversion $conversion) 30 | { 31 | $this->conversion = $conversion; 32 | } 33 | 34 | public function configureOptions(OptionsResolver $resolver): void 35 | { 36 | $resolver->setDefaults( 37 | [ 38 | 'elasticsearch_conversion' => $this->conversion, 39 | 'elasticsearch_child_order_conversion' => new DateChildOrderConversion(), 40 | ] 41 | ); 42 | } 43 | 44 | public function getExtendedType(): string 45 | { 46 | return DateType::class; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Type/FieldTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Type; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\ChildOrderConversion; 17 | use Rollerworks\Component\Search\Elasticsearch\QueryConversion; 18 | use Rollerworks\Component\Search\Elasticsearch\ValueConversion; 19 | use Rollerworks\Component\Search\Extension\Core\Type\SearchFieldType; 20 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 21 | use Symfony\Component\OptionsResolver\OptionsResolver; 22 | 23 | class FieldTypeExtension extends AbstractFieldTypeExtension 24 | { 25 | public function configureOptions(OptionsResolver $resolver): void 26 | { 27 | $resolver 28 | ->setDefaults([ 29 | 'elasticsearch_child_order_conversion' => null, 30 | 'elasticsearch_conversion' => null, 31 | ]) 32 | ->setAllowedTypes('elasticsearch_child_order_conversion', ['null', ChildOrderConversion::class]) 33 | ->setAllowedTypes( 34 | 'elasticsearch_conversion', 35 | [ 36 | 'null', 37 | ValueConversion::class, 38 | QueryConversion::class, 39 | ] 40 | ) 41 | ; 42 | } 43 | 44 | public function getExtendedType(): string 45 | { 46 | return SearchFieldType::class; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Type/MoneyTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Type; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\Extension\Conversion\CurrencyConversion; 17 | use Rollerworks\Component\Search\Extension\Core\Type\MoneyType; 18 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | class MoneyTypeExtension extends AbstractFieldTypeExtension 22 | { 23 | /** 24 | * @var CurrencyConversion 25 | */ 26 | private $conversion; 27 | 28 | public function __construct(CurrencyConversion $conversion) 29 | { 30 | $this->conversion = $conversion; 31 | } 32 | 33 | public function configureOptions(OptionsResolver $resolver): void 34 | { 35 | $resolver->setDefaults( 36 | [ 37 | 'elasticsearch_conversion' => $this->conversion, 38 | ] 39 | ); 40 | } 41 | 42 | public function getExtendedType(): string 43 | { 44 | return MoneyType::class; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/Elasticsearch/Extension/Type/OrderTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch\Extension\Type; 15 | 16 | use Rollerworks\Component\Search\Elasticsearch\ChildOrderConversion; 17 | use Rollerworks\Component\Search\Elasticsearch\QueryConversion; 18 | use Rollerworks\Component\Search\Elasticsearch\ValueConversion; 19 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 20 | use Rollerworks\Component\Search\Field\OrderFieldType; 21 | use Symfony\Component\OptionsResolver\Options; 22 | use Symfony\Component\OptionsResolver\OptionsResolver; 23 | 24 | class OrderTypeExtension extends AbstractFieldTypeExtension 25 | { 26 | public function configureOptions(OptionsResolver $resolver): void 27 | { 28 | $resolver 29 | ->setDefaults([ 30 | 'elasticsearch_child_order_conversion' => static fn (Options $options) => $options['type_options']['elasticsearch_child_order_conversion'] ?? null, 31 | 'elasticsearch_conversion' => null, 32 | ]) 33 | ->setAllowedTypes('elasticsearch_child_order_conversion', ['null', ChildOrderConversion::class]) 34 | ->setAllowedTypes( 35 | 'elasticsearch_conversion', 36 | [ 37 | 'null', 38 | ValueConversion::class, 39 | QueryConversion::class, 40 | ] 41 | ) 42 | ; 43 | } 44 | 45 | public function getExtendedType(): string 46 | { 47 | return OrderFieldType::class; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/Elasticsearch/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/Elasticsearch/QueryConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch; 15 | 16 | /** 17 | * Class QueryConversion. 18 | */ 19 | interface QueryConversion 20 | { 21 | /** 22 | * Returns the query converted to a new form if required. 23 | * 24 | * If null is returned, no query conversion has taken place, 25 | * use whatever is proper in the current context. 26 | */ 27 | public function convertQuery(string $propertyName, $value, QueryPreparationHints $hints): ?array; 28 | } 29 | -------------------------------------------------------------------------------- /lib/Elasticsearch/QueryPreparationHints.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch; 15 | 16 | class QueryPreparationHints 17 | { 18 | public const CONTEXT_PRECONDITION_VALUE = 'PRECONDITION_VALUE'; 19 | public const CONTEXT_PRECONDITION_QUERY = 'PRECONDITION_QUERY'; 20 | public const CONTEXT_SIMPLE_VALUES = 'SIMPLE_VALUES'; 21 | public const CONTEXT_EXCLUDED_SIMPLE_VALUES = 'EXCLUDED_SIMPLE_VALUES'; 22 | public const CONTEXT_RANGE_VALUES = 'RANGE_VALUES'; 23 | public const CONTEXT_EXCLUDED_RANGE_VALUES = 'EXCLUDED_RANGE_VALUES'; 24 | public const CONTEXT_COMPARISON = 'COMPARISON'; 25 | public const CONTEXT_PATTERN_MATCH = 'PATTERN_MATCH'; 26 | public const CONTEXT_ORDER = 'ORDER'; 27 | 28 | /** @var bool */ 29 | public $identifier = false; 30 | 31 | /** 32 | * @var string Preparation context, one of ConversionHints::CONTEXT_* constants 33 | */ 34 | public $context; 35 | } 36 | -------------------------------------------------------------------------------- /lib/Elasticsearch/README.md: -------------------------------------------------------------------------------- 1 | RollerworksSearch Elasticsearch extension 2 | ========================================= 3 | 4 | This package provides the Elasticsearch extension for [RollerworksSearch][1]. 5 | 6 | **Note:** 7 | 8 | > This extension is only about generating a search Query for Elasticsearch, 9 | you need to set-up indexing and storage yourself. 10 | 11 | If you'd like to contribute to this project, please see the [contributing guide lines][2] 12 | for more information. 13 | 14 | Installation 15 | ------------ 16 | 17 | To install this extension, add the `rollerworks/search-elasticsearch` to your composer.json 18 | 19 | ```bash 20 | $ php composer.phar require rollerworks/search-elasticsearch 21 | ``` 22 | 23 | License 24 | ------- 25 | 26 | The source of this package is subject to the MIT license that is bundled 27 | with this source code in the file [LICENSE](LICENSE). 28 | 29 | [1]: https://github.com/rollerworks/search 30 | [2]: https://github.com/rollerworks/search#contributing 31 | -------------------------------------------------------------------------------- /lib/Elasticsearch/ValueConversion.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Elasticsearch; 15 | 16 | interface ValueConversion 17 | { 18 | /** 19 | * Returns the converted value as a valid Elasticsearch value. 20 | */ 21 | public function convertValue($value); 22 | } 23 | -------------------------------------------------------------------------------- /lib/Elasticsearch/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollerworks/search-elasticsearch", 3 | "description": "ElasticSearch (Elastica) extension for RollerworksSearch", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "rollerworks", 8 | "search", 9 | "doctrine", 10 | "dbal" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Sebastiaan Stok", 15 | "email": "s.stok@rollercapes.net" 16 | }, 17 | { 18 | "name": "Community contributions", 19 | "homepage": "https://github.com/Rollerworks/search-elasticsearch/contributors" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=8.1", 24 | "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", 25 | "rollerworks/search": ">=2.0.0-BETA1 ^2.0@dev", 26 | "ruflin/elastica": "^5.0 || ^6.0" 27 | }, 28 | "require-dev": { 29 | "moneyphp/money": "^3.0", 30 | "symfony/phpunit-bridge": "^6.4 || ^7.0", 31 | "symfony/var-dumper": "^6.4 || ^7.0" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Rollerworks\\Component\\Search\\Elasticsearch\\": "" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Rollerworks\\Component\\Search\\Tests\\Elasticsearch\\": "Tests/" 41 | } 42 | }, 43 | "extra": { 44 | "branch-alias": { 45 | "dev-main": "2.0-dev" 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/DependencyInjection/Compiler/ElasticaClientPass.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Bundle\SearchBundle\DependencyInjection\Compiler; 15 | 16 | use Elastica\Client as ElasticaClient; 17 | use JoliCode\Elastically\Client as ElasticallyClient; 18 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 19 | use Symfony\Component\DependencyInjection\ContainerBuilder; 20 | 21 | class ElasticaClientPass implements CompilerPassInterface 22 | { 23 | public function process(ContainerBuilder $container): void 24 | { 25 | if ($container->has('fos_elastica.client')) { 26 | $container->setAlias('rollerworks_search.elasticsearch.client', 'fos_elastica.client'); 27 | 28 | return; 29 | } 30 | 31 | if ($container->has(ElasticaClient::class)) { 32 | $container->setAlias('rollerworks_search.elasticsearch.client', ElasticaClient::class); 33 | 34 | return; 35 | } 36 | 37 | if ($container->has(ElasticallyClient::class)) { 38 | $container->setAlias('rollerworks_search.elasticsearch.client', ElasticallyClient::class); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/DependencyInjection/Compiler/FieldSetRegistryPass.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Bundle\SearchBundle\DependencyInjection\Compiler; 15 | 16 | use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; 17 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 18 | use Symfony\Component\DependencyInjection\ContainerBuilder; 19 | use Symfony\Component\DependencyInjection\Definition; 20 | use Symfony\Component\DependencyInjection\Reference; 21 | use Symfony\Component\DependencyInjection\ServiceLocator; 22 | 23 | /** 24 | * Compiler pass to register tagged FieldSet's for the FieldSetRegistry. 25 | * 26 | * @author Sebastiaan Stok 27 | */ 28 | final class FieldSetRegistryPass implements CompilerPassInterface 29 | { 30 | public function process(ContainerBuilder $container): void 31 | { 32 | if (! $container->hasDefinition('rollerworks_search.fieldset_registry')) { 33 | return; 34 | } 35 | 36 | $fieldSetServices = []; 37 | $fieldSetServiceIds = []; 38 | 39 | foreach ($container->findTaggedServiceIds('rollerworks_search.fieldset') as $serviceId => $tag) { 40 | $class = $container->findDefinition($serviceId)->getClass(); 41 | 42 | $fieldSetServices[$class] = new ServiceClosureArgument(new Reference($serviceId)); 43 | $fieldSetServiceIds[$class] = $serviceId; 44 | } 45 | 46 | $definition = $container->getDefinition('rollerworks_search.fieldset_registry'); 47 | $definition->replaceArgument(0, (new Definition(ServiceLocator::class, [$fieldSetServices]))->addTag('container.service_locator')); 48 | $definition->replaceArgument(1, $fieldSetServiceIds); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/README.md: -------------------------------------------------------------------------------- 1 | RollerworksSearchBundle 2 | ======================= 3 | 4 | This package provides the Symfony integration bundle for [RollerworksSearch][1]. 5 | 6 | If you'd like to contribute to this project, please see the [contributing guide lines][2] 7 | for more information. 8 | 9 | License 10 | ------- 11 | 12 | The source of this package is subject to the MIT license that is bundled 13 | with this source code in the file [LICENSE](LICENSE). 14 | 15 | [1]: https://github.com/rollerworks/RollerworksSearch 16 | [2]: https://github.com/rollerworks/RollerworksSearch#contributing 17 | 18 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/api_platform_doctrine_orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/api_platform_elasticsearch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/condition_exporter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/doctrine_dbal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/doctrine_orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/input_processor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/input_validator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Resources/config/translator_alias_resolver.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/RollerworksSearchBundle.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Bundle\SearchBundle; 15 | 16 | use Rollerworks\Bundle\SearchBundle\DependencyInjection\Compiler; 17 | use Symfony\Component\DependencyInjection\ContainerBuilder; 18 | use Symfony\Component\HttpKernel\Bundle\Bundle; 19 | 20 | class RollerworksSearchBundle extends Bundle 21 | { 22 | public function build(ContainerBuilder $container): void 23 | { 24 | $container->addCompilerPass(new Compiler\ExtensionPass()); 25 | $container->addCompilerPass(new Compiler\InputProcessorPass()); 26 | $container->addCompilerPass(new Compiler\ExporterPass()); 27 | $container->addCompilerPass(new Compiler\FieldSetRegistryPass()); 28 | $container->addCompilerPass(new Compiler\DoctrineOrmPass()); 29 | $container->addCompilerPass(new Compiler\ElasticaClientPass()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/TranslatorBasedAliasResolver.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Bundle\SearchBundle; 15 | 16 | use Rollerworks\Component\Search\Field\FieldConfig; 17 | use Rollerworks\Component\Search\Field\OrderField; 18 | use Symfony\Contracts\Translation\TranslatableInterface; 19 | use Symfony\Contracts\Translation\TranslatorInterface; 20 | 21 | final class TranslatorBasedAliasResolver 22 | { 23 | public function __construct(private TranslatorInterface $translator) 24 | { 25 | } 26 | 27 | public function __invoke(FieldConfig $field): string 28 | { 29 | $label = $field->getOption('label'); 30 | 31 | if ($label === null) { 32 | return $field->getName(); 33 | } 34 | 35 | if ($label instanceof TranslatableInterface) { 36 | $label = $label->trans($this->translator); 37 | } 38 | 39 | if ($field instanceof OrderField && $label[0] !== '@') { 40 | $label = '@' . $label; 41 | } 42 | 43 | return $label; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Type/TranslatableFieldTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Bundle\SearchBundle\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\SearchFieldType; 17 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 18 | use Symfony\Component\OptionsResolver\OptionsResolver; 19 | use Symfony\Contracts\Translation\TranslatableInterface; 20 | 21 | final class TranslatableFieldTypeExtension extends AbstractFieldTypeExtension 22 | { 23 | public function configureOptions(OptionsResolver $resolver): void 24 | { 25 | $resolver->addAllowedTypes('label', TranslatableInterface::class); 26 | } 27 | 28 | public function getExtendedType(): string 29 | { 30 | return SearchFieldType::class; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/Symfony/SearchBundle/Type/TranslatableOrderFieldTypeExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Bundle\SearchBundle\Type; 15 | 16 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 17 | use Rollerworks\Component\Search\Field\OrderFieldType; 18 | use Symfony\Component\OptionsResolver\OptionsResolver; 19 | use Symfony\Contracts\Translation\TranslatableInterface; 20 | 21 | final class TranslatableOrderFieldTypeExtension extends AbstractFieldTypeExtension 22 | { 23 | public function configureOptions(OptionsResolver $resolver): void 24 | { 25 | $resolver->addAllowedTypes('label', TranslatableInterface::class); 26 | } 27 | 28 | public function getExtendedType(): string 29 | { 30 | return OrderFieldType::class; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/Symfony/Validator/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-present Sebastiaan Stok 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/Symfony/Validator/README.md: -------------------------------------------------------------------------------- 1 | RollerworksSearch Symfony Validator 2 | =================================== 3 | 4 | This package provides the Symfony Validator extension for [RollerworksSearch][1]. 5 | 6 | If you'd like to contribute to this project, please see the [contributing guide lines][2] 7 | for more information. 8 | 9 | **Note:** 10 | 11 | > This validation is meant to be used for business rules like a minimum/maximum 12 | > value range or disallowing specific patterns. The data transformers already ensure 13 | > the value is properly transformed. 14 | 15 | Installation 16 | ------------ 17 | 18 | To install this extension, add the `rollerworks/search-symfony-validator` 19 | package in your composer.json and update your dependencies. 20 | 21 | ```bash 22 | $ composer require rollerworks/search-symfony-validator 23 | ``` 24 | 25 | Symfony framework integration 26 | ----------------------------- 27 | 28 | **Note:** The Symfony integration bundle for RollerworksSearch already enables 29 | the Symfony validator service. You don't need to do anything but configure your 30 | field's constraints. 31 | 32 | This package provides the Symfony integration bundle for [RollerworksSearch][1]. 33 | 34 | License 35 | ------- 36 | 37 | The source of this package is subject to the MIT license that is bundled 38 | with this source code in the file [LICENSE](LICENSE). 39 | 40 | [1]: https://github.com/rollerworks/RollerworksSearch 41 | [2]: https://github.com/rollerworks/RollerworksSearch#contributing 42 | -------------------------------------------------------------------------------- /lib/Symfony/Validator/Type/FieldTypeValidatorExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Symfony\Validator\Type; 15 | 16 | use Rollerworks\Component\Search\Extension\Core\Type\SearchFieldType; 17 | use Rollerworks\Component\Search\Field\AbstractFieldTypeExtension; 18 | use Symfony\Component\OptionsResolver\Options; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | /** 22 | * @author Sebastiaan Stok 23 | */ 24 | final class FieldTypeValidatorExtension extends AbstractFieldTypeExtension 25 | { 26 | public function getExtendedType(): string 27 | { 28 | return SearchFieldType::class; 29 | } 30 | 31 | public function configureOptions(OptionsResolver $resolver): void 32 | { 33 | // Constraint should always be converted to an array 34 | $constraintsNormalizer = static fn (Options $options, $constraints) => \is_object($constraints) ? [$constraints] : (array) $constraints; 35 | 36 | $resolver->setDefault('constraints', []); 37 | $resolver->setDefault('pattern_match_constraints', []); 38 | $resolver->setNormalizer('constraints', $constraintsNormalizer); 39 | $resolver->setNormalizer('pattern_match_constraints', $constraintsNormalizer); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Symfony/Validator/ValidatorExtension.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * This source file is subject to the MIT license that is bundled 11 | * with this source code in the file LICENSE. 12 | */ 13 | 14 | namespace Rollerworks\Component\Search\Extension\Symfony\Validator; 15 | 16 | use Rollerworks\Component\Search\AbstractExtension; 17 | 18 | /** 19 | * @author Sebastiaan Stok 20 | */ 21 | final class ValidatorExtension extends AbstractExtension 22 | { 23 | protected function loadTypesExtensions(): array 24 | { 25 | return [ 26 | new Type\FieldTypeValidatorExtension(), 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Symfony/Validator/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollerworks/search-symfony-validator", 3 | "description": "Symfony validator extension for RollerworksSearch", 4 | "license": "MIT", 5 | "type": "library", 6 | "keywords": [ 7 | "rollerworks", 8 | "search", 9 | "validation", 10 | "symfony" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "Sebastiaan Stok", 15 | "email": "s.stok@rollercapes.net" 16 | }, 17 | { 18 | "name": "Community contributions", 19 | "homepage": "https://github.com/Rollerworks/search-symfony-validator/contributors" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=8.1", 24 | "rollerworks/search": ">=2.0.0-BETA3 ^2.0@dev", 25 | "symfony/validator": "^6.4 || ^7.0" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^6.5.4", 29 | "symfony/phpunit-bridge": "^6.4 || ^7.0" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "Rollerworks\\Component\\Search\\Extension\\Symfony\\Validator\\": "" 34 | }, 35 | "exclude-from-classmap": [ 36 | "test/" 37 | ] 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Rollerworks\\Component\\Search\\Extension\\Symfony\\Validator\\Tests\\": "Tests/" 42 | } 43 | }, 44 | "extra": { 45 | "branch-alias": { 46 | "dev-main": "2.0-dev" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /phpunit/mysql.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ../lib/*/Tests/ 34 | ../lib/*/*/Tests/ 35 | ../lib/Doctrine/Dbal/Tests/ 36 | ../lib/Doctrine/Orm/Tests/ 37 | 38 | 39 | 40 | 41 | 42 | ../lib 43 | 44 | 45 | ../vendor/ 46 | ../lib/*/Tests/ 47 | ../lib/*/*/Tests/ 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /phpunit/pgsql.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ../lib/*/Tests/ 34 | ../lib/*/*/Tests/ 35 | ../lib/Doctrine/Dbal/Tests/ 36 | ../lib/Doctrine/Orm/Tests/ 37 | 38 | 39 | 40 | 41 | 42 | ../lib 43 | 44 | 45 | ../vendor/ 46 | ../lib/*/Tests/ 47 | ../lib/*/*/Tests/ 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | --------------------------------------------------------------------------------