# Sluggable behavior extension for Doctrine 2 **Sluggable** behavior will build the slug of predefined fields on a given field which should store the slug Features: - Automatic predifined field transformation into slug - ORM and ODM support using same listener - Slugs can be unique and styled - Can be nested with other behaviors - Annotation, Yaml and Xml mapping support for extensions - Multiple slugs, diferent slugs can link to same fields [blog_reference]: http://gediminasm.org/article/sluggable-behavior-extension-for-doctrine-2 "Sluggable extension for Doctrine 2 makes automatic record field transformations into url friendly names" [blog_test]: http://gediminasm.org/test "Test extensions on this blog" Update **2012-02-26** - Remove slug handlers were removed because of complications it brought together Update **2011-09-11** - Refactored sluggable for doctrine2.2 by specifieng slug fields directly in slug annotation - Slug handler functionality, possibility to create custom ones or use built-in tree path handler or linked slug through single valued association - Updated documentation mapping examples for 2.1.x version or higher Update **2011-04-04** - Made single listener, one instance can be used for any object manager and any number of them Update **2010-12-23** - Full support for unique index on slug field, no more exceptions during concurrent flushes. **Note:** - There is a reported [issue](https://github.com/l3pp4rd/DoctrineExtensions/issues/254) that sluggable transliterator does not work on OSX 10.6 its ok starting again from 10.7 version. To overcome the problem you can use your [custom transliterator](#transliterator) - You can [test live][blog_test] on this blog - Public [Sluggable repository](http://github.com/l3pp4rd/DoctrineExtensions "Sluggable extension on Github") is available on github - Last update date: **2012-02-26** **Portability:** - **Sluggable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle) ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions This article will cover the basic installation and functionality of **Sluggable** behavior Content: - [Including](#including-extension) the extension - Entity [example](#entity-mapping) - Document [example](#document-mapping) - [Yaml](#yaml-mapping) mapping example - [Xml](#xml-mapping) mapping example - Basic usage [examples](#basic-examples) - Custom [transliterator](#transliterator) - Advanced usage [examples](#advanced-examples) - Using [slug handlers](#slug-handlers) ## Setup and autoloading Read the [documentation](http://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) or check the [example code](http://github.com/l3pp4rd/DoctrineExtensions/tree/master/example) on how to setup and use the extensions in most optimized way. ## Sluggable Entity example: ### Sluggable annotations: - **@Gedmo\Mapping\Annotation\Slug** it will use this column to store **slug** generated **fields** option must be specified, an array of field names to slug **Note:** that Sluggable interface is not necessary, except in cases there you need to identify entity as being Sluggable. The metadata is loaded only once then cache is activated **Note:** 2.0.x version of extensions used @Gedmo\Mapping\Annotation\Sluggable to identify the field for slug ``` php id; } public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } public function setCode($code) { $this->code = $code; } public function getCode() { return $this->code; } public function getSlug() { return $this->slug; } } ``` ## Sluggable Document example: ``` php id; } public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } public function setCode($code) { $this->code = $code; } public function getCode() { return $this->code; } public function getSlug() { return $this->slug; } } ``` ## Yaml mapping example Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** ``` --- Entity\Article: type: entity table: articles id: id: type: integer generator: strategy: AUTO fields: title: type: string length: 64 code: type: string length: 16 slug: type: string length: 128 gedmo: slug: separator: _ style: camel fields: - title - code indexes: search_idx: columns: slug ``` ## Xml mapping example **Note:** xml driver is not yet adapted for single slug mapping ``` xml ``` ## Basic usage examples: ### To save **Article** and generate slug simply use: ``` php setTitle('the title'); $article->setCode('my code'); $this->em->persist($article); $this->em->flush(); echo $article->getSlug(); // prints: the-title-my-code ``` ### Some other configuration options for **slug** annotation: - **fields** (required, default=[]) - list of fields for slug - **updatable** (optional, default=true) - **true** to update the slug on sluggable field changes, **false** - otherwise - **unique** (optional, default=true) - **true** if slug should be unique and if identical it will be prefixed, **false** - otherwise - **separator** (optional, default="-") - separator which will separate words in slug - **style** (optional, default="default") - **"default"** all letters will be lowercase, **"camel"** - first word letter will be uppercase ### Example ``` php setTitle('the title'); $article->setCode('my code'); $this->em->persist($article); $this->em->flush(); echo $article->getSlug(); // prints: The_Title_My_Code ``` ## Custom transliterator To set your own custom transliterator, which would be used to generate the slug, use: ``` php setTransliterator($callable); // or use a closure $callable = function($text, $separatorUsed, $objectBeingSlugged) { // ... return $transliteratedText; }; $sluggableListener->setTransliterator($callable); ``` ## Advanced examples: ### Regenerating slug In case if you want the slug to regenerate itself based on sluggable fields. Set the slug to **null** or empty string. ``` php find('Entity\Something', $id); $entity->setSlug(''); $em->persist($entity); $em->flush(); ``` ### Setting the slug manually Sometimes you might need to set it manually, etc if generated one does not look satisfying enough. Sluggable will ensure uniqueness of the slug. ``` php setSluggableField('won't be taken into account'); $entity->setSlug('the required slug, set manually'); $em->persist($entity); $em->flush(); echo $entity->getSlug(); // outputs: "the-required-slug-set-manually" ``` ### Using TranslationListener to translate our slug If you want to attach **TranslationListener** also add it to EventManager after the **SluggableListener**. It is important because slug must be generated first before the creation of it`s translation. ``` php addEventSubscriber($sluggableListener); $translatableListener = new \Gedmo\Translatable\TranslationListener(); $translatableListener->setTranslatableLocale('en_us'); $evm->addEventSubscriber($translatableListener); // now this event manager should be passed to entity manager constructor ``` And the Entity should look like: ``` php id; } public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } public function setCode($code) { $this->code = $code; } public function getCode() { return $this->code; } public function getSlug() { return $this->slug; } public function getUniqueSlug() { return $this->uniqueSlug; } } ``` Now the generated slug will be translated by Translatable behavior Any suggestions on improvements are very welcome