Skip to content

Instantly share code, notes, and snippets.

@mattattui
Last active September 19, 2025 06:01
Show Gist options
  • Select an option

  • Save mattattui/efd1c4470f2ce0097fb5 to your computer and use it in GitHub Desktop.

Select an option

Save mattattui/efd1c4470f2ce0097fb5 to your computer and use it in GitHub Desktop.
Using obfuscated ids with DoctrineParamConverter

Using Tiny (ID obfuscator) with Symfony2's ParamConverters

  • Symfony's convenience methods for automatically fetching database entities from URL parameters are super-handy.
  • Obfuscated/hash IDs are a great idea.
  • Here's how to make them work together.

The stuff in this gist sets up a Twig filter (obfuscate) to create the obfuscated ids (for URLs), makes the obfuscator available as a service (id_obfuscator) so you can also generate obfuscated URLs in your controllers or whatever, and extends the DoctrineParamConverter to allow it to retrieve entities by their deobfuscated id.

I've used Zack Kitzmiller's Tiny-php library here because it's simple and great, but you could use hashids instead with very little change; same difference.

Take it, use it (at your own risk), fix it, no need to credit me. I'm not sure it warrants clogging up Packagist with, but if you do decide to make it into a reusable Symfony bundle then I'd love to know about it.

<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Asset;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class AssetController extends Controller
{
// …
/**
* @Route("/resource/{id}", name="myroute")
* @ParamConverter("asset", class="AppBundle:Asset", converter="obfuscated")
*/
public function showAction(Request $request, Asset $asset)
{
return $this->render('AppBundle:Asset:show.html.twig', [
'asset' => $asset,
]);
}
}
services:
id_obfuscator:
class: ZackKitzmiller\Tiny
arguments:
- "%id_obfuscator_key%"
obfuscated_paramconverter:
class: AppBundle\Request\ParamConverter\ObfuscatedDoctrineParamConverter
arguments:
- "@doctrine"
- "@id_obfuscator"
tags:
- { name: request.param_converter, priority: false, converter: obfuscated }
obfuscator_extension:
private: true
class: AppBundle\Extension\Obfuscator
arguments:
- "@id_obfuscator"
tags:
- { name: twig.extension }
{% extends '::layout.html.twig' %}
{% block body %}
//…
<a href="{{ path('myroute', { id: asset.id | obfuscate }) }}">Link to thing</a>
// …
{% endblock %}
<?php
namespace AppBundle\Request\ParamConverter;
use Doctrine\Common\Persistence\ManagerRegistry;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\DoctrineParamConverter;
use Symfony\Component\HttpFoundation\Request;
use ZackKitzmiller\Tiny;
/**
* DoctrineParamConverter.
*
* @author Fabien Potencier <[email protected]>
*/
class ObfuscatedDoctrineParamConverter extends DoctrineParamConverter
{
/**
* @var ManagerRegistry
*/
protected $registry;
/**
* @var ZackKitzmiller\Tiny
*/
protected $tiny;
public function __construct(ManagerRegistry $registry = null, Tiny $tiny)
{
parent::__construct($registry);
$this->tiny = $tiny;
}
protected function getIdentifier(Request $request, $options, $name)
{
$id = parent::getIdentifier($request, $options, $name);
if ($id) {
return $this->tiny->from($id);
}
return false;
}
}
<?php
namespace AppBundle\Extension;
use ZackKitzmiller\Tiny;
class Obfuscator extends \Twig_Extension
{
private $tiny;
public function __construct(Tiny $tiny)
{
$this->tiny = $tiny;
}
/**
* Returns a list of functions to add to the existing list.
*
* @return array An array of functions
*/
public function getFilters()
{
return array(
'obfuscate' => new \Twig_Filter_Method($this, 'getObfuscatedId'),
);
}
public function getObfuscatedId($id)
{
return $this->tiny->to($id);
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'obfuscate';
}
}
parameters:
id_obfuscator_key: "[generate this using vendor/zackkitzmiller/tiny/bin/genset]"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment