Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ a release.

## [Unreleased]

### Changed
- Replace the `behat/transliterator` with `symfony/string` for the Sluggable extension for its default transliteration and urlization steps"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Replace the `behat/transliterator` with `symfony/string` for the Sluggable extension for its default transliteration and urlization steps"
- Sluggable: Replaced abandoned `behat/transliterator` with `symfony/string` for default transliteration and urlization steps (#2985)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks updated


## [3.20.1] - 2025-09-14
### Fixed
- Compatibility with `doctrine/mongodb-odm` ^2.11 (#2945)
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,17 @@
},
"require": {
"php": "^7.4 || ^8.0",
"behat/transliterator": "^1.2",
"doctrine/collections": "^1.2 || ^2.0",
"doctrine/deprecations": "^1.0",
"doctrine/event-manager": "^1.2 || ^2.0",
"doctrine/persistence": "^2.2 || ^3.0 || ^4.0",
"psr/cache": "^1 || ^2 || ^3",
"psr/clock": "^1",
"symfony/cache": "^5.4 || ^6.0 || ^7.0"
"symfony/cache": "^5.4 || ^6.0 || ^7.0",
"symfony/string": "^5.4 || ^6.0 || ^7.0"
},
"require-dev": {
"behat/transliterator": "^1.2",
"doctrine/annotations": "^1.13 || ^2.0",
"doctrine/cache": "^1.11 || ^2.0",
"doctrine/common": "^2.13 || ^3.0",
Expand All @@ -72,6 +73,7 @@
"symfony/yaml": "^5.4 || ^6.0 || ^7.0"
},
"conflict": {
"behat/transliterator": "<1.2 || >=2.0",
"doctrine/annotations": "<1.13 || >=3.0",
"doctrine/common": "<2.13 || >=4.0",
"doctrine/dbal": "<3.7 || >=5.0",
Expand Down
8 changes: 0 additions & 8 deletions src/Sluggable/Handler/InversedRelativeSlugHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,6 @@ class InversedRelativeSlugHandler implements SlugHandlerInterface
*/
protected $sluggable;

/**
* $options = array(
* 'relationClass' => 'objectclass',
* 'inverseSlugField' => 'slug',
* 'mappedBy' => 'relationField'
* )
* {@inheritdoc}
*/
public function __construct(SluggableListener $sluggable)
{
$this->sluggable = $sluggable;
Expand Down
20 changes: 8 additions & 12 deletions src/Sluggable/Handler/RelativeSlugHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,15 @@ class RelativeSlugHandler implements SlugHandlerInterface
*
* @var array<string, mixed>
*/
private $usedOptions;
private array $usedOptions = [];

/**
* Callable of original transliterator
* which is used by sluggable
* Callable of original transliterator which is used by the sluggable listener.
*
* @var callable
* @var callable(string, string, object): string
*/
private $originalTransliterator;

/**
* $options = array(
* 'separator' => '/',
* 'relationField' => 'something',
* 'relationSlugField' => 'slug'
* )
* {@inheritdoc}
*/
public function __construct(SluggableListener $sluggable)
{
$this->sluggable = $sluggable;
Expand Down Expand Up @@ -120,6 +111,10 @@ public function transliterate($text, $separator, $object)
$this->originalTransliterator,
[$text, $separator, $object]
);
$result = call_user_func_array(
$this->sluggable->getUrlizer(),
[$result, $separator, $object]
);
$wrapped = AbstractWrapper::wrap($object, $this->om);
$relation = $wrapped->getPropertyValue($this->usedOptions['relationField']);
if ($relation) {
Expand All @@ -135,6 +130,7 @@ public function transliterate($text, $separator, $object)

$result = $slug.$this->usedOptions['separator'].$result;
}

$this->sluggable->setTransliterator($this->originalTransliterator);

return $result;
Expand Down
30 changes: 8 additions & 22 deletions src/Sluggable/Handler/TreeSlugHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
use Gedmo\Sluggable\SluggableListener;
use Gedmo\Tool\Wrapper\AbstractWrapper;

use function Symfony\Component\String\u;

/**
* Sluggable handler which slugs all parent nodes
* recursively and synchronizes on updates. For instance
Expand All @@ -40,36 +42,24 @@ class TreeSlugHandler implements SlugHandlerWithUniqueCallbackInterface
*/
protected $sluggable;

/**
* @var string
*/
private $prefix;
private string $prefix = '';

/**
* @var string
*/
private $suffix;
private string $suffix = '';

/**
* True if node is being inserted
*
* @var bool
*/
private $isInsert = false;
private bool $isInsert = false;

/**
* Transliterated parent slug
*
* @var string
*/
private $parentSlug;
private string $parentSlug = '';

/**
* Used path separator
*
* @var string
*/
private $usedPathSeparator;
private string $usedPathSeparator = self::SEPARATOR;

public function __construct(SluggableListener $sluggable)
{
Expand Down Expand Up @@ -106,11 +96,7 @@ public function postSlugBuild(SluggableAdapter $ea, array &$config, $object, &$s

// if needed, remove suffix from parentSlug, so we can use it to prepend it to our slug
if (isset($options['suffix'])) {
$suffix = $options['suffix'];

if (substr($this->parentSlug, -strlen($suffix)) === $suffix) { // endsWith
$this->parentSlug = substr_replace($this->parentSlug, '', -1 * strlen($suffix));
}
$this->parentSlug = u($this->parentSlug)->trimSuffix($options['suffix'])->toString();
}
}
}
Expand Down
57 changes: 37 additions & 20 deletions src/Sluggable/SluggableListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
use Gedmo\Sluggable\Handler\SlugHandlerInterface;
use Gedmo\Sluggable\Handler\SlugHandlerWithUniqueCallbackInterface;
use Gedmo\Sluggable\Mapping\Event\SluggableAdapter;
use Gedmo\Sluggable\Util\Urlizer;
use Symfony\Component\String\Slugger\AsciiSlugger;

use function Symfony\Component\String\u;

/**
* The SluggableListener handles the generation of slugs
Expand Down Expand Up @@ -60,6 +62,7 @@
* relationField?: string,
* relationSlugField?: string,
* separator?: string,
* urilize?: bool,
* }>,
* uniqueOverTranslations: bool,
* useObjectClass?: class-string,
Expand All @@ -78,20 +81,16 @@ class SluggableListener extends MappedEventSubscriber
/**
* Transliteration callback for slugs
*
* @var callable
*
* @phpstan-var callable(string $text, string $separator, object $object): string
* @var callable(string, string, object): string
*/
private $transliterator = [Urlizer::class, 'transliterate'];
private $transliterator;

/**
* Urlize callback for slugs
*
* @var callable
*
* @phpstan-var callable(string $text, string $separator, object $object): string
* @var callable(string, string, object): string
*/
private $urlizer = [Urlizer::class, 'urlize'];
private $urlizer;

/**
* List of inserted slugs for each object class.
Expand Down Expand Up @@ -121,6 +120,28 @@ class SluggableListener extends MappedEventSubscriber
*/
private array $managedFilters = [];

public function __construct()
{
parent::__construct();

$this->setTransliterator(
static fn (string $text, string $separator, object $object): string => u($text)->ascii()->toString()
);

/*
* Note - Requiring the call to `lower()` in this chain contradicts with the `style` configuration
* which doesn't require or enforce lowercase styling by default, but the Behat transliterator applied
* this styling so it is used for B/C
*/

$this->setUrlizer(
static fn (string $text, string $separator, object $object): string => (new AsciiSlugger())
->slug($text, $separator)
->lower()
->toString()
);
}

/**
* Specifies the list of events to listen
*
Expand Down Expand Up @@ -412,25 +433,21 @@ private function generateSlug(SluggableAdapter $ea, object $object): void
switch ($options['style']) {
case 'camel':
$quotedSeparator = preg_quote($options['separator']);
$slug = preg_replace_callback('/^[a-z]|'.$quotedSeparator.'[a-z]/smi', static fn ($m) => strtoupper($m[0]), $slug);
$slug = preg_replace_callback(
'/^[a-z]|'.$quotedSeparator.'[a-z]/smi',
static fn (array $m): string => u($m[0])->upper()->toString(),
$slug
);

break;

case 'lower':
if (function_exists('mb_strtolower')) {
$slug = mb_strtolower($slug);
} else {
$slug = strtolower($slug);
}
$slug = u($slug)->lower()->toString();

break;

case 'upper':
if (function_exists('mb_strtoupper')) {
$slug = mb_strtoupper($slug);
} else {
$slug = strtoupper($slug);
}
$slug = u($slug)->upper()->toString();

break;

Expand Down
7 changes: 7 additions & 0 deletions src/Sluggable/Util/Urlizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@
namespace Gedmo\Sluggable\Util;

use Behat\Transliterator\Transliterator;
use Gedmo\Exception\RuntimeException;

if (!class_exists(Transliterator::class)) {
throw new RuntimeException(sprintf('Cannot use the "%s" class when the "behat/transliterator" package is not installed.', Urlizer::class));
}

/**
* Transliteration utility
*
* @deprecated since gedmo/doctrine-extensions 3.21, will be removed in version 4.0.
*
* @final since gedmo/doctrine-extensions 3.11
*/
class Urlizer extends Transliterator
Expand Down
24 changes: 6 additions & 18 deletions tests/Gedmo/Sluggable/Fixture/Handler/People/Occupation.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,14 @@
class Occupation
{
/**
* @var int|null
*
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
private $id;
private ?int $id = null;

/**
* @ORM\Column(length=64)
Expand All @@ -48,8 +46,6 @@ class Occupation
private ?string $title = null;

/**
* @var string|null
*
* @Gedmo\Slug(handlers={
* @Gedmo\SlugHandler(class="Gedmo\Sluggable\Handler\TreeSlugHandler", options={
* @Gedmo\SlugHandlerOption(name="parentRelationField", value="parent"),
Expand All @@ -68,7 +64,7 @@ class Occupation
#[Gedmo\SlugHandler(class: TreeSlugHandler::class, options: ['parentRelationField' => 'parent', 'separator' => '/'])]
#[Gedmo\SlugHandler(class: InversedRelativeSlugHandler::class, options: ['relationClass' => Person::class, 'mappedBy' => 'occupation', 'inverseSlugField' => 'slug'])]
#[ORM\Column(length: 64, unique: true)]
private $slug;
private ?string $slug = null;

/**
* @Gedmo\TreeParent
Expand All @@ -87,48 +83,40 @@ class Occupation
private Collection $children;

/**
* @var int|null
*
* @Gedmo\TreeLeft
*
* @ORM\Column(type="integer")
*/
#[ORM\Column(type: Types::INTEGER)]
#[Gedmo\TreeLeft]
private $lft;
private ?int $lft = null;

/**
* @var int|null
*
* @Gedmo\TreeRight
*
* @ORM\Column(type="integer")
*/
#[ORM\Column(type: Types::INTEGER)]
#[Gedmo\TreeRight]
private $rgt;
private ?int $rgt = null;

/**
* @var int|null
*
* @Gedmo\TreeRoot
*
* @ORM\Column(type="integer")
*/
#[ORM\Column(type: Types::INTEGER)]
#[Gedmo\TreeRoot]
private $root;
private ?int $root = null;

/**
* @var int|null
*
* @Gedmo\TreeLevel
*
* @ORM\Column(name="lvl", type="integer")
*/
#[ORM\Column(name: 'lvl', type: Types::INTEGER)]
#[Gedmo\TreeLevel]
private $level;
private ?int $level = null;

public function __construct()
{
Expand Down
Loading