Short URL String Helper for Laravel

In a recent project, I wanted to add some string helper methods. Laravel already provides a long list of string helper methods but they didn't cover what I wanted. These string methods can also be chained using the fluent Stringable behaviour. Here is what I did.

I extended the base Illuminate\Support\Str class and added my new method. However, when using the Str::of() behaviour, Laravel returned the underlying class and not my new one. To solve this I created a custom Stringable class and return this by overriding the of() method in my class.

<?php

namespace App\Support;

use App\Support\Stringable;
use Illuminate\Support\Str as Base;

final class Str extends Base
{
    /** @param string $string */
    public static function of($string): Stringable
    {
        return new Stringable($string);
    }

    public static function shortUrl(string $url): string
    {
        return preg_replace('#https?:\/\/(www\.)?#', '', $value);
    }
}

The corresponding custom Stringable class is very simple. It mimics the new method but returns itself so it can be chained fluently.

<?php

namespace App\Support;

use App\Support\Str;
use Illuminate\Support\Stringable as Base;

final class Stringable extends Base
{
    public function shortUrl(): self
    {
        return new static(Str::shortUrl($this->value));
    }
}

I can now easily use these methods to return a shorter URL, both as a standalone method or chained using the fluent interface.

Str::shortUrl('https://www.trovster.com');
Str::of('https://www.trovster.com')->shortUrl()->finish('/');

This isn't the only method I have added, so it makes sense to continue to add them to these classes and keep them organised it one place.

In the config/app.php, I added my new class to the facade alias list. The latest versions of Laravel have made this easy and streamlined the configuration.

'aliases' => Facade::defaultAliases()->merge([
	'Str' => \App\Support\Str::class,
])->toArray(),

I noticed I was unable to use the str() helper method because it defaults to the original Str class. I don't use this helper, but the method is wrapped in a function_exists check, so it can be replaced in the application scope.

Using macro

One thing I hadn't considered was using a string macro. Instead of extending the core support classes to add new methods, I could use the built-in "macro" behaviour. This isn't explicitly mentioned in the Laravel documentation for string helpers, but by inspecting the Str class you can see it uses the Macroable trait.

This means you can do the following, without having to extend the original classes.

Illuminate\Support\Str::macro(
	'shortUrl',
	static fn (string $value): string => preg_replace('#https?:\/\/(www\.)?#', '', $value)
);

Illuminate\Support\Stringable::macro(
	'shortUrl',
	fn (): Stringable => new self(Str::shortUrl($this->value))
);