|
@@ -2,11 +2,11 @@
|
|
|
|
|
|
namespace Gedmo\Sluggable;
|
|
|
|
|
|
-use Doctrine\Common\EventArgs,
|
|
|
- Doctrine\Common\Persistence\ObjectManager,
|
|
|
- Doctrine\Common\Persistence\Mapping\ClassMetadata,
|
|
|
- Gedmo\Mapping\MappedEventSubscriber,
|
|
|
- Gedmo\Sluggable\Mapping\Event\SluggableAdapter;
|
|
|
+use Doctrine\Common\EventArgs;
|
|
|
+use Doctrine\Common\Persistence\ObjectManager;
|
|
|
+use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
|
|
+use Gedmo\Mapping\MappedEventSubscriber;
|
|
|
+use Gedmo\Sluggable\Mapping\Event\SluggableAdapter;
|
|
|
|
|
|
/**
|
|
|
* The SluggableListener handles the generation of slugs
|
|
@@ -24,20 +24,6 @@ use Doctrine\Common\EventArgs,
|
|
|
*/
|
|
|
class SluggableListener extends MappedEventSubscriber
|
|
|
{
|
|
|
- /**
|
|
|
- * Specifies the list of events to listen
|
|
|
- *
|
|
|
- * @return array
|
|
|
- */
|
|
|
- public function getSubscribedEvents()
|
|
|
- {
|
|
|
- return array(
|
|
|
- 'prePersist',
|
|
|
- 'onFlush',
|
|
|
- 'loadClassMetadata'
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* The power exponent to jump
|
|
|
* the slug unique number by tens.
|
|
@@ -53,6 +39,28 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
*/
|
|
|
private $transliterator = array('Gedmo\Sluggable\Util\Urlizer', 'transliterate');
|
|
|
|
|
|
+ /**
|
|
|
+ * List of inserted slugs for each object class.
|
|
|
+ * This is needed in case there are identical slug
|
|
|
+ * composition in number of persisted objects
|
|
|
+ *
|
|
|
+ * @var array
|
|
|
+ */
|
|
|
+ private $persistedSlugs = array();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Specifies the list of events to listen
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function getSubscribedEvents()
|
|
|
+ {
|
|
|
+ return array(
|
|
|
+ 'onFlush',
|
|
|
+ 'loadClassMetadata'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Set the transliteration callable method
|
|
|
* to transliterate slugs
|
|
@@ -68,33 +76,25 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Mapps additional metadata
|
|
|
+ * Get currently used transliterator callable
|
|
|
*
|
|
|
- * @param EventArgs $eventArgs
|
|
|
- * @return void
|
|
|
+ * @return callable
|
|
|
*/
|
|
|
- public function loadClassMetadata(EventArgs $eventArgs)
|
|
|
+ public function getTransliterator()
|
|
|
{
|
|
|
- $ea = $this->getEventAdapter($eventArgs);
|
|
|
- $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
|
|
|
+ return $this->transliterator;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Checks for persisted object to specify slug
|
|
|
+ * Mapps additional metadata
|
|
|
*
|
|
|
- * @param EventArgs $args
|
|
|
+ * @param EventArgs $eventArgs
|
|
|
* @return void
|
|
|
*/
|
|
|
- public function prePersist(EventArgs $args)
|
|
|
+ public function loadClassMetadata(EventArgs $eventArgs)
|
|
|
{
|
|
|
- $ea = $this->getEventAdapter($args);
|
|
|
- $om = $ea->getObjectManager();
|
|
|
- $object = $ea->getObject();
|
|
|
- $meta = $om->getClassMetadata(get_class($object));
|
|
|
-
|
|
|
- if ($config = $this->getConfiguration($om, $meta->name)) {
|
|
|
- $this->generateSlug($ea, $object, false);
|
|
|
- }
|
|
|
+ $ea = $this->getEventAdapter($eventArgs);
|
|
|
+ $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -110,13 +110,25 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
$om = $ea->getObjectManager();
|
|
|
$uow = $om->getUnitOfWork();
|
|
|
|
|
|
+ // process all objects being inserted, using scheduled insertions instead
|
|
|
+ // of prePersist in case if record will be changed before flushing this will
|
|
|
+ // ensure correct result. No additional overhead is encoutered
|
|
|
+ foreach ($ea->getScheduledObjectInsertions($uow) as $object) {
|
|
|
+ $meta = $om->getClassMetadata(get_class($object));
|
|
|
+ if ($config = $this->getConfiguration($om, $meta->name)) {
|
|
|
+ // generate first to exclude this object from similar persisted slugs result
|
|
|
+ $this->generateSlug($ea, $object);
|
|
|
+ $slug = $meta->getReflectionProperty($config['slug'])->getValue($object);
|
|
|
+ $this->persistedSlugs[$meta->name][] = $slug;
|
|
|
+ }
|
|
|
+ }
|
|
|
// we use onFlush and not preUpdate event to let other
|
|
|
// event listeners be nested together
|
|
|
foreach ($ea->getScheduledObjectUpdates($uow) as $object) {
|
|
|
$meta = $om->getClassMetadata(get_class($object));
|
|
|
if ($config = $this->getConfiguration($om, $meta->name)) {
|
|
|
if ($config['updatable']) {
|
|
|
- $this->generateSlug($ea, $object, $ea->getObjectChangeSet($uow, $object));
|
|
|
+ $this->generateSlug($ea, $object);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -135,18 +147,16 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
*
|
|
|
* @param SluggableAdapter $ea
|
|
|
* @param object $object
|
|
|
- * @param mixed $changeSet
|
|
|
- * case array: the change set array
|
|
|
- * case boolean(false): object is not managed
|
|
|
* @throws UnexpectedValueException - if parameters are missing
|
|
|
* or invalid
|
|
|
* @return void
|
|
|
*/
|
|
|
- protected function generateSlug(SluggableAdapter $ea, $object, $changeSet)
|
|
|
+ private function generateSlug(SluggableAdapter $ea, $object)
|
|
|
{
|
|
|
$om = $ea->getObjectManager();
|
|
|
$meta = $om->getClassMetadata(get_class($object));
|
|
|
$uow = $om->getUnitOfWork();
|
|
|
+ $changeSet = $ea->getObjectChangeSet($uow, $object);
|
|
|
$config = $this->getConfiguration($om, $meta->name);
|
|
|
|
|
|
// sort sluggable fields by position
|
|
@@ -162,7 +172,7 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
$slug = '';
|
|
|
$needToChangeSlug = false;
|
|
|
foreach ($fields as $sluggableField) {
|
|
|
- if ($changeSet === false || isset($changeSet[$sluggableField['field']])) {
|
|
|
+ if (isset($changeSet[$sluggableField['field']])) {
|
|
|
$needToChangeSlug = true;
|
|
|
}
|
|
|
$slug .= $meta->getReflectionProperty($sluggableField['field'])->getValue($object) . ' ';
|
|
@@ -210,10 +220,8 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
}
|
|
|
// set the final slug
|
|
|
$meta->getReflectionProperty($config['slug'])->setValue($object, $slug);
|
|
|
- // recompute changeset if object is managed
|
|
|
- if ($changeSet !== false) {
|
|
|
- $ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
|
|
|
- }
|
|
|
+ // recompute changeset
|
|
|
+ $ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -224,7 +232,7 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
* @param string $preferedSlug
|
|
|
* @return string - unique slug
|
|
|
*/
|
|
|
- protected function makeUniqueSlug(SluggableAdapter $ea, $object, $preferedSlug)
|
|
|
+ private function makeUniqueSlug(SluggableAdapter $ea, $object, $preferedSlug)
|
|
|
{
|
|
|
$om = $ea->getObjectManager();
|
|
|
$meta = $om->getClassMetadata(get_class($object));
|
|
@@ -232,6 +240,8 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
|
|
|
// search for similar slug
|
|
|
$result = $ea->getSimilarSlugs($object, $meta, $config, $preferedSlug);
|
|
|
+ // add similar persisted slugs into account
|
|
|
+ $result += $this->getSimilarPersistedSlugs($meta->name, $preferedSlug);
|
|
|
|
|
|
if ($result) {
|
|
|
$generatedSlug = $preferedSlug;
|
|
@@ -259,4 +269,27 @@ class SluggableListener extends MappedEventSubscriber
|
|
|
}
|
|
|
return $preferedSlug;
|
|
|
}
|
|
|
-}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * In case if any number of records are persisted instantly
|
|
|
+ * and they contain same slugs. This method will filter those
|
|
|
+ * identical slugs specialy for persisted objects. Returns
|
|
|
+ * array of similar slugs found
|
|
|
+ *
|
|
|
+ * @param string $class
|
|
|
+ * @param string $preferedSlug
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ private function getSimilarPersistedSlugs($class, $preferedSlug)
|
|
|
+ {
|
|
|
+ $result = array();
|
|
|
+ if (isset($this->persistedSlugs[$class])) {
|
|
|
+ array_walk($this->persistedSlugs[$class], function($val) use ($preferedSlug, &$result) {
|
|
|
+ if (preg_match("/{$preferedSlug}.*/smi", $val)) {
|
|
|
+ $result[] = array('slug' => $val);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return $result;
|
|
|
+ }
|
|
|
+}
|