Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- `Naming::headline()`, the words of a name with a capital each, as Laravel's `Str::headline()` writes them, for the packages that label a role or a binding source from its class name

## [0.1.0] - 2026-10-12

A first version for the framework's next release to build on; 1.0.0 comes with that release.
Expand Down
19 changes: 19 additions & 0 deletions src/Support/Naming.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ public static function kebab(string $value): string
return self::snake($value, '-');
}

/**
* The value as words with a capital each, as Laravel's Str::headline()
* writes it: `EventManager`, `event_manager` and `event-manager` give
* `Event Manager`.
*/
public static function headline(string $value): string
{
$words = mb_split('\s+', $value) ?: [$value];
$title = static fn (string $word): string => mb_convert_case($word, MB_CASE_TITLE, 'UTF-8');

$parts = count($words) > 1
? array_map($title, $words)
: array_map($title, preg_split('/(?=\p{Lu})/u', implode('_', $words), -1, PREG_SPLIT_NO_EMPTY) ?: []);

$collapsed = str_replace(['-', '_', ' '], '_', implode('_', $parts));

return implode(' ', array_filter(explode('_', $collapsed)));
}

/**
* The class name without its namespace.
*/
Expand Down
4 changes: 4 additions & 0 deletions tests/Unit/NamingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
expect(Naming::kebab($name))->toBe(Str::kebab($name));
})->with('names');

it('writes headlines as Str::headline() does', function (string $name): void {
expect(Naming::headline($name))->toBe(Str::headline($name));
})->with('names')->with(['kebab-case' => 'event-manager', 'snake with digits' => 'event_manager_2', 'several words' => 'Event manager role', 'empty' => '']);

it('strips the namespace as class_basename() does', function (): void {
expect(Naming::classBasename('App\\Cms\\PostTypes\\Book'))->toBe(class_basename('App\\Cms\\PostTypes\\Book'))
->and(Naming::classBasename('Book'))->toBe('Book');
Expand Down
Loading