Installation:
composer require jetcod/eloquent-keygen
php artisan vendor:publish --provider="eloquent-key-generator-config"
config/eloquent-key-generator.php file exists and adjust settings (e.g., worker_id, datacenter_id) if needed.First Use Case:
Illuminate\Database\Eloquent\Model with Jetcod\Eloquent\Model in app/Models/Model.php (or your base model).
use Jetcod\Eloquent\Model as BaseModel;
class Model extends BaseModel { ... }
use Jetcod\Eloquent\Traits\Snowflake; to a single model.
use Jetcod\Eloquent\Traits\Snowflake;
class User extends Model { use Snowflake; ... }
Migrate Existing Tables:
auto-increment constraint and set the primary key as unsignedBigInteger:
$table->bigInteger('id')->unsigned()->primary();
php artisan migrate
Test:
$user = User::create(['name' => 'Test']);
echo $user->id; // Outputs a Snowflake ID (e.g., 123456789012345678)
Global vs. Per-Model Configuration:
worker_id, datacenter_id) in config/eloquent-key-generator.php.
Example:
'worker_id' => env('SNOWFLAKE_WORKER_ID', 1),
'datacenter_id' => env('SNOWFLAKE_DATACENTER_ID', 1),
'epoch' => 1609459200, // Custom epoch if needed
use Jetcod\Eloquent\Traits\Snowflake as SnowflakeTrait;
class User extends Model {
use SnowflakeTrait {
SnowflakeTrait::boot as private bootSnowflake;
}
protected static function bootSnowflake() {
parent::bootSnowflake();
config(['eloquent-key-generator.worker_id' => 2]); // Override for this model
}
}
Integration with Existing Workflows:
User::factory()->create(); // Auto-generates Snowflake ID
$user = User::create(['name' => 'Alice']);
$post = Post::create(['user_id' => $user->id, 'title' => 'Hello']); // user_id is Snowflake
Batch Operations:
insert() or create() in bulk:
User::insert([
['name' => 'Alice'],
['name' => 'Bob'],
]);
// Both IDs are Snowflake-generated.
Custom ID Fields:
id field by setting $primaryKey and $keyType:
class Order extends Model {
use Snowflake;
protected $primaryKey = 'order_id';
public $keyType = 'string'; // If using string-based Snowflake
}
Multi-Tenant Applications:
datacenter_id to isolate ID ranges per tenant:
config(['eloquent-key-generator.datacenter_id' => $tenantId]);
datacenter_id after operations to avoid conflicts.Testing:
Snowflake trait’s generateId() method:
$this->app->bind('snowflake', function () {
return \Mockery::mock('overload:\Jetcod\Eloquent\Snowflake');
});
config(['eloquent-key-generator.epoch' => 0]);
Performance:
Migration Issues:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry.
Cause: Reusing auto-increment IDs after switching to Snowflake.
Fix: Reset the id column to start from 1 (or a high value) in your migration:
$table->bigInteger('id')->unsigned()->primary()->default(1);
Then truncate and repopulate data.Foreign Key Conflicts:
Foreign key constraint is incorrectly formed.
Cause: Foreign keys referencing Snowflake IDs must also be unsignedBigInteger.
Fix: Update foreign key columns in migrations:
$table->unsignedBigInteger('user_id')->constrained();
Time-Based Collisions:
epoch is too far in the past/future, IDs may collide or appear invalid.
Fix: Use a recent epoch (e.g., 1609459200 for 2021-01-01) and ensure all servers use the same time.Trait vs. Base Model:
Jetcod\Eloquent\Model or include the trait.
Fix: Verify your model extends the correct base class or uses the trait.Environment-Specific IDs:
worker_id/datacenter_id mismatches.
Fix: Use environment variables for these settings:
'worker_id' => env('SNOWFLAKE_WORKER_ID', 1),
'datacenter_id' => env('SNOWFLAKE_DATACENTER_ID', env('APP_ENV') === 'production' ? 1 : 2),
Log Snowflake Generation:
Add this to your model’s bootSnowflake() to debug:
static::created(function ($model) {
\Log::debug('Generated Snowflake ID:', ['id' => $model->id, 'model' => static::class]);
});
Validate ID Format: Snowflake IDs should be 19 digits (for 64-bit IDs). Use this helper:
function isValidSnowflake($id) {
return preg_match('/^\d{19}$/', $id) === 1;
}
Check for ID Reuse: If IDs appear to reset, verify:
auto-increment is set in the database.epoch and worker_id/datacenter_id are consistent across servers.Custom ID Length:
Override the generateId() method in the trait to customize length:
use Jetcod\Eloquent\Traits\Snowflake as SnowflakeTrait;
class User extends Model {
use SnowflakeTrait {
SnowflakeTrait::generateId as private generateSnowflakeId;
}
protected static function generateId() {
return str_pad(parent::generateSnowflakeId(), 20, '0', STR_PAD_LEFT);
}
}
String-Based Snowflake: Convert IDs to strings for readability (e.g., UUID-like format):
protected $keyType = 'string';
protected static function bootSnowflake() {
static::creating(function ($model) {
$model->{$model->primaryKey} = (string) $model->{$model->primaryKey};
});
}
Fallback to UUID: Combine Snowflake with UUID for extra safety (e.g., in distributed systems):
use Ramsey\Uuid\Uuid;
protected static function bootSnowflake() {
static::creating(function ($model) {
$model->{$model->primaryKey} = Uuid::uuid4()->toString();
});
}
Database Indexing: Snowflake IDs are sequential
How can I help you explore Laravel packages today?