Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Conciseuuid Laravel Package

phpexperts/conciseuuid

ConciseUUID is a PHP package for generating compact, URL-safe UUIDs. It encodes standard UUIDs as shorter Base62 strings, cutting identifier length for URLs, databases, and APIs while preserving uniqueness. Simple API works in Laravel or any PHP project.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package:

    composer require phpexperts/conciseuuid
    
  2. Generate a migration that uses a string primary key sized for base62 UUIDs (22 chars), not the default bigIncrements:

    Schema::create('orders', function (Blueprint $table) {
        $table->char('id', 22)->primary();
        $table->string('customer_name');
        $table->timestamps();
    });
    
  3. Have your model use the package's base model (or trait — check vendor/phpexperts/conciseuuid/src for the exact class name and whether it ships a ConciseUuidModel base class or a trait):

    use PHPExperts\ConciseUuid\ConciseUuidModel;
    
    class Order extends ConciseUuidModel
    {
        protected $guarded = [];
    }
    

    The base model is responsible for setting $incrementing = false, $keyType = 'string', and generating the base62 UUID in a creating hook.

  4. First use case: models that end up in URLs or public APIs where a 36-char UUID is noisy. Create a record, confirm $order->id is a 22-char string, then wire route model binding:

    Route::get('/orders/{order}', [OrderController::class, 'show']);
    

    Because the key is a string, Laravel's implicit binding works without extra configuration as long as the controller method type-hints Order.

Where to look first: the package is small. Read the single model/trait source file before using it — it will tell you exactly which method generates the UUID and how the model boots, which is what you need for every pattern below.

Implementation Patterns

Extend the base model for new tables. This is the least-surprising path: any model extending the package's base class inherits the non-incrementing string key and the creating hook that fills it.

Apply the trait to existing models. If you already extend a custom base model, use the package trait (if provided) and replicate the boot method:

protected static function bootConciseUuid(): void
{
    static::creating(function ($model) {
        if (empty($model->{$model->getKeyName()})) {
            $model->{$model->getKeyName()} = /* package generator */;
        }
    });
}

The empty() guard matters — it lets you assign an explicit ID (seeders, imports, tests) without being overwritten.

Migrations that reference a concise-UUID table. Foreign keys must match the referenced column type:

$table->char('order_id', 22);
$table->foreign('order_id')->references('id')->on('orders');

Using $table->foreignId('order_id') will fail — it creates a bigint unsigned, which won't match a char(22) column.

Relationships. Use belongsTo/hasMany normally. The relationship resolves fine because Eloquent compares string keys with WHERE id = ?; just make sure the owning model's $keyType is 'string' so Eloquent doesn't cast the key to int during hydration (the base model handles this if you extend it).

API resources and URLs. The main payoff is shorter, non-sequential identifiers. When exposing IDs in JSON, keep them as strings and never (int) cast them:

return [
    'id' => $this->id,          // "3kJ9aZ..."
    'created_at' => $this->created_at->toIso8601String(),
];

Also consider getRouteKeyName() (returns the primary key by default, so usually nothing to do) and resolveRouteBinding() only if you need custom lookup logic.

Factories and seeders. Since IDs are strings, don't assume integers. Factories should leave the id unset so the creating hook generates it, or explicitly set a deterministic value for fixture tests:

Order::factory()->create(['id' => '0000000000000000000000']);

Polymorphic relations work the same way, but confirm the *_id columns are char(22)/string, since the default morphs() helper creates bigint columns. You'll need to hand-write them:

$table->char('commentable_id', 22);
$table->string('commentable_type');

Bulk inserts. The creating hook only fires per-model. DB::table('orders')->insert([...]) and Model::insert() bypass it entirely — you must generate IDs yourself in bulk paths or switch to Model::create()/upsert.

Gotchas and Tips

  • Type mismatch in migrations is the #1 failure. A char(22) primary key with a bigint foreign key produces confusing "Cannot add foreign key constraint" errors in MariaDB/MySQL. Always match the referenced column definition exactly, including length and collation.
  • $incrementing and $keyType. If you don't use the package's base model, you must set public $incrementing = false; and protected $keyType = 'string'; yourself, or Eloquent will try INSERT ... id = 0 and cast keys to int on read, silently truncating leading characters.
  • Mass assignment. Set protected $guarded = []; or add id to $fillable only if you intend to allow client-supplied IDs. Otherwise keep id guarded so users can't choose keys.
  • No time-ordering. Base62 UUIDs inherit UUID v4 randomness — they are not sortable and have poor B-tree locality under heavy insert load compared to auto-increment or UUIDv7. For high-write tables, keep an integer auto-increment or a created_at index for range queries rather than relying on id order.
  • Length assumptions. 22 characters is a typical base62 encoding of a 128-bit UUID, but verify against the actual output of the package's generator before hard-coding column lengths; using string('id', 36) is a safe fallback if you're unsure.
  • Debugging a missing ID. If inserts fail with a NOT NULL violation, the creating hook likely isn't booting — check the model extends the right base class, that the trait's boot{TraitName} convention matches, and that you haven't overridden boot() without calling parent::boot().
  • Testing. With RefreshDatabase, IDs are regenerated per test, so never assert on a specific ID value. Assert on format instead (e.g., length 22, matches /^[0-9A-Za-z]{22}$/).
  • Extension point. If you need a different alphabet or UUID version (v5 from a namespace, v7 for ordering), override the generator method or swap in a custom creating hook rather than editing the vendor file. The package is small enough that copying its single generator into your own trait is often the lower-risk move.
  • Maintenance risk. With a single-digit star count, zero dependents, and a small surface area, treat this as a thin helper rather than infrastructure. Pin the version in composer.json, and be prepared to inline the (short) generator if the package stops receiving Laravel compatibility updates.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib