Skip to content

Commit

Permalink
refactor: prefixes to use class
Browse files Browse the repository at this point in the history
  • Loading branch information
bensherred committed Nov 28, 2023
1 parent 7fa2f86 commit abcc448
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 85 deletions.
26 changes: 9 additions & 17 deletions config/sqids.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
| Alphabet
|--------------------------------------------------------------------------
|
| This option controls the default "alphabet" used for generating sqids.
| This option controls the default "alphabet" used for generating Sqids.
| The characters and numbers listed below will be included. You must
| have at least 3 unique characters or numbers.
|
Expand All @@ -35,7 +35,7 @@
| Length
|--------------------------------------------------------------------------
|
| This option controls the "minimum length" of the generated sqid
| This option controls the "minimum length" of the generated Sqid
| excluding the prefix and separator. This value must be greater
| than 0.
|
Expand All @@ -49,7 +49,7 @@
|--------------------------------------------------------------------------
|
| THis option allows you to "blacklist" certain words that shouldn't be
| included in the generated sqids.
| included in the generated Sqids.
|
*/

Expand All @@ -61,31 +61,23 @@
|--------------------------------------------------------------------------
|
| This option controls the "separator" between the prefix and the
| generatedsqid.
| generated Sqid.
|
*/

'separator' => '_',

/*
|--------------------------------------------------------------------------
| Prefix
| Prefix Class
|--------------------------------------------------------------------------
|
| This option controls the sqid "prefix", You can control the length of
| the prefix and the casing. By default, the prefix will be generated
| based on the model name.
|
| Setting the prefix length to "0" will remove the prefix all together.
|
| Supported Casing: "lower", "upper", "camel", "snake", "kebab",
| "title", "studly"
| This option controls the class that should be used for generating the
| Sqid prefix. You can use any class that implements the following
| contract: \RedExplosion\Sqids\Contracts\Prefix.
|
*/

'prefix' => [
'length' => 3,
'case' => 'lower',
],
'prefix_class' => \RedExplosion\Sqids\Prefixes\ConstantPrefix::class,

];
8 changes: 0 additions & 8 deletions src/Concerns/HasSqids.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,6 @@ public function resolveRouteBindingQuery($query, $value, $field = null): Builder

public static function keyFromSqid(string $sqid): ?int
{
$prefixLength = Config::prefixLength();
$prefix = Str::beforeLast(subject: $sqid, search: Config::separator());
$expectedPrefix = Sqids::prefixForModel(model: __CLASS__);

if ($prefixLength && $prefix !== $expectedPrefix) {
return null;
}

$sqid = Str::afterLast(subject: $sqid, search: Config::separator());

$length = Str::length(value: $sqid);
Expand Down
16 changes: 16 additions & 0 deletions src/Contracts/Prefix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace RedExplosion\Sqids\Contracts;

use Illuminate\Database\Eloquent\Model;

interface Prefix
{
/**
* @param class-string<Model> $model
* @return string
*/
public function prefix(string $model): string;
}
11 changes: 8 additions & 3 deletions src/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,14 @@ protected static function models(): array
/** @phpstan-ignore-next-line */
->filter(fn(ReflectionClass $class) => in_array(needle: HasSqids::class, haystack: $class->getTraitNames()))
/** @phpstan-ignore-next-line */
->mapWithKeys(fn(ReflectionClass $reflectionClass) => [
Sqids::prefixForModel($reflectionClass->getName()) => $reflectionClass->getName()
])
->mapWithKeys(function (ReflectionClass $reflectionClass): array {
/** @var class-string<EloquentModel> $model */
$model = $reflectionClass->getName();

return [
Sqids::prefixForModel($model) => $reflectionClass->getName(),
];
})
->toArray();

return $models;
Expand Down
29 changes: 29 additions & 0 deletions src/Prefixes/ConstantPrefix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace RedExplosion\Sqids\Prefixes;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use RedExplosion\Sqids\Contracts\Prefix;

class ConstantPrefix implements Prefix
{
/**
* Use the first 3 constants as the model prefix.
*
* @param class-string<Model> $model
* @return string
*/
public function prefix(string $model): string
{
$classBasename = class_basename(class: $model);

$prefix = str_replace(search: ['a', 'e', 'i', 'o', 'u'], replace: '', subject: $classBasename);

$prefix = rtrim(mb_strimwidth(string: $prefix, start: 0, width: 3, encoding: 'UTF-8'));

return Str::lower(value: $prefix);
}
}
27 changes: 27 additions & 0 deletions src/Prefixes/SimplePrefix.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace RedExplosion\Sqids\Prefixes;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use RedExplosion\Sqids\Contracts\Prefix;

class SimplePrefix implements Prefix
{
/**
* Use the first 3 characters as the model prefix.
*
* @param class-string<Model> $model
* @return string
*/
public function prefix(string $model): string
{
$classBasename = class_basename(class: $model);

$prefix = rtrim(mb_strimwidth(string: $classBasename, start: 0, width: 3, encoding: 'UTF-8'));

return Str::lower(value: $prefix);
}
}
30 changes: 10 additions & 20 deletions src/Sqids.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace RedExplosion\Sqids;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use RedExplosion\Sqids\Support\Config;
use Sqids\Sqids as SqidsCore;

Expand All @@ -23,14 +22,13 @@ public static function forModel(Model $model): string
return "{$prefix}{$separator}{$sqid}";
}

/**
* @param class-string<Model> $model
* @return string
*/
public static function prefixForModel(string $model): ?string
{
$classBasename = class_basename(class: $model);
$prefixLength = Config::prefixLength();

if (!$prefixLength) {
return null;
}
$prefixClass = Config::prefixClass();

/** @var string|null $modelPrefix */
/** @phpstan-ignore-next-line */
Expand All @@ -40,19 +38,11 @@ public static function prefixForModel(string $model): ?string
return $modelPrefix;
}

$prefix = $prefixLength < 0
? $classBasename
: rtrim(mb_strimwidth(string: $classBasename, start: 0, width: $prefixLength, encoding: 'UTF-8'));

return match (Config::prefixCase()) {
'upper' => Str::upper(value: $prefix),
'camel' => Str::camel(value: $prefix),
'snake' => Str::snake(value: $prefix),
'kebab' => Str::kebab(value: $prefix),
'title' => Str::title(value: $prefix),
'studly' => Str::studly(value: $prefix),
default => Str::lower(value: $prefix),
};
if (!$prefixClass) {
return null;
}

return $prefixClass->prefix(model: $model);
}

public static function encodeId(string $model, int $id): string
Expand Down
30 changes: 16 additions & 14 deletions src/Support/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

namespace RedExplosion\Sqids\Support;

use Exception;
use RedExplosion\Sqids\Contracts\Prefix;
use RedExplosion\Sqids\Prefixes\SimplePrefix;

class Config
{
protected static string $defaultAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
Expand Down Expand Up @@ -74,26 +78,24 @@ public static function separator(): string
return $separator;
}

public static function prefixLength(): int
public static function prefixClass(): ?Prefix
{
/** @var int|null $prefixLength */
$prefixLength = config(key: 'sqids.prefix.length', default: static::$defaultPrefixLength);
$prefix = config(key: 'sqids.prefix_class');

if (!$prefixLength || !is_int($prefixLength)) {
return static::$defaultPrefixLength;
if (!$prefix) {
return null;
}

return $prefixLength;
}

public static function prefixCase(): string
{
$prefixCase = config(key: 'sqids.prefix.case', default: static::$defaultPrefixCase);
try {
$prefix = new $prefix();
} catch (Exception) {
return new SimplePrefix();
}

if (!$prefixCase || !is_string(value: $prefixCase)) {
return static::$defaultPrefixCase;
if (!$prefix instanceof Prefix) {
return new SimplePrefix();
}

return $prefixCase;
return new $prefix();
}
}
2 changes: 1 addition & 1 deletion tests/Concerns/HasSqidsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

expect($customer->sqid)
->toBeString()
->toStartWith('cus_');
->toStartWith('cst_');
});

it('can get the sqid prefix', function (): void {
Expand Down
16 changes: 16 additions & 0 deletions tests/Prefixes/ConstantPrefixTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

use RedExplosion\Sqids\Prefixes\ConstantPrefix;
use Workbench\App\Models\Charge;
use Workbench\App\Models\Customer;

it(description: 'can generate a prefix without vowels', closure: function (): void {
expect(new ConstantPrefix())
->prefix(model: Customer::class)
->toBe(expected: 'cst')
->prefix(model: Charge::class)
->toBe(expected: 'chr');
;
});
16 changes: 16 additions & 0 deletions tests/Prefixes/DefaultPrefixTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

use RedExplosion\Sqids\Prefixes\SimplePrefix;
use Workbench\App\Models\Charge;
use Workbench\App\Models\Customer;

it(description: 'can generate a default prefix', closure: function (): void {
expect(new SimplePrefix())
->prefix(model: Customer::class)
->toBe(expected: 'cus')
->prefix(model: Charge::class)
->toBe(expected: 'cha');
;
});
4 changes: 2 additions & 2 deletions tests/SqidsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

expect(Sqids::forModel(model: $customer))
->toBeString()
->toStartWith('cus_');
->toStartWith('cst_');
});

it('can get the sqid prefix for a model', function (): void {
Expand All @@ -24,7 +24,7 @@
expect($customer->getSqidPrefix())
->toBeNull()
->and(Sqids::prefixForModel(model: $customer::class))
->toBe('cus')
->toBe('cst')
->and($charge->getSqidPrefix())
->toBe('ch')
->and(Sqids::prefixForModel(model: $charge::class))
Expand Down
26 changes: 6 additions & 20 deletions tests/Support/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

declare(strict_types=1);

use RedExplosion\Sqids\Prefixes\ConstantPrefix;
use RedExplosion\Sqids\Prefixes\SimplePrefix;
use RedExplosion\Sqids\Support\Config;

it('can get the alphabet', function (): void {
Expand Down Expand Up @@ -52,26 +54,10 @@
expect(Config::separator())->toBe('_');
});

it('can get the prefix length', function (): void {
expect(Config::prefixLength())->toBe(3);
it('can get the prefix class', function (): void {
expect(Config::prefixClass())->toBeInstanceOf(ConstantPrefix::class);

config()->set('sqids.prefix.length', 4);
config()->set('sqids.prefix_class', SimplePrefix::class);

expect(Config::prefixLength())->toBe(4);

config()->set('sqids.prefix.length', '1');

expect(Config::prefixLength())->toBe(3);
});

it('can get the prefix case', function (): void {
expect(Config::prefixCase())->toBe('lower');

config()->set('sqids.prefix.case', 'upper');

expect(Config::prefixCase())->toBe('upper');

config()->set('sqids.prefix.case', 4);

expect(Config::prefixCase())->toBe('lower');
expect(Config::prefixClass())->toBeInstanceOf(SimplePrefix::class);
});

0 comments on commit abcc448

Please sign in to comment.