Search code examples
symfonydoctrine-ormseparator

Symfony2 Sluggable multiple separators


Using the Gedmo Sluggable behavior in Symfony2, I need to know if there is a way to allow both slashes (/) and dashes (-) as word separators. In other words, one specific separator as usual, but leaving alone the other specific special character so it is ignored. I want to do something like this as a slug:

products/some-product

This allows me to use slugs in the URL that are categorized via the slash and separate the spaces via the dash. But, for instance, if the separator is "/", the "-" will be replaced as well instead of left alone.

I've looked through the related Sluggable class code (Urlizer) and see a lot of regex going on, but I'm not sure where I should override to allow slashed and/or dashes to NOT be substituted along with everything else.


Solution

  • Turns out you can accomplish this by creating your own class that extends Urlizer, then setting it as the callable for the listener instead of Gedmo's class.

    When using STOF doctrine extensions, a sluggable listener is created as a service, but it is set as private so you can't normally access. So you must first create an alias to this listener in your own config:

    services:
    
        sluggable.listener:
            alias: stof_doctrine_extensions.listener.sluggable
    

    You must then create your class. There is a setter called setTransliterator() that you can use to call your own transliterator, which you can then use to inject what you need to modify the slugging process. The function postProccessText() is what you really want to modify, the transliterate() function is just what is callable:

    namespace My\Bundle\Util;
    
    use Gedmo\Sluggable\Util\Urlizer as BaseUrlizer;
    
    class Urlizer extends BaseUrlizer
    {
        public static function transliterate($text, $separator = '-')
        {
            // copy the code from the parent here
        }
    
        private static function postProcessText($text, $separator)
        {
            // copy code from parent, but modify the following part:
    
            $text = strtolower(preg_replace('/[^A-Z^a-z^0-9^\/]+/', $separator,
                               preg_replace('/([a-z\d])([A-Z])/', '\1_\2',
                               preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1_\2',
                               preg_replace('/::/', '/', $text)))));
        }
    }
    

    The regular expressions of postProcessText() are what you want to modify to your liking. After that, you must make your function the callable right before you persist, and you're good to go:

    // custom transliterator
    $listener = $this->get('sluggable.listener');
    $listener->setTransliterator(array('My\Bundle\Util\Urlizer', 'transliterate'));
    $em->flush();