Skip to content

Commit

Permalink
feat: extend hashids service
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohammad-Alavi committed Feb 2, 2025
1 parent 14820c7 commit c9a9ac6
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ public function resolveRouteBinding($value, $field = null)
public function processHashId(mixed $value): mixed
{
if ($this->shouldProcessHashIdRouteBinding($value)) {
$value = hashids()->decode($value)[0];
return hashids()->tryDecode($value) ?? $value;
}

return $value;
}

public function shouldProcessHashIdRouteBinding(mixed $value): bool
{
return config('apiato.hash-id') && is_string($value) && [] !== hashids()->decode($value);
return config('apiato.hash-id') && is_string($value);
}

public function resolveChildRouteBinding($childType, $value, $field)
Expand Down
16 changes: 5 additions & 11 deletions src/Abstract/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,18 +217,15 @@ public function decodeSearchQueryString(array $fieldsToDecode): void
request()->query->replace($query);
}

private function decodeValue(string $searchQuery): string|null
private function decodeValue(string $searchQuery): string|int|null
{
$searchValue = $this->parserSearchValue($searchQuery);

if ($searchValue) {
$decodedId = hashids()->decode($searchValue);
if ($decodedId) {
return $decodedId[0];
}
if (is_string($searchValue)) {
return hashids()->tryDecode($searchValue) ?? $searchValue;
}

return $searchValue;
return null;
}

private function parserSearchValue($search)
Expand All @@ -254,10 +251,7 @@ private function decodeData(array $fieldsToDecode, string $searchQuery): array

foreach ($fieldsToDecode as $field) {
if (array_key_exists($field, $searchArray)) {
if (empty(hashids()->decode($searchArray[$field]))) {
throw new \InvalidArgumentException("Only hash ids are allowed. {$field}:$searchArray[$field]");
}
$searchArray[$field] = hashids()->decode($searchArray[$field])[0];
$searchArray[$field] = hashids()->decode($searchArray[$field]);
}
}

Expand Down
9 changes: 2 additions & 7 deletions src/Abstract/Requests/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,8 @@ public function skipHashIdDecode($field): bool

public function decode(string|null $id): int|null
{
if (is_null($id) || 'null' === strtolower($id)) {
return $id;
}

$decoded = hashids()->decode($id);
if ([] !== $decoded) {
return $decoded[0];
if (is_string($id)) {
return hashids()->tryDecode($id);
}

return null;
Expand Down
6 changes: 6 additions & 0 deletions src/Foundation/Providers/ApiatoServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
use Apiato\Foundation\Support\Providers\MigrationServiceProvider;
use Apiato\Foundation\Support\Providers\RateLimitingServiceProvider;
use Apiato\Foundation\Support\Providers\ViewServiceProvider;
use Apiato\Support\HashidsManagerDecorator;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Foundation\Console\AboutCommand;
use Vinkla\Hashids\HashidsManager;

final class ApiatoServiceProvider extends ServiceProvider
{
Expand All @@ -29,6 +31,10 @@ public function register(): void
AliasLoader::getInstance()->alias('DatabaseSeeder', DatabaseSeeder::class);

$this->app->singletonIf(Apiato::class, static fn (): Apiato => Apiato::instance());

$this->app->extend('hashids', static function (HashidsManager $manager) {
return new HashidsManagerDecorator($manager);
});
}

public function boot(): void
Expand Down
4 changes: 2 additions & 2 deletions src/Foundation/helpers.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

use Apiato\Foundation\Apiato;
use Vinkla\Hashids\HashidsManager;
use Apiato\Support\HashidsManagerDecorator;

if (!function_exists('apiato')) {
/**
Expand All @@ -27,7 +27,7 @@ function shared_path(string $path = ''): string
/**
* Get the Hashids instance.
*/
function hashids(): HashidsManager
function hashids(): HashidsManagerDecorator
{
return app('hashids');
}
Expand Down
2 changes: 1 addition & 1 deletion src/Macros/MacroServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function boot(): void
*/
fn (string $hashedValue, string $key = 'id'): bool =>
/* @var Collection $this */
$this->contains($key, hashids()->decode($hashedValue)[0]),
$this->contains($key, hashids()->decode($hashedValue)),
);
}

Expand Down
69 changes: 69 additions & 0 deletions src/Support/HashidsManagerDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Apiato\Support;


use Illuminate\Support\Traits\ForwardsCalls;
use Illuminate\Support\Traits\Macroable;
use Vinkla\Hashids\HashidsManager;

/**
* @mixin HashidsManager
*/
final class HashidsManagerDecorator
{
use ForwardsCalls, Macroable {
__call as macroCall;
}

public function __construct(
private readonly HashidsManager $manager,
) {
}

/**
* Decode a hash id.
*/
public function tryDecode(string $hash): int|null
{
$result = $this->manager->decode($hash);

if ([] !== $result && is_int($result[0])) {
return $result[0];
}

return null;
}

/**
* Decode a hash id.
*
* @throws \InvalidArgumentException
*/
public function decode(string $hash): int
{
$result = $this->tryDecode($hash);

if (is_null($result)) {
throw new \InvalidArgumentException('Invalid hash id.');
}

return $result;
}

/**
* Dynamically pass method calls to the underlying resource.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if (self::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}

return $this->forwardDecoratedCallTo($this->manager, $method, $parameters);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Foundation\AliasLoader;
use Illuminate\Foundation\Http\Kernel;
use Illuminate\Support\Facades\DB;
use Vinkla\Hashids\HashidsManager;
use Workbench\App\Containers\MySection\Book\Middlewares\BeforeMiddleware;

describe(class_basename(ApiatoServiceProvider::class), function (): void {
Expand Down Expand Up @@ -56,4 +57,8 @@

expect(DB::table('books')->count())->toBe(16);
});

it('extends hashids service provider', function (): void {
expect(hashids()->tryDecode('abc'))->toBeNull();
});
})->covers(ApiatoServiceProvider::class);
4 changes: 2 additions & 2 deletions tests/Unit/Foundation/HelpersTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php

use Apiato\Foundation\Apiato;
use Vinkla\Hashids\HashidsManager;
use Apiato\Support\HashidsManagerDecorator;

describe('helpers', function (): void {
it('can get the Apiato instance', function (): void {
Expand All @@ -13,6 +13,6 @@
})->coversFunction('shared_path');

it('can get the Hashids instance', function (): void {
expect(hashids())->toBeInstanceOf(HashidsManager::class);
expect(hashids())->toBeInstanceOf(HashidsManagerDecorator::class);
})->coversFunction('hashids');
});

0 comments on commit c9a9ac6

Please sign in to comment.