forked from symfony/ux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Autocomplete] Allow passing extra options to the autocomplete fields
- Loading branch information
1 parent
0bae1d8
commit 308daef
Showing
22 changed files
with
480 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\UX\Autocomplete\Checksum; | ||
|
||
/** @internal */ | ||
class ChecksumCalculator | ||
{ | ||
public function __construct(private readonly string $secret) | ||
{ | ||
} | ||
|
||
public function calculateForArray(array $data): string | ||
{ | ||
$this->sortKeysRecursively($data); | ||
|
||
return base64_encode(hash_hmac('sha256', json_encode($data), $this->secret, true)); | ||
} | ||
|
||
private function sortKeysRecursively(array &$data): void | ||
{ | ||
foreach ($data as &$value) { | ||
if (\is_array($value)) { | ||
$this->sortKeysRecursively($value); | ||
} | ||
} | ||
ksort($data); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,20 +14,27 @@ | |
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | ||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
use Symfony\UX\Autocomplete\AutocompleteResultsExecutor; | ||
use Symfony\UX\Autocomplete\AutocompleterRegistry; | ||
use Symfony\UX\Autocomplete\Checksum\ChecksumCalculator; | ||
use Symfony\UX\Autocomplete\Form\AutocompleteChoiceTypeExtension; | ||
use Symfony\UX\Autocomplete\OptionsAwareEntityAutocompleterInterface; | ||
|
||
/** | ||
* @author Ryan Weaver <[email protected]> | ||
*/ | ||
final class EntityAutocompleteController | ||
{ | ||
public const EXTRA_OPTIONS = 'extra_options'; | ||
|
||
public function __construct( | ||
private AutocompleterRegistry $autocompleteFieldRegistry, | ||
private AutocompleteResultsExecutor $autocompleteResultsExecutor, | ||
private UrlGeneratorInterface $urlGenerator, | ||
private ChecksumCalculator $checksumCalculator, | ||
) { | ||
} | ||
|
||
|
@@ -38,6 +45,11 @@ public function __invoke(string $alias, Request $request): Response | |
throw new NotFoundHttpException(sprintf('No autocompleter found for "%s". Available autocompleters are: (%s)', $alias, implode(', ', $this->autocompleteFieldRegistry->getAutocompleterNames()))); | ||
} | ||
|
||
if ($autocompleter instanceof OptionsAwareEntityAutocompleterInterface) { | ||
$extraOptions = $this->getExtraOptions($request); | ||
$autocompleter->setOptions([self::EXTRA_OPTIONS => $extraOptions]); | ||
} | ||
|
||
$page = $request->query->getInt('page', 1); | ||
$nextPage = null; | ||
|
||
|
@@ -54,4 +66,48 @@ public function __invoke(string $alias, Request $request): Response | |
'next_page' => $nextPage, | ||
]); | ||
} | ||
|
||
/** | ||
* @return array<string, scalar|array|null> | ||
*/ | ||
private function getExtraOptions(Request $request): array | ||
{ | ||
if (!$request->query->has(self::EXTRA_OPTIONS)) { | ||
return []; | ||
} | ||
|
||
$extraOptions = $this->getDecodedExtraOptions($request->query->getString(self::EXTRA_OPTIONS)); | ||
|
||
if (!\array_key_exists(AutocompleteChoiceTypeExtension::CHECKSUM_KEY, $extraOptions)) { | ||
throw new BadRequestHttpException('The extra options are missing the checksum.'); | ||
} | ||
|
||
$this->validateChecksum($extraOptions[AutocompleteChoiceTypeExtension::CHECKSUM_KEY], $extraOptions); | ||
|
||
return $extraOptions; | ||
} | ||
|
||
/** | ||
* @return array<string, scalar> | ||
*/ | ||
private function getDecodedExtraOptions(string $extraOptions): array | ||
{ | ||
return json_decode(base64_decode($extraOptions), true, flags: \JSON_THROW_ON_ERROR); | ||
} | ||
|
||
/** | ||
* @param array<string, scalar> $extraOptions | ||
*/ | ||
private function validateChecksum(string $checksum, array $extraOptions): void | ||
{ | ||
$extraOptionsWithoutChecksum = array_filter( | ||
$extraOptions, | ||
fn (string $key) => AutocompleteChoiceTypeExtension::CHECKSUM_KEY !== $key, | ||
\ARRAY_FILTER_USE_KEY, | ||
); | ||
|
||
if ($checksum !== $this->checksumCalculator->calculateForArray($extraOptionsWithoutChecksum)) { | ||
throw new BadRequestHttpException('The extra options have been tampered with.'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
src/Autocomplete/src/OptionsAwareEntityAutocompleterInterface.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\UX\Autocomplete; | ||
|
||
/** | ||
* Interface for classes that will have an "autocomplete" endpoint exposed with a possibility to pass additional form options. | ||
*/ | ||
interface OptionsAwareEntityAutocompleterInterface extends EntityAutocompleterInterface | ||
{ | ||
public function setOptions(array $options): void; | ||
} |
17 changes: 9 additions & 8 deletions
17
src/Autocomplete/tests/Fixtures/Autocompleter/CustomGroupByProductAutocompleter.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
<?php | ||
|
||
namespace Symfony\UX\Autocomplete\Tests\Fixtures\Autocompleter; | ||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
use Doctrine\ORM\EntityRepository; | ||
use Doctrine\ORM\QueryBuilder; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Bundle\SecurityBundle\Security; | ||
use Symfony\UX\Autocomplete\Doctrine\EntitySearchUtil; | ||
use Symfony\UX\Autocomplete\EntityAutocompleterInterface; | ||
use Symfony\UX\Autocomplete\Tests\Fixtures\Entity\Product; | ||
namespace Symfony\UX\Autocomplete\Tests\Fixtures\Autocompleter; | ||
|
||
class CustomGroupByProductAutocompleter extends CustomProductAutocompleter | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,20 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\UX\Autocomplete\Tests\Fixtures\Autocompleter; | ||
|
||
use Doctrine\ORM\EntityRepository; | ||
use Doctrine\ORM\QueryBuilder; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\Bundle\SecurityBundle\Security; | ||
use Symfony\Component\HttpFoundation\RequestStack; | ||
use Symfony\UX\Autocomplete\Doctrine\EntitySearchUtil; | ||
use Symfony\UX\Autocomplete\EntityAutocompleterInterface; | ||
use Symfony\UX\Autocomplete\Tests\Fixtures\Entity\Product; | ||
|
@@ -14,9 +23,8 @@ class CustomProductAutocompleter implements EntityAutocompleterInterface | |
{ | ||
public function __construct( | ||
private RequestStack $requestStack, | ||
private EntitySearchUtil $entitySearchUtil | ||
) | ||
{ | ||
private EntitySearchUtil $entitySearchUtil, | ||
) { | ||
} | ||
|
||
public function getEntityClass(): string | ||
|
Oops, something went wrong.