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.

Revisions

  1. mattattui revised this gist Mar 7, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion 0README.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # 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, especially in APIs where you aren't concerned with SEO.
    * Obfuscated/hash IDs are a great idea, especially in APIs (where you aren't concerned with SEO, but might be concerned about sequential numeric ids or exposing database information).
    * 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.
  2. mattattui revised this gist Mar 7, 2015. 5 changed files with 54 additions and 12 deletions.
    19 changes: 17 additions & 2 deletions 0README.md
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,26 @@
    # 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.
    * Obfuscated/hash IDs are a great idea, especially in APIs where you aren't concerned with SEO.
    * 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.

    Following Phil Sturgeon's excellent advice in [Build APIs You Won't Hate](https://leanpub.com/build-apis-you-wont-hate), I've also added an option to allow multiple ids to be loaded at once, like `/resources/id1,id2,id3,id4`. It's really quite handy sometimes. Bewarned though; it won't 404 if only some of the ids are missing. Of this whole thing, this is the part that could do with the most attention, sorry! The good news is that it's totally optional, so if you want to avoid the potential shonkiness then just don't use it. In the example controller code I put separate actions to demonstrate how you use it with and without multiple-id support, but obviously don't use *both*, that's just silly. Pick one! If you don't like to read and just paste the code in, it'll crash. That's my petty vengeance at work :)

    I've used Zack Kitzmiller's [Tiny-php](https://github.com/zackkitzmiller/tiny-php) library here because it's simple and great, but you could use [hashids](http://hashids.org/php/) 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.
    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.

    ## If you aren't using Symfony 2.6 (or you upgraded to it)

    * sf2.6 has an `app/config/services.yml` file, while if you started your project with an earlier version you probably don't. You can make your own and import it to `config.yml` or you could just put this stuff in there directly, whatever.
    * sf2.6 starts new projects with an AppBundle, following the Symfony Best Practices, while older versions don't (and actually make it kind of an arse to set one up, since it has no vendor namespace). Just… use whatever you're already using I guess.

    ## Suggested improvements

    * Read *APIs you won't hate* again to remember how to handle 404s with multiple ids
    * Support for different Tiny keys (e.g. for different entities)
    * Add tests
    * Make it a Bundle

    35 changes: 28 additions & 7 deletions AssetController.php
    Original file line number Diff line number Diff line change
    @@ -2,23 +2,44 @@

    namespace AppBundle\Controller;

    use AppBundle\Entity\Asset;
    use AppBundle\Entity\Resource;
    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
    class ResourceController extends Controller
    {
    // …
    /**
    * @Route("/resource/{id}", name="myroute")
    * @ParamConverter("asset", class="AppBundle:Asset", converter="obfuscated")
    * @Route("/resources/{id}", name="resource_show")
    * @ParamConverter("resource", class="AppBundle:Resource", converter="obfuscated")
    */
    public function showAction(Request $request, Asset $asset)
    public function showAction(Request $request, Resource $resource)
    {
    return $this->render('AppBundle:Asset:show.html.twig', [
    'asset' => $asset,
    //…
    return $this->render('AppBundle:Resource:show.html.twig', [
    'resource' => $resource,
    ]);
    }

    /**
    * Alternative with support for multiple ids. Don't use both, pick one -
    * I've made this throw a fatal error so you don't just cut and paste :P
    *
    * @Route("/resources/{id}", name="resource_show")
    * @ParamConverter("resources", class="AppBundle:Resource", converter="obfuscated", options={
    * "repository_method":"findById",
    * "multiple": true
    * })
    */
    public function showAction(Request $request, array $resources)
    {
    //…
    return $this->render('AppBundle:Resource:show.html.twig', [
    'resources' => $resources,
    ]);
    }


    }
    7 changes: 7 additions & 0 deletions ObfuscatedDoctrineParamConverter.php
    Original file line number Diff line number Diff line change
    @@ -34,10 +34,17 @@ protected function getIdentifier(Request $request, $options, $name)
    {
    $id = parent::getIdentifier($request, $options, $name);

    if ($id && array_key_exists('multiple', $options) && $options['multiple']) {
    $id = array_map([$this->tiny, 'from'], array_filter(preg_split('/[\s,]+/', $id)));

    return $id;
    }

    if ($id) {
    return $this->tiny->from($id);
    }

    return false;
    }
    }

    2 changes: 1 addition & 1 deletion index.html.twig
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,6 @@

    {% block body %}
    //…
    <a href="{{ path('myroute', { id: asset.id | obfuscate }) }}">Link to thing</a>
    <a href="{{ path('resource_show', { id: resource.id | obfuscate }) }}">Link to thing</a>
    // …
    {% endblock %}
    3 changes: 1 addition & 2 deletions config.yml → services.yml
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    services:
    id_obfuscator:
    class: ZackKitzmiller\Tiny
    @@ -10,7 +9,7 @@ services:
    arguments:
    - "@doctrine"
    - "@id_obfuscator"
    tags:
    tags: # Priority false means it won't ever run automatically; you have to ask for it
    - { name: request.param_converter, priority: false, converter: obfuscated }

    obfuscator_extension:
  3. mattattui renamed this gist Oct 30, 2014. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. mattattui created this gist Oct 30, 2014.
    24 changes: 24 additions & 0 deletions AssetController.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    <?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,
    ]);
    }
    }
    43 changes: 43 additions & 0 deletions ObfuscatedDoctrineParamConverter.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    <?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;
    }
    }
    41 changes: 41 additions & 0 deletions Obfuscator.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    <?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';
    }
    }
    11 changes: 11 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    # 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](https://github.com/zackkitzmiller/tiny-php) library here because it's simple and great, but you could use [hashids](http://hashids.org/php/) 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.
    22 changes: 22 additions & 0 deletions config.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@

    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 }
    7 changes: 7 additions & 0 deletions index.html.twig
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    {% extends '::layout.html.twig' %}

    {% block body %}
    //…
    <a href="{{ path('myroute', { id: asset.id | obfuscate }) }}">Link to thing</a>
    // …
    {% endblock %}
    2 changes: 2 additions & 0 deletions parameters.yml.dist
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    parameters:
    id_obfuscator_key: "[generate this using vendor/zackkitzmiller/tiny/bin/genset]"