Browse Source

[sluggable] allow identifier fields to be slugged

gedi 13 years ago
parent
commit
d40b449d6a

+ 3 - 0
lib/Gedmo/Sluggable/Mapping/Driver/Annotation.php

@@ -133,6 +133,9 @@ class Annotation implements AnnotationDriverInterface
                 if (!is_bool($slug->unique)) {
                     throw new InvalidMappingException("Slug annotation [unique], type is not valid and must be 'boolean' in class - {$meta->name}");
                 }
+                if ($meta->isIdentifier($field) && !(bool)$slug->unique) {
+                    throw new InvalidMappingException("Identifier field - [{$field}] slug must be unique in order to maintain primary key in class - {$meta->name}");
+                }
                 // set all options
                 $config['slugs'][$field] = array(
                     'fields' => $slug->fields,

+ 3 - 0
lib/Gedmo/Sluggable/Mapping/Driver/Xml.php

@@ -96,6 +96,9 @@ class Xml extends BaseXml
                             $this->_getAttribute($slug, 'separator') : '-',
                         'handlers' => $handlers
                     );
+                    if ($meta->isIdentifier($field) && !$config['slugs'][$field]['unique']) {
+                        throw new InvalidMappingException("Identifier field - [{$field}] slug must be unique in order to maintain primary key in class - {$meta->name}");
+                    }
                 }
             }
         }

+ 5 - 0
lib/Gedmo/Sluggable/Mapping/Driver/Yaml.php

@@ -75,6 +75,7 @@ class Yaml extends File implements Driver
                                 throw new InvalidMappingException("Cannot use field - [{$slugField}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}");
                             }
                         }
+
                         $config['slugs'][$field]['fields'] = $slug['fields'];
                         $config['slugs'][$field]['handlers'] = $handlers;
                         $config['slugs'][$field]['slug'] = $field;
@@ -89,6 +90,10 @@ class Yaml extends File implements Driver
 
                         $config['slugs'][$field]['separator'] = isset($slug['separator']) ?
                             (string)$slug['separator'] : '-';
+
+                        if ($meta->isIdentifier($field) && !$config['slugs'][$field]['unique']) {
+                            throw new InvalidMappingException("Identifier field - [{$field}] slug must be unique in order to maintain primary key in class - {$meta->name}");
+                        }
                     }
                 }
             }

+ 24 - 1
lib/Gedmo/Sluggable/SluggableListener.php

@@ -63,7 +63,8 @@ class SluggableListener extends MappedEventSubscriber
     {
         return array(
             'onFlush',
-            'loadClassMetadata'
+            'loadClassMetadata',
+            'prePersist'
         );
     }
 
@@ -103,6 +104,28 @@ class SluggableListener extends MappedEventSubscriber
         $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
     }
 
+    /**
+     * Allows identifier fields to be slugged as usual
+     *
+     * @param EventArgs $args
+     * @return void
+     */
+    public function prePersist(EventArgs $args)
+    {
+        $ea = $this->getEventAdapter($args);
+        $om = $ea->getObjectManager();
+        $object = $ea->getObject();
+        $meta = $om->getClassMetadata(get_class($object));
+
+        if ($config = $this->getConfiguration($om, $meta->name)) {
+            foreach ($config['slugs'] as $slugField => $options) {
+                if ($meta->isIdentifier($slugField)) {
+                    $meta->getReflectionProperty($slugField)->setValue($object, '__id__');
+                }
+            }
+        }
+    }
+
     /**
      * Generate slug on objects being updated during flush
      * if they require changing

+ 40 - 0
tests/Gedmo/Sluggable/Fixture/Identifier.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Sluggable\Fixture;
+
+use Gedmo\Mapping\Annotation as Gedmo;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Entity
+ */
+class Identifier
+{
+    /**
+     * @ORM\Id
+     * @Gedmo\Slug(separator="_", updatable=false, fields={"title"})
+     * @ORM\Column(length=32, unique=true)
+     */
+    private $id;
+
+    /**
+     * @ORM\Column(length=32)
+     */
+    private $title;
+
+
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    public function setTitle($title)
+    {
+        $this->title = $title;
+    }
+
+    public function getTitle()
+    {
+        return $this->title;
+    }
+}

+ 68 - 0
tests/Gedmo/Sluggable/SluggableIdentifierTest.php

@@ -0,0 +1,68 @@
+<?php
+
+namespace Gedmo\Sluggable;
+
+use Doctrine\Common\EventManager;
+use Tool\BaseTestCaseORM;
+use Sluggable\Fixture\Identifier;
+
+/**
+ * These are tests for Sluggable behavior
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Sluggable
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class SluggableIdentifierTest extends BaseTestCaseORM
+{
+    const TARGET = 'Sluggable\\Fixture\\Identifier';
+
+    protected function setUp()
+    {
+        parent::setUp();
+
+        $evm = new EventManager;
+        $evm->addEventSubscriber(new SluggableListener);
+
+        $this->getMockSqliteEntityManager($evm);
+    }
+
+    /**
+     * @test
+     */
+    function shouldBePossibleToSlugIdentifiers()
+    {
+        $sport = new Identifier;
+        $sport->setTitle('Sport');
+        $this->em->persist($sport);
+        $this->em->flush();
+
+        $this->assertEquals('sport', $sport->getId());
+    }
+
+    /**
+     * @test
+     */
+    function shouldPersistMultipleNonConflictingIdentifierSlugs()
+    {
+        $sport = new Identifier;
+        $sport->setTitle('Sport');
+        $this->em->persist($sport);
+
+        $sport2 = new Identifier;
+        $sport2->setTitle('Sport');
+        $this->em->persist($sport2);
+        $this->em->flush();
+
+        $this->assertEquals('sport', $sport->getId());
+        $this->assertEquals('sport_1', $sport2->getId());
+    }
+
+    protected function getUsedEntityFixtures()
+    {
+        return array(
+            self::TARGET,
+        );
+    }
+}