diff --git a/src/Doctrine/Orm/Filter/AttributeFilter.php b/src/Doctrine/Orm/Filter/AttributeFilter.php new file mode 100644 index 00000000..2bfa1930 --- /dev/null +++ b/src/Doctrine/Orm/Filter/AttributeFilter.php @@ -0,0 +1,265 @@ +iriConverter = $iriConverter; + } + + /** + * @param string $property + * @param $values + * @param QueryBuilder $queryBuilder + * @param QueryNameGeneratorInterface $queryNameGenerator + * @param string $resourceClass + * @param string|null $operationName + */ + protected function filterProperty( + string $property, + $values, + QueryBuilder $queryBuilder, + QueryNameGeneratorInterface $queryNameGenerator, + string $resourceClass, + string $operationName = null + ): void + { + if ( + !\is_array($values) || + !$this->isPropertyEnabled($property, $resourceClass) + ) { + return; + } + + $attributeId = $this->getAttributeId($values, $property); + $value = $this->getValue($values, $property); + if (null === $attributeId || null === $value) { + return; + } + + $alias = $queryBuilder->getRootAliases()[0]; + + if ($this->isPropertyNested($property.".id", $resourceClass)) { + [$alias] = $this->addJoinsForNestedProperty($property.".id", $alias, $queryBuilder, $queryNameGenerator, $resourceClass); + } + + $this->addWhere( + $queryBuilder, + $queryNameGenerator, + $alias, + $property, + $attributeId, + $value + ); + } + + /** @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(); + $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 ($properties as $property => $unused) { + $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 (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 getValue(array $values, string $property): ?string + { + + if (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; + } + + /** + * @return int|float|string|null|bool|\DateTime + * @throws \Exception + */ + private function normalizeValue(string $value, string $type) + { + + switch ($type) { + case CheckboxAttributeType::TYPE: + $value = (bool) $value; + break; + case DateAttributeType::TYPE: + case DatetimeAttributeType::TYPE: + $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; + } + + return $value; + } +} diff --git a/src/Resources/api_resources/Product.xml b/src/Resources/api_resources/Product.xml index 188dc48c..45e8d6af 100644 --- a/src/Resources/api_resources/Product.xml +++ b/src/Resources/api_resources/Product.xml @@ -21,6 +21,7 @@ We are hiring developers from all over the world. Join us and start your new, ex sylius.api.product_taxon_code_filter sylius.api.product_taxon_slug_filter sylius.api.product_price_filter + sylius.api.product_attribute_filter @@ -34,6 +35,7 @@ We are hiring developers from all over the world. Join us and start your new, ex sylius.api.product_taxon_code_filter sylius.api.product_taxon_slug_filter sylius.api.product_price_filter + sylius.api.product_attribute_filter sylius diff --git a/src/Resources/api_resources/ProductAttribute.xml b/src/Resources/api_resources/ProductAttribute.xml index e54e29c7..0af9cb07 100644 --- a/src/Resources/api_resources/ProductAttribute.xml +++ b/src/Resources/api_resources/ProductAttribute.xml @@ -13,13 +13,11 @@ We are hiring developers from all over the world. Join us and start your new, ex - - - - shop:product:read - - + + page + + @@ -28,5 +26,17 @@ We are hiring developers from all over the world. Join us and start your new, ex + + + object + + + string + string + string + + + + diff --git a/src/Resources/api_resources/ProductAttributeTranslation.xml b/src/Resources/api_resources/ProductAttributeTranslation.xml new file mode 100644 index 00000000..09b20595 --- /dev/null +++ b/src/Resources/api_resources/ProductAttributeTranslation.xml @@ -0,0 +1,26 @@ + + + + + + + + + page + + + + + + + + + + diff --git a/src/Resources/serialization/PaymentMethod.xml b/src/Resources/serialization/PaymentMethod.xml index 74dea502..ddb65bd8 100644 --- a/src/Resources/serialization/PaymentMethod.xml +++ b/src/Resources/serialization/PaymentMethod.xml @@ -10,7 +10,7 @@ We are hiring developers from all over the world. Join us and start your new, ex xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/serializer-mapping https://symfony.com/schema/dic/serializer-mapping/serializer-mapping-1.0.xsd" > - + admin:payment_method:read shop:payment_method:read diff --git a/src/Resources/services/doctrine_orm.xml b/src/Resources/services/doctrine_orm.xml new file mode 100644 index 00000000..cd3623e3 --- /dev/null +++ b/src/Resources/services/doctrine_orm.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + null + + + + + + + diff --git a/src/Resources/services/filters.xml b/src/Resources/services/filters.xml index f7d89a77..414d4b48 100644 --- a/src/Resources/services/filters.xml +++ b/src/Resources/services/filters.xml @@ -21,17 +21,17 @@ We are hiring developers from all over the world. Join us and start your new, ex - - partial - partial + -