Skip to content

Commit

Permalink
Code review fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
tbuczen committed Nov 25, 2021
1 parent 9ea49a6 commit e542e1a
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 28 deletions.
272 changes: 272 additions & 0 deletions spec/Doctrine/Orm/Filter/AttributeFilterSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
<?php

/*
* This file was created by developers working at BitBag
* Do you need more information about us and what we do? Visit our https://bitbag.io website!
* We are hiring developers from all over the world. Join us and start your new, exciting adventure and become part of us: https://bitbag.io/career
*/

declare(strict_types=1);

namespace BitBag\SyliusGraphqlPlugin\Doctrine\Orm\Filter;

use ApiPlatform\Core\Api\FilterInterface;
use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractContextAwareFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\PropertyHelperTrait;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Sylius\Component\Attribute\AttributeType\CheckboxAttributeType;
use Sylius\Component\Attribute\AttributeType\DateAttributeType;
use Sylius\Component\Attribute\AttributeType\DatetimeAttributeType;
use Sylius\Component\Attribute\AttributeType\IntegerAttributeType;
use Sylius\Component\Attribute\AttributeType\PercentAttributeType;
use Sylius\Component\Attribute\AttributeType\SelectAttributeType;
use Sylius\Component\Attribute\Model\AttributeInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Webmozart\Assert\Assert;

/**
* Filters the collection by value of given attribute IRI
*/
class AttributeFilterSpec extends AbstractContextAwareFilter implements FilterInterface
{
use PropertyHelperTrait;

private IriConverterInterface $iriConverter;

public const OPERATOR_EXACT = 'exact';

public const OPERATOR_PARTIAL = 'partial';

public const ATTRIBUTE_ID = 'attribute_id';

public const VALUE = 'value';

public function __construct(
ManagerRegistry $managerRegistry,
IriConverterInterface $iriConverter,
?RequestStack $requestStack = null,
LoggerInterface $logger = null,
array $properties = null,
NameConverterInterface $nameConverter = null
) {
parent::__construct(
$managerRegistry,
$requestStack,
$logger,
$properties,
$nameConverter
);

$this->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;
}
}
2 changes: 1 addition & 1 deletion src/Behat/Context/GraphqlApiPlatformContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
44 changes: 21 additions & 23 deletions src/Doctrine/Orm/Filter/AttributeFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -66,9 +63,7 @@ public function __construct(
$this->iriConverter = $iriConverter;
}

/**
* @param mixed $value
*/
/** @param mixed $value */
protected function filterProperty(
string $property,
$value,
Expand All @@ -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);
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -226,7 +219,7 @@ private function extractValue(array $values, string $property): ?string
return null;
}

return $value;
return (string) $values[self::VALUE];;
}

/**
Expand Down Expand Up @@ -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:
Expand All @@ -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);
}
}
Loading

0 comments on commit e542e1a

Please sign in to comment.