Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Doctrine/Odm/NestedPropertyHelperTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected function addNestedParameterLookups(string $property, Builder $aggregat
$nestedInfo = ($extraProperties['nested_properties_info'] ?? []) ? reset($extraProperties['nested_properties_info']) : null;

if (!$nestedInfo) {
return $property;
return $extraProperties['query_property'] ?? $property;
}

$odmSegments = $nestedInfo['odm_segments'] ?? [];
Expand Down
2 changes: 1 addition & 1 deletion src/Doctrine/Orm/NestedPropertyHelperTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ protected function addNestedParameterJoins(
$nestedInfo = ($extraProperties['nested_properties_info'] ?? []) ? reset($extraProperties['nested_properties_info']) : null;

if (!$nestedInfo) {
return [$alias, $property];
return [$alias, $extraProperties['query_property'] ?? $property];
}

$relationClasses = $nestedInfo['relation_classes'] ?? [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,10 @@ private function setDefaults(string $key, Parameter $parameter, ?object $filter,
// Skip name conversion if we already have nested property info
$paramExtraProps = $parameter->getExtraProperties();
if (!isset($paramExtraProps['nested_properties_info'])) {
$parameter = $parameter->withProperty($this->nameConverter->normalize($property));
// Keep the original (non name-converted) property to build the query while the public property matches the API naming convention.
$parameter = $parameter
->withExtraProperties([...$paramExtraProps, 'query_property' => $property])
->withProperty($this->nameConverter->normalize($property));
}
}

Expand Down
57 changes: 57 additions & 0 deletions tests/Fixtures/TestBundle/Document/ConvertedFilterParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Document;

use ApiPlatform\Doctrine\Odm\Filter\ExactFilter;
use ApiPlatform\Doctrine\Odm\Filter\SortFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\QueryParameter;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;

/**
* Tests that modern parameter filters (ExactFilter, SortFilter) work with
* QueryParameter on a multi-word property when a nameConverter is configured
* (issue #8380).
*/
#[ApiResource(
operations: [
new GetCollection(
parameters: [
'nameConverted' => new QueryParameter(
filter: new ExactFilter(),
property: 'nameConverted',
),
'orderNameConverted' => new QueryParameter(
filter: new SortFilter(),
property: 'nameConverted',
),
],
),
],
)]
#[ODM\Document]
class ConvertedFilterParameter
{
#[ODM\Id(strategy: 'INCREMENT', type: 'int')]
private ?int $id = null;

#[ODM\Field(type: 'string')]
public string $nameConverted = '';

public function getId(): ?int
{
return $this->id;
}
}
59 changes: 59 additions & 0 deletions tests/Fixtures/TestBundle/Entity/ConvertedFilterParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity;

use ApiPlatform\Doctrine\Orm\Filter\ExactFilter;
use ApiPlatform\Doctrine\Orm\Filter\SortFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\QueryParameter;
use Doctrine\ORM\Mapping as ORM;

/**
* Tests that modern parameter filters (ExactFilter, SortFilter) work with
* QueryParameter on a multi-word property when a nameConverter is configured
* (issue #8380).
*/
#[ApiResource(
operations: [
new GetCollection(
parameters: [
'nameConverted' => new QueryParameter(
filter: new ExactFilter(),
property: 'nameConverted',
),
'orderNameConverted' => new QueryParameter(
filter: new SortFilter(),
property: 'nameConverted',
),
],
),
],
)]
#[ORM\Entity]
class ConvertedFilterParameter
{
#[ORM\Column(type: 'integer', nullable: true)]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private ?int $id = null;

#[ORM\Column(type: 'string')]
public string $nameConverted = '';

public function getId(): ?int
{
return $this->id;
}
}
84 changes: 84 additions & 0 deletions tests/Functional/Parameters/NameConverterModernFilterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\Tests\Functional\Parameters;

use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use ApiPlatform\Tests\Fixtures\TestBundle\Document\ConvertedFilterParameter as ConvertedFilterParameterDocument;
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\ConvertedFilterParameter;
use ApiPlatform\Tests\RecreateSchemaTrait;
use ApiPlatform\Tests\SetupClassResourcesTrait;

/**
* Tests that modern parameter filters (ExactFilter, SortFilter) work with
* QueryParameter on a multi-word property when a nameConverter is configured.
*
* @see https://github.com/api-platform/core/issues/8380
*/
final class NameConverterModernFilterTest extends ApiTestCase
{
use RecreateSchemaTrait;
use SetupClassResourcesTrait;

protected static ?bool $alwaysBootKernel = false;

/**
* @return class-string[]
*/
public static function getResources(): array
{
return [ConvertedFilterParameter::class];
}

protected function setUp(): void
{
$entityClass = $this->isMongoDB() ? ConvertedFilterParameterDocument::class : ConvertedFilterParameter::class;

$this->recreateSchema([$entityClass]);
$this->loadFixtures($entityClass);
}

public function testExactFilterWithNameConverter(): void
{
$response = self::createClient()->request('GET', '/converted_filter_parameters?nameConverted=bar');
$this->assertResponseIsSuccessful();
$members = $response->toArray()['hydra:member'];
$this->assertCount(1, $members);
$this->assertSame('bar', $members[0]['name_converted']);
}

public function testSortFilterWithNameConverter(): void
{
$response = self::createClient()->request('GET', '/converted_filter_parameters?orderNameConverted=desc');
$this->assertResponseIsSuccessful();
$members = $response->toArray()['hydra:member'];
$names = array_map(static fn (array $m): string => $m['name_converted'], $members);
$this->assertSame(['foo', 'baz', 'bar'], $names);
}

/**
* @param class-string $entityClass
*/
private function loadFixtures(string $entityClass): void
{
$manager = $this->getManager();

foreach (['bar', 'baz', 'foo'] as $name) {
$entity = new $entityClass();
$entity->nameConverted = $name;
$manager->persist($entity);
}

$manager->flush();
}
}
Loading