diff --git a/spec/Doctrine/Orm/Filter/AttributeFilterSpec.php b/spec/Doctrine/Orm/Filter/AttributeFilterSpec.php new file mode 100644 index 00000000..3d0519a3 --- /dev/null +++ b/spec/Doctrine/Orm/Filter/AttributeFilterSpec.php @@ -0,0 +1,272 @@ +iriConverter = $iriConverter; + } + + /** + * @param mixed $value + */ + protected function filterProperty( + string $property, + $value, + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + string $operationName = null + ): void { + if ( + !\is_array($value) || + !$this->isPropertyEnabled($property, $resourceClass) + ) { + return; + } + + $attributeId = $this->getAttributeId($value, $property); + $extractedValue = $this->extractValue($value, $property); + if (null === $attributeId || null === $extractedValue) { + return; + } + + $alias = $queryBuilder->getRootAliases()[0]; + + if ($this->isPropertyNested($property . '.id', $resourceClass)) { + [$alias] = $this->addJoinsForNestedProperty($property . '.id', $alias, $queryBuilder, $queryNameGenerator, $resourceClass); + } + + Assert::string($alias); + + $this->addWhere( + $queryBuilder, + $queryNameGenerator, + $alias, + $property, + $attributeId, + $extractedValue + ); + } + + /** @param mixed $value */ + protected function addWhere( + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $alias, + string $field, + string $attributeId, + $value, + string $operator = self::OPERATOR_EXACT + ): void { + $valueParameter = $queryNameGenerator->generateParameterName($field); + + /** @var AttributeInterface $attribute */ + $attribute = $this->iriConverter->getItemFromIri($attributeId); + $attributeType = $attribute->getType(); + Assert::notNull($attributeType); + $value = $this->normalizeValue($value, $attributeType); + + switch ($operator) { + case self::OPERATOR_EXACT: + $queryBuilder + ->andWhere( + sprintf( + '%s.%s = :%s', + $alias, + $attributeType, + $valueParameter + ) + ) + ->setParameter($valueParameter, $value); + + break; + case self::OPERATOR_PARTIAL: + if (null === $value) { + return; + } + + $queryBuilder + ->andWhere(sprintf('%s.%s > :%s', $alias, $field, $valueParameter)) + ->setParameter($valueParameter, $value); + + break; + } + } + + public function getDescription(string $resourceClass): array + { + $description = []; + + $properties = $this->getProperties(); + if (null === $properties) { + $properties = array_fill_keys($this->getClassMetadata($resourceClass)->getFieldNames(), null); + } + + /** @var string $property */ + foreach (array_keys($properties) as $property) { + $description += $this->getFilterDescription($property, self::ATTRIBUTE_ID); + $description += $this->getFilterDescription($property, self::VALUE); + } + + return $description; + } + + protected function getFilterDescription(string $fieldName, string $operator): array + { + /** @var string $propertyName */ + $propertyName = $this->normalizePropertyName($fieldName); + + return [ + sprintf('%s[%s]', $propertyName, $operator) => [ + 'property' => $propertyName, + 'type' => 'string', + 'required' => false, + ], + ]; + } + + private function getAttributeId(array $values, string $property): ?string + { + if (array_key_exists(self::ATTRIBUTE_ID, $values)) { + /** @var string $attributeId */ + $attributeId = $values[self::ATTRIBUTE_ID]; + } else { + $this->getLogger()->notice('Invalid filter ignored', [ + 'exception' => new InvalidArgumentException( + sprintf( + '%s is required for "%s" property', + self::ATTRIBUTE_ID, + $property + ) + ), + ]); + + return null; + } + + return $attributeId; + } + + private function extractValue(array $values, string $property): ?string + { + if (array_key_exists(self::VALUE, $values)) { + /** @var string $value */ + $value = $values[self::VALUE]; + } else { + $this->getLogger()->notice('Invalid filter ignored', [ + 'exception' => new InvalidArgumentException( + sprintf( + '%s is required for "%s" property', + self::VALUE, + $property + ) + ), + ]); + + return null; + } + + return $value; + } + + /** + * @param ?mixed $value + * + * @return int|float|string|bool|\DateTime|null + * + * @throws \Exception + */ + private function normalizeValue($value, string $type) + { + switch ($type) { + case CheckboxAttributeType::TYPE: + $value = (bool) $value; + + break; + case DateAttributeType::TYPE: + case DatetimeAttributeType::TYPE: + Assert::string($value); + $value = new \DateTime($value); + + break; + case IntegerAttributeType::TYPE: + $value = (int) $value; + + break; + case PercentAttributeType::TYPE: + $value = (float) $value; + + break; + case SelectAttributeType::TYPE: + //assume IRI ? + //TODO:: + $value = (bool) $value; + + break; + default: + $value = (null !== $value) ? (string) $value : null; + } + + return $value; + } +} diff --git a/src/Behat/Context/GraphqlApiPlatformContext.php b/src/Behat/Context/GraphqlApiPlatformContext.php index 3d26eef8..ba0841ba 100644 --- a/src/Behat/Context/GraphqlApiPlatformContext.php +++ b/src/Behat/Context/GraphqlApiPlatformContext.php @@ -180,7 +180,7 @@ private function castToType($value, string $type = null) break; default: - $value = is_array($value) ? (array) $value: (string) $value; + $value = is_array($value) ? $value: (string) $value; break; } diff --git a/src/Doctrine/Orm/Filter/AttributeFilter.php b/src/Doctrine/Orm/Filter/AttributeFilter.php index b1ac083a..f4106aa0 100644 --- a/src/Doctrine/Orm/Filter/AttributeFilter.php +++ b/src/Doctrine/Orm/Filter/AttributeFilter.php @@ -30,9 +30,6 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface; use Webmozart\Assert\Assert; -/** - * Filters the collection by value of given attribute IRI - */ class AttributeFilter extends AbstractContextAwareFilter implements FilterInterface { use PropertyHelperTrait; @@ -66,9 +63,7 @@ public function __construct( $this->iriConverter = $iriConverter; } - /** - * @param mixed $value - */ + /** @param mixed $value */ protected function filterProperty( string $property, $value, @@ -77,20 +72,24 @@ protected function filterProperty( string $resourceClass, string $operationName = null ): void { - if ( - !\is_array($value) || - !$this->isPropertyEnabled($property, $resourceClass) - ) { + if ($this->isWrongValueOrProperty($value, $property, $resourceClass)) { return; } + Assert::isArray($value); + $attributeId = $this->getAttributeId($value, $property); $extractedValue = $this->extractValue($value, $property); if (null === $attributeId || null === $extractedValue) { return; } - $alias = $queryBuilder->getRootAliases()[0]; + $aliases = $queryBuilder->getRootAliases(); + if(count($aliases) > 0){ + $alias = reset($aliases); + }else{ + return; + } if ($this->isPropertyNested($property . '.id', $resourceClass)) { [$alias] = $this->addJoinsForNestedProperty($property . '.id', $alias, $queryBuilder, $queryNameGenerator, $resourceClass); @@ -187,10 +186,7 @@ protected function getFilterDescription(string $fieldName, string $operator): ar private function getAttributeId(array $values, string $property): ?string { - if (array_key_exists(self::ATTRIBUTE_ID, $values)) { - /** @var string $attributeId */ - $attributeId = $values[self::ATTRIBUTE_ID]; - } else { + if (!array_key_exists(self::ATTRIBUTE_ID, $values)) { $this->getLogger()->notice('Invalid filter ignored', [ 'exception' => new InvalidArgumentException( sprintf( @@ -204,15 +200,12 @@ private function getAttributeId(array $values, string $property): ?string return null; } - return $attributeId; + return (string) $values[self::ATTRIBUTE_ID]; } private function extractValue(array $values, string $property): ?string { if (array_key_exists(self::VALUE, $values)) { - /** @var string $value */ - $value = $values[self::VALUE]; - } else { $this->getLogger()->notice('Invalid filter ignored', [ 'exception' => new InvalidArgumentException( sprintf( @@ -226,7 +219,7 @@ private function extractValue(array $values, string $property): ?string return null; } - return $value; + return (string) $values[self::VALUE];; } /** @@ -258,9 +251,7 @@ private function normalizeValue($value, string $type) break; case SelectAttributeType::TYPE: - //assume IRI ? - //TODO:: - $value = (bool) $value; + $value = (string) $value; break; default: @@ -269,4 +260,11 @@ private function normalizeValue($value, string $type) return $value; } + + /** @param mixed $value */ + protected function isWrongValueOrProperty($value, string $property, string $resourceClass): bool + { + return !\is_array($value) || + !$this->isPropertyEnabled($property, $resourceClass); + } } diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index 37904ba2..43bfa98f 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -10,7 +10,7 @@ We are hiring developers from all over the world. Join us and start your new, ex xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + diff --git a/src/Resources/services/behat/contexts.xml b/src/Resources/services/behat/contexts.xml index 29dc746d..4c91a0de 100755 --- a/src/Resources/services/behat/contexts.xml +++ b/src/Resources/services/behat/contexts.xml @@ -68,6 +68,5 @@ - diff --git a/src/Resources/services/doctrine_orm.xml b/src/Resources/services/doctrine_orm.xml index 180a4d50..e7ed22e7 100644 --- a/src/Resources/services/doctrine_orm.xml +++ b/src/Resources/services/doctrine_orm.xml @@ -11,14 +11,18 @@ We are hiring developers from all over the world. Join us and start your new, ex - + null - +