定制 atldays/laravel-hashids 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

atldays/laravel-hashids

Composer 安装命令:

composer require atldays/laravel-hashids

包简介

Laravel Hashids package for generating short hashed identifiers for models and other numeric values.

README 文档

README

Latest Version on Packagist Total Downloads CI License: MIT

atldays/laravel-hashids helps Laravel apps use hash IDs as one clean, consistent flow.

Instead of handling encoding, decoding, validation, routing and API output in different places, the package keeps everything working together:

  • generating hash IDs on models
  • resolving models from hash IDs
  • route model binding
  • validation rules
  • request decoding in FormRequest
  • serialized API output
  • model-aware artisan commands

Under the hood, it is powered by hashids/hashids, while adding a Laravel-first developer experience around models, requests, validation and routing.

Your application can keep working with plain numeric values internally, while the outside world works with hash IDs in a predictable way.

Features

  • Model trait for generating and resolving hash IDs
  • Query helpers like findByHashId() and whereHashId()
  • Optional route model binding with hash IDs
  • Validation rules for single and multiple values
  • FormRequest integration for automatic decoding
  • Wildcard request field support like items.*.author
  • Optional serialized output that replaces the source column with a hash ID
  • Model attributes for configuring hash ID source column and salt
  • Legacy registry support for custom and old salts
  • Artisan commands for model-specific encode and decode

Installation

Install the package via Composer:

composer require atldays/laravel-hashids

Publish the config file if you want to customize the defaults:

php artisan vendor:publish --provider="Atldays\\HashIds\\HashIdServiceProvider" --tag="laravel-hashids-config"

If you plan to contribute, please also read CONTRIBUTING.md.

Quick Start

Add HasHashId to your model:

<?php

namespace App\Models;

use Atldays\HashIds\Concerns\HasHashId;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasHashId;
}

Now you can:

$user = User::findOrFail(123);

$user->getHashId();
$user->getHashIdInput();
$user->hash_id;

User::findByHashId($user->hash_id);
User::findOrFailByHashId($user->hash_id);

User::query()->whereHashId($user->hash_id)->first();

Optional Model Contract

The HasHashId trait is enough for normal package usage. You do not need an interface just to generate hash IDs, resolve route bindings, validate request fields, or use the request helpers.

If you want a full typed contract for your own services, package integrations or PHPStan-friendly dynamic model classes, also implement HasHashIdModel:

<?php

namespace App\Models;

use Atldays\HashIds\Concerns\HasHashId;
use Atldays\HashIds\Contracts\HasHashIdModel;
use Illuminate\Database\Eloquent\Model;

class User extends Model implements HasHashIdModel
{
    use HasHashId;
}

This is useful when your own code accepts hash ID capable models through type hints:

use Atldays\HashIds\Contracts\HasHashIdModel;
use Illuminate\Database\Eloquent\Model;

/**
 * @param Model&HasHashIdModel $model
 */
function exposeHashId(Model $model): ?string
{
    return $model->getHashId();
}

Core Concept

The package is built around two layers:

  1. Internal application values Your models and database keep using numeric values.

  2. External application values Routes, requests, validation and serialized output can use hash IDs instead.

The hashid.enabled config option controls that external behavior.

When it is enabled:

  • routes use hash IDs
  • validation expects hash IDs
  • FormRequest decoding expects hash IDs
  • serialized output can expose hash IDs

When it is disabled:

  • the core HashId service still works
  • model methods still work
  • external integrations use plain numeric values instead

That makes local debugging and gradual adoption much easier.

For a full request-response cycle, use getHashIdInput() when sending model identifiers to the client and findByHashIdInput() when accepting them back:

$value = $user->getHashIdInput();

$user = User::findByHashIdInput($value);

When hashid.enabled is true, getHashIdInput() returns a hash ID string. When it is false, it returns the plain numeric source value.

Basic Model Usage

Generate Hash IDs

$user = User::findOrFail(123);

$user->hash_id;
$user->getHashId();
$user->getHashIdInput();

User::encodeHashId(123);
User::decodeHashId($user->hash_id);

getHashId() always returns the hashed representation when the model has a source value. getHashIdInput() follows hashid.enabled and is the safer choice for identifiers you send to a client and expect back in a request.

Find Models By Hash ID

User::findByHashId($hashId);
User::findOrFailByHashId($hashId);
User::findManyByHashId([$firstHashId, $secondHashId]);
User::findOrByHashId($hashId, fn () => null);
User::findOrNewByHashId($hashId);

Find Models By External Input

Use these helpers when the value comes from a request, route or another external input and should follow the hashid.enabled config:

User::findByHashIdInput($value);
User::findOrFailByHashIdInput($value);
User::findManyByHashIdInput([$firstValue, $secondValue]);

When hashid.enabled is true, the input is decoded as a hash ID. When it is false, the input is resolved as a plain numeric value.

For symmetry, send values out with getHashIdInput():

return [
    'id' => $user->getHashIdInput(),
];

Query Builder Helpers

User::query()->whereHashId($hashId)->first();
User::query()->whereHashIdNot($hashId)->get();

User::query()->whereHashIds([$firstHashId, $secondHashId])->get();
User::query()->whereHashIdsNot([$firstHashId, $secondHashId])->get();

User::query()->whereHashIdInput($value)->first();
User::query()->whereHashIdInputNot($value)->get();

User::query()->whereHashIdInputs([$firstValue, $secondValue])->get();
User::query()->whereHashIdInputsNot([$firstValue, $secondValue])->get();

Route Model Binding

If you want route model binding to work with hash IDs, add HasHashIdRouting:

<?php

namespace App\Models;

use Atldays\HashIds\Concerns\HasHashId;
use Atldays\HashIds\Concerns\HasHashIdRouting;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasHashId;
    use HasHashIdRouting;
}

Then standard Laravel route model binding works:

// GET /users/jR8k2PqL

Route::get('/users/{user}', function (User $user) {
    return $user;
})->name('users.show');

And route generation also uses the hash ID:

route('users.show', $user);

Validation

The package provides two rules:

  • HashId — validates that the value is a valid hash ID
  • HashIdExists — validates that the hash ID is valid and the model exists

Single Value

use App\Models\User;
use Atldays\HashIds\Rules\HashId;
use Atldays\HashIds\Rules\HashIdExists;

$request->validate([
    'user' => ['required', new HashId(User::class)],
    'existing_user' => ['required', new HashIdExists(User::class)],
]);

Arrays

$request->validate([
    'users' => ['required', 'array', new HashId(User::class)],
    'existing_users' => ['required', 'array', new HashIdExists(User::class)],
]);

The rules are nullable-friendly by design, so they work naturally with Laravel rules like required and nullable.

Validation messages are translated through the package translation files, and you can override them in your application the same way you override other Laravel package translations.

First publish the package translations:

php artisan vendor:publish --tag=hashids-translations

Example:

// lang/vendor/laravel-hashids/en/validation.php

return [
    'hash_id' => 'The :attribute must be a valid hash ID.',
    'hash_ids' => 'The :attribute must contain only valid hash IDs.',
    'hash_id_exists' => 'The selected :attribute is invalid.',
    'hash_ids_exist' => 'One or more selected :attribute values are invalid.',
];

Form Requests

For API-heavy projects, FormRequest integration is one of the most useful parts of the package.

It lets you accept hash IDs from the outside world, validate them, and then work with plain numeric values or resolved models inside your request and controllers.

Use the InteractsWithHashIds trait:

<?php

namespace App\Http\Requests;

use App\Models\Post;
use App\Models\User;
use Atldays\HashIds\Http\Attributes\HashIdField;
use Atldays\HashIds\Http\Concerns\InteractsWithHashIds;
use Atldays\HashIds\Rules\HashId;
use Illuminate\Foundation\Http\FormRequest;

#[HashIdField('posts', Post::class)]
class IndexPostsRequest extends FormRequest
{
    use InteractsWithHashIds;

    protected array $hashIdFields = [
        'author' => User::class,
    ];

    public function rules(): array
    {
        return [
            'author' => ['nullable', new HashId(User::class)],
            'posts' => ['nullable', 'array', new HashId(Post::class)],
        ];
    }
}

After validation:

  • author becomes an integer model value
  • posts becomes an array of integer model values

Resolve Models From Request Fields

$author = $request->hashedModel('author');
$author = $request->hashedModelOrFail('author');
$posts = $request->hashedModels('posts');

Dot Notation And Wildcards

The request layer supports nested fields:

protected array $hashIdFields = [
    'filters.author' => User::class,
    'items.*.users' => User::class,
];

And then:

$request->hashedModel('filters.author');
$request->hashedModels('items.0.users');

Serialized Output

If you want your model to expose hash IDs in serialized output, add SerializesHashId:

<?php

namespace App\Models;

use Atldays\HashIds\Concerns\HasHashId;
use Atldays\HashIds\Concerns\SerializesHashId;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use HasHashId;
    use SerializesHashId;
}

When hashid.enabled is true, the source column used by the model is replaced in serialized output:

$user->toArray();

Instead of:

{
  "id": 123,
  "name": "Alice"
}

you get:

{
  "id": "jR8k2PqL",
  "name": "Alice"
}

When hashid.enabled is false, serialization stays plain and returns numeric values.

Model Attributes

The package supports PHP attributes for model configuration.

Custom Source Column

use Atldays\HashIds\Attributes\HashIdColumn;

#[HashIdColumn('public_id')]
class User extends Model
{
    use HasHashId;
}

Custom Salt

use Atldays\HashIds\Attributes\HashIdSalt;

#[HashIdSalt('custom-user-salt')]
class User extends Model
{
    use HasHashId;
}

Salt From Class Name

use Atldays\HashIds\Attributes\HashIdSaltFromClass;

#[HashIdSaltFromClass]
class User extends Model
{
    use HasHashId;
}

Salt From Table Name

use Atldays\HashIds\Attributes\HashIdSaltFromTable;

#[HashIdSaltFromTable]
class User extends Model
{
    use HasHashId;
}

Trait-Level Defaults

Attributes can also live on traits, which is useful when you want shared behavior across multiple models.

Model-level attributes still take priority over trait-level attributes.

Legacy And Custom Salt Mapping

If you need to support old salts, old namespaces or legacy model mappings, use HashIdRegistry.

Example:

use App\Models\User;
use Atldays\HashIds\HashIdRegistry;

HashIdRegistry::make('App\\Old\\Models\\User', User::class);

This is especially useful when you moved models between namespaces or repositories but still need old hash IDs to keep working.

Artisan Commands

The package provides model-aware commands:

php artisan hashid:encode "App\\Models\\User" 123
php artisan hashid:decode "App\\Models\\User" jR8k2PqL

These commands use the model's own hash ID configuration, including its salt logic.

Configuration

return [
    'enabled' => (bool) env('HASH_ID_ENABLED', !env('APP_DEBUG')),
    'salt' => env('HASH_ID_SALT', 'secret-salt'),
    'length' => (int) env('HASH_ID_LENGTH', 12),
    'alphabet' => env('HASH_ID_ALPHABET', 'abcdefghjklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ234567890'),
];

enabled

Controls the package's external behavior.

When enabled, the package uses hash IDs in:

  • route model binding
  • validation
  • request decoding
  • serialized output

When disabled, those integrations use plain numeric values instead.

salt

Default salt for the core HashId service.

length

The generated hash ID length.

alphabet

The alphabet passed to the underlying hashids/hashids encoder.

Core HashId Service

If you need the low-level service directly, you can use the HashId facade:

use Atldays\HashIds\Facades\HashId;

$encoded = HashId::encode(123);
$decoded = HashId::decode($encoded);

If you need a custom runtime configuration, you can create a configured HashId instance directly:

use Atldays\HashIds\HashId;

$hashId = HashId::make(
    salt: 'custom-salt',
    length: 16,
    alphabet: 'abcdefghijklmnopqrstuvwxyz1234567890',
);

You can also override only the values you need:

$hashId = HashId::make(salt: 'custom-salt');

Local Development

Install dependencies in the same Docker-based environment used by the project:

./bin/docker-php "composer update --prefer-dist --no-interaction"

Run formatting and tests:

./bin/docker-php "vendor/bin/pint --test"
./bin/docker-php "composer test"

atldays/laravel-hashids 适用场景与选型建议

atldays/laravel-hashids 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 90 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 15 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「hash」 「id」 「laravel」 「hashid」 「atldays」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 atldays/laravel-hashids 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 atldays/laravel-hashids 我们能提供哪些服务?
定制开发 / 二次开发

基于 atldays/laravel-hashids 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 90
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 40
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-04-15