Skip to content

Instantly share code, notes, and snippets.

@artyuum
Last active August 6, 2021 20:52
Show Gist options
  • Save artyuum/6405566cf7fde3dc6182bf2557021b59 to your computer and use it in GitHub Desktop.
Save artyuum/6405566cf7fde3dc6182bf2557021b59 to your computer and use it in GitHub Desktop.
A ParamConverter to use HashIds with Doctrine + Symfony.
<?php
namespace App\ParamConverter;
use Doctrine\ORM\EntityManagerInterface;
use Hashids\HashidsInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Throwable;
class HashidsDoctrineParamConverter implements ParamConverterInterface
{
public function __construct(
private HashidsInterface $hashids, private EntityManagerInterface $entityManager, private PropertyAccessorInterface $propertyAccessor
) {
}
public function supports(ParamConverter $configuration)
{
// supports by default
if (!isset($configuration->getOptions()['hashids_doctrine'])) {
return true;
}
return $configuration->getOptions()['hashids_doctrine'];
}
public function apply(Request $request, ParamConverter $configuration)
{
$routeParams = $request->attributes->get('_route_params');
$parameterName = $configuration->getName();
$parameterValue = $request->attributes->get($parameterName);
$class = $configuration->getClass();
if (!$configuration->getClass()) {
return;
}
// ensures that the parameter exists in the route params
if (!$routeParams || !in_array($parameterName, array_keys($routeParams), true)) {
return;
}
// ends here if the class is not an entity
try {
$repository = $this->entityManager->getRepository($class);
} catch (Throwable) {
return;
}
if (property_exists($class, $parameterName)) {
$entity = $repository->findOneBy([
$parameterName => $parameterValue,
]);
// ends here if the entity couldn't be found
if (!$entity) {
throw new NotFoundHttpException(sprintf('%s object not found by the property "%s" with the value "%s".', $class, $parameterName, $parameterValue));
}
// replaces the parameter value by the entity
$request->attributes->set($parameterName, $entity);
return;
}
// decodes the encoded ID
$decodedId = $this->hashids->decode($parameterValue);
// ends here if the ID couldn't be decoded
if (empty($decodedId)) {
throw new NotFoundHttpException(sprintf('The passed ID "%s" could not be decoded.', $parameterValue));
}
$decodedId = current($decodedId);
$entity = $repository->find($decodedId);
// ends here if the entity couldn't be found
if (!$entity) {
throw new NotFoundHttpException(sprintf('%s object not found by ID: %s (%s)', $class, $decodedId, $parameterValue));
}
// replaces the parameter value by the entity
$request->attributes->set($parameterName, $entity);
}
}
@artyuum
Copy link
Author

artyuum commented Sep 30, 2020

I had to make my own ParamConverter because hashids-bundle is not properly working in my app.

Usage
The ParamConverter will be automatically used but if you want to disable it, just do it like that:

/**
 * Edits a wallpaper.
 *
 * @Route("/wallpapers/{wallpaper}", methods={"PATCH"}, name="wallpaper.edit")
 * @ParamConverter("wallpaper", class="\App\Entity\Wallpaper", options={"hashids_doctrine": false})
 */
public function __invoke(Wallpaper $wallpaper): Response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment