mirror of
https://github.com/flarum/core.git
synced 2025-07-19 07:41:22 +02:00
feat: discussion UTF-8 slug driver (#3606)
* feat: add utf-8 slug driver * test: add tests for slugging expectations * fix: non-word characters aren't removed Signed-off-by: Sami Mazouz <ilyasmazouz@gmail.com> Co-authored-by: Alexander Skvortsov <sasha.skvortsov109@gmail.com>
This commit is contained in:
@@ -441,6 +441,7 @@ class Discussion extends AbstractModel
|
||||
*
|
||||
* This automatically creates a matching slug for the discussion.
|
||||
*
|
||||
* @todo slug should be set by the slugger, drop slug column entirely?
|
||||
* @param string $title
|
||||
*/
|
||||
protected function setTitleAttribute($title)
|
||||
|
46
framework/core/src/Discussion/Utf8SlugDriver.php
Normal file
46
framework/core/src/Discussion/Utf8SlugDriver.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Flarum\Discussion;
|
||||
|
||||
use Flarum\Database\AbstractModel;
|
||||
use Flarum\Http\SlugDriverInterface;
|
||||
use Flarum\User\User;
|
||||
|
||||
class Utf8SlugDriver implements SlugDriverInterface
|
||||
{
|
||||
/**
|
||||
* @var DiscussionRepository
|
||||
*/
|
||||
protected $discussions;
|
||||
|
||||
public function __construct(DiscussionRepository $discussions)
|
||||
{
|
||||
$this->discussions = $discussions;
|
||||
}
|
||||
|
||||
public function toSlug(AbstractModel $instance): string
|
||||
{
|
||||
$slug = preg_replace('/[-\s]+/u', '-', $instance->title);
|
||||
$slug = preg_replace('/[^\p{L}\p{N}\p{M}_-]+/u', '', $slug);
|
||||
$slug = strtolower($slug);
|
||||
|
||||
return $instance->id.(trim($slug) ? '-'.$slug : '');
|
||||
}
|
||||
|
||||
public function fromSlug(string $slug, User $actor): AbstractModel
|
||||
{
|
||||
if (strpos($slug, '-')) {
|
||||
$slug_array = explode('-', $slug);
|
||||
$slug = $slug_array[0];
|
||||
}
|
||||
|
||||
return $this->discussions->findOrFail($slug, $actor);
|
||||
}
|
||||
}
|
@@ -11,6 +11,7 @@ namespace Flarum\Http;
|
||||
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Discussion\IdWithTransliteratedSlugDriver;
|
||||
use Flarum\Discussion\Utf8SlugDriver;
|
||||
use Flarum\Foundation\AbstractServiceProvider;
|
||||
use Flarum\Settings\SettingsRepositoryInterface;
|
||||
use Flarum\User\IdSlugDriver;
|
||||
@@ -37,7 +38,8 @@ class HttpServiceProvider extends AbstractServiceProvider
|
||||
$this->container->singleton('flarum.http.slugDrivers', function () {
|
||||
return [
|
||||
Discussion::class => [
|
||||
'default' => IdWithTransliteratedSlugDriver::class
|
||||
'default' => IdWithTransliteratedSlugDriver::class,
|
||||
'utf8' => Utf8SlugDriver::class,
|
||||
],
|
||||
User::class => [
|
||||
'default' => UsernameSlugDriver::class,
|
||||
|
93
framework/core/tests/integration/slugger/SlugDriverTest.php
Normal file
93
framework/core/tests/integration/slugger/SlugDriverTest.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Flarum.
|
||||
*
|
||||
* For detailed copyright and license information, please view the
|
||||
* LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace integration\slugger;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Flarum\Discussion\Discussion;
|
||||
use Flarum\Http\SlugManager;
|
||||
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||
use Flarum\Testing\integration\TestCase;
|
||||
use Flarum\User\User;
|
||||
|
||||
class SlugDriverTest extends TestCase
|
||||
{
|
||||
use RetrievesAuthorizedUsers;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->prepareDatabase([
|
||||
'users' => [
|
||||
$this->normalUser(),
|
||||
],
|
||||
'discussions' => [
|
||||
['id' => 20, 'title' => 'Empty discussion', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => null, 'comment_count' => 0, 'is_private' => 0],
|
||||
['id' => 21, 'title' => 'తెలుగు', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => null, 'comment_count' => 0, 'is_private' => 0],
|
||||
['id' => 22, 'title' => '支持中文吗', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => null, 'comment_count' => 0, 'is_private' => 0],
|
||||
['id' => 23, 'title' => 'తెలుగు%$', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => null, 'comment_count' => 0, 'is_private' => 0],
|
||||
['id' => 24, 'title' => '支持中文吗%*', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => null, 'comment_count' => 0, 'is_private' => 0],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider slugInstancePairDataProvider
|
||||
* @test
|
||||
*/
|
||||
public function slugger_formats_the_correct_slug_from_instance(string $driver, string $modelClassName, int $id, string $slug)
|
||||
{
|
||||
$this->setting("slug_driver_$modelClassName", $driver);
|
||||
|
||||
/** @var SlugManager $slugger */
|
||||
$slugger = $this->app()->getContainer()->make(SlugManager::class)->forResource($modelClassName);
|
||||
|
||||
$instance = $modelClassName::query()->find($id);
|
||||
|
||||
/** @see Discussion::setTitleAttribute() */
|
||||
if ($modelClassName === Discussion::class) {
|
||||
$instance->title = $instance->title;
|
||||
}
|
||||
|
||||
$this->assertEquals($slug, $slugger->toSlug($instance));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider slugInstancePairDataProvider
|
||||
* @test
|
||||
*/
|
||||
public function slugger_returns_the_correct_instance_from_slug(string $driver, string $modelClassName, int $id, string $slug)
|
||||
{
|
||||
$this->setting("slug_driver_$modelClassName", $driver);
|
||||
|
||||
/** @var SlugManager $slugger */
|
||||
$slugger = $this->app()->getContainer()->make(SlugManager::class)->forResource($modelClassName);
|
||||
|
||||
$this->assertEquals($modelClassName::query()->find($id), $slugger->fromSlug($slug, User::query()->find(1)));
|
||||
}
|
||||
|
||||
public function slugInstancePairDataProvider(): array
|
||||
{
|
||||
return [
|
||||
['default', Discussion::class, 20, '20-empty-discussion'],
|
||||
['default', Discussion::class, 21, '21'],
|
||||
['default', Discussion::class, 22, '22'],
|
||||
|
||||
['utf8', Discussion::class, 20, '20-empty-discussion'],
|
||||
['utf8', Discussion::class, 21, '21-తెలుగు'],
|
||||
['utf8', Discussion::class, 22, '22-支持中文吗'],
|
||||
['utf8', Discussion::class, 23, '23-తెలుగు'],
|
||||
['utf8', Discussion::class, 24, '24-支持中文吗'],
|
||||
|
||||
['default', User::class, 2, 'normal'],
|
||||
['id', User::class, 2, '2'],
|
||||
];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user