Skip to content

Instantly share code, notes, and snippets.

@iDevelopThings
Last active January 25, 2023 11:08
Show Gist options
  • Save iDevelopThings/85ad34ae16aa0eac4d4cf4acaeab8afa to your computer and use it in GitHub Desktop.
Save iDevelopThings/85ad34ae16aa0eac4d4cf4acaeab8afa to your computer and use it in GitHub Desktop.
HashIds in laravel
<?php
namespace App\Console\Commands;
use App\Support\HashIds;
use Illuminate\Console\Command;
use Illuminate\Contracts\Container\BindingResolutionException;
use Spatie\ModelInfo\ModelFinder;
use Str;
class GetHashIdCommand extends Command
{
protected $signature = 'hashid {model} {id}';
protected $description = 'Get the hash id for a model id';
public function handle()
{
$modelStr = $this->argument('model');
$id = $this->argument('id');
$model = null;
try {
$model = resolve($modelStr);
} catch (BindingResolutionException $exception) {
$models = ModelFinder::all();
$this->info('Searching all models for model...');
$model = $models->first(fn($model) => Str::endsWith($model, $this->argument('model')));
}
if (!$model) {
$this->error("Model: {$modelStr} not found");
return;
}
$hash = HashIds::forModel($model);
$this->components->twoColumnDetail(
"Hash ID:",
$hash->get($id)
);
}
}
<?php
namespace App\Models\Traits;
use App\Support\HashIds;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException;
/**
* @mixin Model
*/
trait HasHashId
{
public function hashId(): Attribute
{
return Attribute::make(
get: fn() => HashIds::formatHashId($this->getHashIdPrefix(), $this->getKey()),
)->shouldCache();
}
public function getHashIdPrefix(): string
{
return HashIds::formatPrefix($this->getTable());
}
/**
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
*
* @return Model|null
*/
public function resolveRouteBinding($value, $field = null): ?Model
{
if ($field) {
return parent::resolveRouteBinding($value, $field);
}
return $this->findByHashId($value);
}
/**
* Get the value of the model's route key.
*
* @return string
*/
public function getRouteKey(): string
{
return $this->hash_id;
}
public function findByHashId(string $hashId)
{
[$value, $prefix] = HashIds::decode($hashId);
if ($prefix !== $this->getHashIdPrefix()) {
return null;
}
return self::query()->where('id', $value)->first();
}
public static function findOrFailByHashId(string $hashId)
{
$modelInst = resolve(static::class);
return $modelInst->findByHashId($hashId) ?? throw new ModelNotFoundException();
}
}
<?php
namespace App\Support;
use Illuminate\Database\Eloquent\Model;
class HashIds
{
private string $modelFqn;
/**
* @var Model|mixed
*/
private Model $modelInst;
private int|string|null $id = null;
/**
* @param class-string|Model $model
*/
public function __construct(string|Model $model, int|null $id = null)
{
if (!is_a($model, Model::class, true)) {
throw new \InvalidArgumentException("Model must be a subclass of Illuminate\Database\Eloquent\Model");
}
$this->modelFqn = is_string($model) ? $model : get_class($model);
$this->modelInst = is_string($model) ? new $model() : $model;
$this->id = $id ?? (is_string($model) ? null : $model->getKey());
}
/**
* @param string|Model $model
*
* @return HashIds
*/
public static function forModel(string|Model $model, int|null $id = null): HashIds
{
return new static($model, $id);
}
/**
* @param string $hashId
*
* @return array{0: string, 1: string}
*/
public static function decode(string $hashId): array
{
$prefix = str($hashId)->beforeLast('_')->toString();
$hashIdValue = str($hashId)->afterLast('_')->toString();
$hashId = \Hashids::decode($hashIdValue);
return [$hashId, $prefix];
}
public function getPrefix(): string
{
return self::formatPrefix($this->modelInst->getTable());
}
public function get(int|string|null $id = null): string
{
return self::formatHashId($this->getPrefix(), $id ?? $this->modelInst->getKey());
}
public static function encode(int $id): string
{
return \Hashids::encode($id);
}
public static function formatPrefix(string $prefix): string
{
return str($prefix)->singular()->toString();
}
public static function formatHashId(string $prefix, int $id): string
{
return $prefix . '_' . self::encode($id);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment