Просмотр исходного кода

[sluggable] relative slug handler experimental implementation

gediminasm 14 лет назад
Родитель
Сommit
4b75f30024

+ 44 - 15
lib/Gedmo/Sluggable/Handler/RelativeSlugHandler.php

@@ -32,6 +32,8 @@ class RelativeSlugHandler implements SlugHandlerInterface
 
     private $parts = array();
 
+    private $isInsert = false;
+
     /**
      * {@inheritDoc}
      */
@@ -49,21 +51,26 @@ class RelativeSlugHandler implements SlugHandlerInterface
     /**
      * {@inheritDoc}
      */
-    public function postSlugBuild(SluggableAdapter $ea, $field, $object, &$slug)
+    public function postSlugBuild(SluggableAdapter $ea, array &$config, $object, &$slug)
     {
         $this->originalTransliterator = $this->sluggable->getTransliterator();
         $this->sluggable->setTransliterator(array($this, 'transliterate'));
         $this->parts = array();
+        $this->isInsert = $this->om->getUnitOfWork()->isScheduledForInsert($object);
+
         $wrapped = AbstractWrapper::wrapp($object, $this->om);
-        do {
-            $relation = $wrapped->getPropertyValue($this->options['relation']);
-            if ($relation) {
-                $wrappedRelation = AbstractWrapper::wrapp($relation, $this->om);
-                array_unshift($this->parts, $wrappedRelation->getPropertyValue($this->options['targetField']));
-                $wrapped = $wrappedRelation;
-            }
-        } while ($this->options['recursive'] && $relation);
-        //var_dump($slug);
+        if ($this->isInsert) {
+            do {
+                $relation = $wrapped->getPropertyValue($this->options['relation']);
+                if ($relation) {
+                    $wrappedRelation = AbstractWrapper::wrapp($relation, $this->om);
+                    array_unshift($this->parts, $wrappedRelation->getPropertyValue($this->options['targetField']));
+                    $wrapped = $wrappedRelation;
+                }
+            } while ($this->options['recursive'] && $relation);
+        } else {
+            $this->parts = explode($this->options['separator'], $wrapped->getPropertyValue($config['slug']));
+        }
     }
 
     /**
@@ -79,14 +86,36 @@ class RelativeSlugHandler implements SlugHandlerInterface
         }*/
     }
 
-    public function transliterate($text, $separator, $object)
+    /**
+     * {@inheritDoc}
+     */
+    public function onSlugCompletion(SluggableAdapter $ea, array &$config, $object, &$slug)
     {
-        foreach ($this->parts as &$part) {
-            $part = call_user_func_array(
-                $this->originalTransliterator,
-                array($part, $separator, $object)
+        if (!$this->isInsert) {
+            $wrapped = AbstractWrapper::wrapp($object, $this->om);
+            $extConfig = $this->sluggable->getConfiguration($this->om, $wrapped->getMetadata()->name);
+            $config['useObjectClass'] = $extConfig['useObjectClass'];
+            $ea->replaceRelative(
+                $object,
+                $config,
+                $wrapped->getPropertyValue($config['slug']).$this->options['separator'],
+                $slug
             );
         }
+    }
+
+    public function transliterate($text, $separator, $object)
+    {
+        if ($this->isInsert) {
+            foreach ($this->parts as &$part) {
+                $part = call_user_func_array(
+                    $this->originalTransliterator,
+                    array($part, $separator, $object)
+                );
+            }
+        } else {
+            array_pop($this->parts);
+        }
         $this->parts[] = call_user_func_array(
             $this->originalTransliterator,
             array($text, $separator, $object)

+ 13 - 2
lib/Gedmo/Sluggable/Handler/SlugHandlerInterface.php

@@ -22,12 +22,23 @@ interface SlugHandlerInterface
      * Callback on slug handlers right after the slug is built
      *
      * @param Gedmo\Sluggable\Mapping\Event\SluggableAdapter $ea
-     * @param string $field
+     * @param array $config
      * @param object $object
      * @param string $slug
      * @return void
      */
-    function postSlugBuild(SluggableAdapter $ea, $field, $object, &$slug);
+    function postSlugBuild(SluggableAdapter $ea, array &$config, $object, &$slug);
+
+    /**
+     * Callback for slug handlers on slug completion
+     *
+     * @param Gedmo\Sluggable\Mapping\Event\SluggableAdapter $ea
+     * @param array $config
+     * @param object $object
+     * @param string $slug
+     * @return void
+     */
+    function onSlugCompletion(SluggableAdapter $ea, array &$config, $object, &$slug);
 
     /**
      * Validate handler options

+ 19 - 0
lib/Gedmo/Sluggable/Mapping/Event/Adapter/ORM.php

@@ -48,4 +48,23 @@ final class ORM extends BaseAdapterORM implements SluggableAdapter
         $q->setHydrationMode(Query::HYDRATE_ARRAY);
         return $q->execute();
     }
+
+    public function replaceRelative($object, array $config, $target, $replacement)
+    {
+        $em = $this->getObjectManager();
+        $qb = $em->createQueryBuilder();
+        $qb->update($config['useObjectClass'], 'rec')
+            ->set('rec.'.$config['slug'], $qb->expr()->concat(
+                $qb->expr()->literal($replacement),
+                $qb->expr()->substring('rec.'.$config['slug'], strlen($target))
+            ))
+            ->where($qb->expr()->like(
+                'rec.'.$config['slug'],
+                $qb->expr()->literal($target . '%'))
+            )
+        ;
+        // update in memory
+        $q = $qb->getQuery();
+        return $q->execute();
+    }
 }

+ 11 - 2
lib/Gedmo/Sluggable/SluggableListener.php

@@ -201,17 +201,17 @@ class SluggableListener extends MappedEventSubscriber
                     throw new \Gedmo\Exception\UnexpectedValueException("Unable to find any non empty sluggable fields for slug [{$slugField}] , make sure they have something at least.");
                 }
 
+                $slugFieldConfig = $config['slugFields'][$slugField];
                 // notify slug handlers --> postSlugBuild
                 if (isset($config['handlers'])) {
                     foreach ($config['handlers'] as $class => $options) {
                         $this
                             ->getHandler($class, $om, $options)
-                            ->postSlugBuild($ea, $slugField, $object, $slug)
+                            ->postSlugBuild($ea, $slugFieldConfig, $object, $slug)
                         ;
                     }
                 }
 
-                $slugFieldConfig = $config['slugFields'][$slugField];
                 // build the slug
                 $slug = call_user_func_array(
                     $this->transliterator,
@@ -245,6 +245,15 @@ class SluggableListener extends MappedEventSubscriber
                     $arrayConfig['useObjectClass'] = $config['useObjectClass'];
                     $slug = $this->makeUniqueSlug($ea, $object, $slug, false, $arrayConfig);
                 }
+                // notify slug handlers --> onSlugCompletion
+                if (isset($config['handlers'])) {
+                    foreach ($config['handlers'] as $class => $options) {
+                        $this
+                            ->getHandler($class, $om, $options)
+                            ->onSlugCompletion($ea, $slugFieldConfig, $object, $slug)
+                        ;
+                    }
+                }
                 // set the final slug
                 $meta->getReflectionProperty($slugFieldConfig['slug'])->setValue($object, $slug);
                 // recompute changeset

+ 1 - 1
tests/Gedmo/Sluggable/Fixture/RelativeSlug.php

@@ -31,7 +31,7 @@ class RelativeSlug
      *          @Gedmo\SlugHandlerOption(name="targetField", value="title"),
      *          @Gedmo\SlugHandlerOption(name="separator", value="/")
      *      })
-     * }, separator="-", updatable=false)
+     * }, separator="-", updatable=true)
      * @ORM\Column(name="slug", type="string", length=64, unique=true)
      */
     private $slug;

+ 26 - 1
tests/Gedmo/Sluggable/RelativeSlugHandlerTest.php

@@ -24,8 +24,8 @@ class RelativeSlugHandlerTest extends BaseTestCaseORM
         parent::setUp();
 
         $evm = new EventManager;
-        $evm->addEventSubscriber(new TreeListener);
         $evm->addEventSubscriber(new SluggableListener);
+        $evm->addEventSubscriber(new TreeListener);
 
         $conn = array(
             'driver' => 'pdo_mysql',
@@ -41,6 +41,31 @@ class RelativeSlugHandlerTest extends BaseTestCaseORM
     public function testSlugGeneration()
     {
         $this->populate();
+        $repo = $this->em->getRepository(self::TARGET);
+
+        $food = $repo->findOneByTitle('Food');
+        $this->assertEquals('food', $food->getSlug());
+
+        $fruits = $repo->findOneByTitle('Fruits');
+        $this->assertEquals('food/fruits', $fruits->getSlug());
+
+        $oranges = $repo->findOneByTitle('Oranges');
+        $this->assertEquals('food/fruits/oranges', $oranges->getSlug());
+
+        $citrons = $repo->findOneByTitle('Citrons');
+        $this->assertEquals('food/fruits/citrons', $citrons->getSlug());
+    }
+
+    public function testSlugUpdates()
+    {
+        $this->populate();
+        $repo = $this->em->getRepository(self::TARGET);
+
+        $fruits = $repo->findOneByTitle('Fruits');
+        $fruits->setTitle('Fructis');
+
+        $this->em->persist($fruits);
+        $this->em->flush();
     }
 
     protected function getUsedEntityFixtures()