Skip to content

Commit 32fb3e9

Browse files
authored
feat(support): adds string pluralizer
1 parent 4785aea commit 32fb3e9

File tree

10 files changed

+195
-2
lines changed

10 files changed

+195
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- Add `LanguageHelper::pluralize` and `LanguageHelper::singularize`
1213
- Unit tests for the CommandBus `dispatch` method.
1314
- Replaced Ignition with Whoops.
1415
- Properly detect environment from `.env` when present.
15-
- `discovery:cache` command that will generate the discovery cache
16+
- `discovery:cache` command that will generate the discovery cache

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"description": "The PHP framework that gets out of your way.",
44
"license": "MIT",
55
"require": {
6+
"doctrine/inflector": "^2.0",
67
"egulias/email-validator": "^4.0.2",
78
"ext-dom": "*",
89
"ext-fileinfo": "*",

src/Tempest/Support/composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"license": "MIT",
44
"minimum-stability": "dev",
55
"require": {
6-
"php": "^8.3"
6+
"php": "^8.3",
7+
"doctrine/inflector": "^2.0",
8+
"tempest/container": "self.version"
79
},
810
"autoload": {
911
"psr-4": {

src/Tempest/Support/src/LanguageHelper.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
namespace Tempest\Support;
66

7+
use Countable;
8+
use function Tempest\get;
9+
use Tempest\Support\Pluralizer\Pluralizer;
10+
711
final class LanguageHelper
812
{
913
/**
@@ -19,4 +23,14 @@ public static function join(array $parts): string
1923

2024
return $last;
2125
}
26+
27+
public static function pluralize(string $value, int|array|Countable $count = 2): string
28+
{
29+
return get(Pluralizer::class)->pluralize($value, $count);
30+
}
31+
32+
public static function singularize(string $value): string
33+
{
34+
return get(Pluralizer::class)->singularize($value);
35+
}
2236
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Support\Pluralizer;
6+
7+
use Countable;
8+
use Doctrine\Inflector\Inflector;
9+
use Doctrine\Inflector\InflectorFactory;
10+
11+
final class InflectorPluralizer implements Pluralizer
12+
{
13+
private Inflector $inflector;
14+
15+
public function __construct(string $language = 'english')
16+
{
17+
$this->inflector = InflectorFactory::createForLanguage($language)->build();
18+
}
19+
20+
public function pluralize(string $value, int|array|Countable $count = 2): string
21+
{
22+
if (is_countable($count)) {
23+
$count = count($count);
24+
}
25+
26+
if ((int) abs($count) === 1 || preg_match('/^(.*)[A-Za-z0-9\x{0080}-\x{FFFF}]$/u', $value) == 0) {
27+
return $value;
28+
}
29+
30+
return $this->matchCase($this->inflector->pluralize($value), $value);
31+
}
32+
33+
public function singularize(string $value): string
34+
{
35+
return $this->matchCase($this->inflector->singularize($value), $value);
36+
}
37+
38+
private function matchCase(string $value, string $comparison): string
39+
{
40+
$functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords'];
41+
42+
foreach ($functions as $function) {
43+
if ($function($comparison) === $comparison) {
44+
return $function($value);
45+
}
46+
}
47+
48+
return $value;
49+
}
50+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Support\Pluralizer;
6+
7+
use Countable;
8+
9+
interface Pluralizer
10+
{
11+
public function pluralize(string $value, int|array|Countable $count = 2): string;
12+
13+
public function singularize(string $value): string;
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Support\Pluralizer;
6+
7+
use Tempest\Container\Container;
8+
use Tempest\Container\Initializer;
9+
10+
final readonly class PluralizerInitializer implements Initializer
11+
{
12+
public function initialize(Container $container): Pluralizer
13+
{
14+
return new InflectorPluralizer();
15+
}
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Support\Tests;
6+
7+
use PHPUnit\Framework\Attributes\TestWith;
8+
use PHPUnit\Framework\TestCase;
9+
use Tempest\Support\LanguageHelper;
10+
11+
/**
12+
* @internal
13+
* @small
14+
*/
15+
final class LanguageHelperTest extends TestCase
16+
{
17+
#[TestWith([['Jon', 'Jane'], 'Jon and Jane'])]
18+
#[TestWith([['Jon', 'Jane', 'Jill'], 'Jon, Jane and Jill'])]
19+
public function test_join(array $parts, string $expected): void
20+
{
21+
$this->assertEquals($expected, LanguageHelper::join($parts));
22+
}
23+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Support\Tests\Pluralizer;
6+
7+
use Countable;
8+
use PHPUnit\Framework\Attributes\TestWith;
9+
use PHPUnit\Framework\TestCase;
10+
use Tempest\Support\Pluralizer\InflectorPluralizer;
11+
12+
/**
13+
* @internal
14+
* @small
15+
*/
16+
final class InflectorPluralizerTest extends TestCase
17+
{
18+
#[TestWith(['migration', 'migrations', 0])]
19+
#[TestWith(['migration', 'migration', 1])]
20+
#[TestWith(['Migration', 'Migrations', 2])]
21+
#[TestWith(['migration', 'migrations', 2])]
22+
#[TestWith(['migration', 'migrations', [1, 2]])]
23+
public function test_that_pluralizer_pluralizes(string $value, string $expected, int|array|Countable $count): void
24+
{
25+
$pluralizer = new InflectorPluralizer();
26+
27+
$this->assertEquals($expected, $pluralizer->pluralize($value, $count));
28+
}
29+
30+
#[TestWith(['Migrations', 'Migration'])]
31+
#[TestWith(['migrations', 'migration'])]
32+
public function test_that_pluralizer_singularizes(string $value, string $expected): void
33+
{
34+
$pluralizer = new InflectorPluralizer();
35+
36+
$this->assertEquals($expected, $pluralizer->singularize($value));
37+
}
38+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Tempest\Integration\Support;
6+
7+
use Countable;
8+
use PHPUnit\Framework\Attributes\TestWith;
9+
use Tempest\Support\LanguageHelper;
10+
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
11+
12+
/**
13+
* @internal
14+
* @small
15+
*/
16+
final class LanguageHelperTest extends FrameworkIntegrationTestCase
17+
{
18+
#[TestWith(['migration', 'migrations', 0])]
19+
#[TestWith(['migration', 'migration', 1])]
20+
#[TestWith(['Migration', 'Migrations', 2])]
21+
#[TestWith(['migration', 'migrations', 2])]
22+
#[TestWith(['migration', 'migrations', [1, 2]])]
23+
public function test_that_pluralize_pluralizes(string $value, string $expected, int|array|Countable $count): void
24+
{
25+
$this->assertEquals($expected, LanguageHelper::pluralize($value, $count));
26+
}
27+
28+
#[TestWith(['Migrations', 'Migration'])]
29+
#[TestWith(['migrations', 'migration'])]
30+
public function test_that_pluralizer_singularizes(string $value, string $expected): void
31+
{
32+
$this->assertEquals($expected, LanguageHelper::singularize($value));
33+
}
34+
}

0 commit comments

Comments
 (0)