anourvalar/eloquent-serialize
Serialize and restore Laravel Eloquent QueryBuilder instances. Save complex queries (with relations, where clauses, limits, etc.) to an array/package and later unserialize back into a builder to run the query again. Supports Laravel 6–12.
Installation:
composer require anourvalar/eloquent-serialize
No additional configuration is required for basic usage.
First Use Case: Serialize a simple query for caching or deferred execution:
use AnourValar\EloquentSerialize\EloquentSerialize;
// Serialize a query
$serialized = EloquentSerialize::serialize(
\App\User::query()
->where('active', true)
->limit(10)
);
// Store $serialized in cache/Redis or pass to a queue job
// Later, deserialize and execute
$builder = EloquentSerialize::unserialize($serialized);
$users = $builder->get();
Where to Look First:
serialize() and unserialize() methods.tests/ directory) for edge cases and supported query types.EloquentSerialize exceptions during deserialization (e.g., schema mismatches).Cache serialized queries for high-traffic endpoints (e.g., API paginated lists):
$cacheKey = 'users_active_paginated';
$serialized = cache()->get($cacheKey);
if (!$serialized) {
$serialized = EloquentSerialize::serialize(
\App\User::query()
->where('active', true)
->paginate(20)
);
cache()->put($cacheKey, $serialized, now()->addHours(1));
}
$builder = EloquentSerialize::unserialize($serialized);
return $builder->get();
Offload query execution to background jobs (e.g., nightly reports):
// In your controller/route
dispatch(new SerializeAndQueueJob($serializedQuery));
// Job class
class SerializeAndQueueJob implements ShouldQueue {
public function __construct(public string $serializedQuery) {}
public function handle() {
$builder = EloquentSerialize::unserialize($this->serializedQuery);
$results = $builder->get();
// Process results (e.g., send email, update analytics)
}
}
Serialize tenant-scoped queries to avoid rebuilding filters:
$tenantId = auth()->user()->tenant_id;
$serialized = EloquentSerialize::serialize(
\App\User::query()
->where('tenant_id', $tenantId)
->with(['orders', 'subscriptions'])
);
// Reuse $serialized across requests/services
Toggle query logic via feature flags without code changes:
if (featureEnabled('premium_dashboard')) {
$serialized = EloquentSerialize::serialize(
\App\User::query()
->with(['premiumOrders', 'analytics'])
->whereHas('premiumSubscription')
);
} else {
$serialized = EloquentSerialize::serialize(
\App\User::query()
->with(['orders', 'basicAnalytics'])
);
}
Export query logic to third-party tools:
$queryLogic = EloquentSerialize::serialize(
\App\Order::query()
->whereYear('created_at', now()->year)
->with(['user', 'items'])
);
// Send $queryLogic to a BI tool (without exposing DB credentials)
Pre-serialize and cache queries during low-traffic periods:
// In a scheduled command (e.g., Artisan command)
public function handle() {
$serialized = EloquentSerialize::serialize(
\App\Product::query()
->with(['reviews', 'inventory'])
->orderBy('created_at', 'desc')
);
cache()->forever('products_featured', $serialized);
}
Validate schema before unserializing to handle DB changes:
$builder = EloquentSerialize::unserialize($serialized);
$this->validateSchema($builder); // Custom method to check columns/tables
if ($this->schemaValid) {
$results = $builder->get();
} else {
throw new \RuntimeException("Schema mismatch for serialized query.");
}
Serialize only specific parts of a query for flexibility:
$baseQuery = \App\User::query()->where('active', true);
$serializedWhere = EloquentSerialize::serialize($baseQuery->where('role', 'admin'));
// Later, merge with dynamic conditions
$builder = EloquentSerialize::unserialize($serializedWhere);
$builder->where('created_at', '>', now()->subYear());
Use Laravel’s cache drivers (Redis, Memcached) for serialized queries:
$serialized = EloquentSerialize::serialize(
\App\Post::query()
->where('published', true)
->orderBy('views', 'desc')
);
cache()->put('trending_posts', $serialized, now()->addMinutes(5));
Store serialized queries in job payloads for async processing:
// Job
public function handle() {
$builder = EloquentSerialize::unserialize($this->serializedQuery);
$data = $builder->get()->toArray();
// Process $data (e.g., generate PDF, update cache)
}
Cache serialized API responses to reduce DB load:
Route::get('/users', function () {
$cacheKey = 'api_users_list';
$serialized = cache()->get($cacheKey);
if (!$serialized) {
$serialized = EloquentSerialize::serialize(
\App\User::query()
->with(['posts', 'roles'])
->paginate(15)
);
cache()->put($cacheKey, $serialized, now()->addMinutes(10));
}
return EloquentSerialize::unserialize($serialized)->get();
});
Mock serialized queries in unit tests:
public function test_serialized_query() {
$serialized = EloquentSerialize::serialize(
\App\User::query()->where('name', 'John')
);
$builder = EloquentSerialize::unserialize($serialized);
$result = $builder->get();
$this->assertCount(1, $result);
}
$builder = EloquentSerialize::unserialize($serialized);
if (!$this->checkSchemaCompatibility($builder)) {
throw new \RuntimeException("Query incompatible with current schema.");
}
Schema facade to verify column/table existence:
private function checkSchemaCompatibility($builder) {
foreach ($builder->getQuery()->columns as $column) {
if (!$this->schema->hasColumn($builder->getModel()->getTable(), $column)) {
return false;
}
}
return true;
}
selectRaw()) may fail to serialize.// Example: Extend for custom accessors
EloquentSerialize::extend(function ($serializer) {
$serializer->addIgnoredMethod('customAccessor');
});
where(), with(), orderBy()).// Avoid serializing for real-time paths
if (request()->wantsJson() && !cache()->has('serialized_query')) {
// Use fresh query instead
}
$query = \App\User::query()->withoutGlobalScopes(['SoftDeletes']);
$serialized = EloquentSerialize::serialize
How can I help you explore Laravel packages today?