Installation:
composer require laravie/serialize-queries
Publish the config (if needed):
php artisan vendor:publish --provider="Laravie\SerializeQueries\SerializeQueriesServiceProvider"
Basic Usage: Serialize a query:
use Laravie\SerializeQueries\Facades\SerializeQueries;
$query = User::query()->where('active', true)->orderBy('name');
$serialized = SerializeQueries::serialize($query);
Deserialize and execute:
$executed = SerializeQueries::deserialize($serialized);
$results = $executed->get();
First Use Case: Cache query results for reports or dashboards:
$cacheKey = 'recent_users';
$serialized = cache()->get($cacheKey);
if (!$serialized) {
$serialized = SerializeQueries::serialize(User::query()->latest()->take(10));
cache()->put($cacheKey, $serialized, now()->addHours(1));
}
$results = SerializeQueries::deserialize($serialized)->get();
Caching Strategies:
$serialized = SerializeQueries::serialize(Report::query()->where('period', $period));
cache()->put("report_{$period}", $serialized, now()->addMinutes(5));
$serialized = SerializeQueries::serialize(Order::query()->with('user')->where('status', 'pending'));
cache()->forever("pending_orders_preview", $serialized);
API Rate Limiting: Serialize expensive queries to avoid repeated execution:
$key = "user_{$userId}_expensive_query";
$serialized = cache()->get($key);
if (!$serialized) {
$serialized = SerializeQueries::serialize(
User::query()->with(['orders', 'posts' => function($q) {
$q->where('published', true);
}])
);
cache()->put($key, $serialized, now()->addHours(2));
}
Background Jobs: Offload query serialization to a job for heavy computations:
class SerializeHeavyQueryJob implements ShouldQueue
{
public function handle()
{
$serialized = SerializeQueries::serialize(
Analytics::query()->whereBetween('created_at', [$start, $end])
);
cache()->put("analytics_{$start}_{$end}", $serialized, now()->addDays(7));
}
}
Middleware for Cached Queries: Create middleware to auto-serialize/deserialize queries for specific routes:
public function handle($request, Closure $next)
{
if ($request->has('cached_query')) {
$results = SerializeQueries::deserialize($request->cached_query)->get();
return response()->json($results);
}
return $next($request);
}
Query Builder Extensions: Extend the query builder to add serialization methods:
use Laravie\SerializeQueries\QueryBuilder;
QueryBuilder::macro('serializeAndCache', function ($minutes) {
$serialized = SerializeQueries::serialize($this);
cache()->put('query_' . md5($this->toSql()), $serialized, $minutes);
return $this;
});
Usage:
User::query()->serializeAndCache(10)->get();
Dynamic Query Reconstruction: Rebuild queries dynamically from serialized data:
$serialized = '{"query":"select * from users where active = ?","bindings":[1],"orderBy":["name"],"limit":10}';
$query = SerializeQueries::deserialize($serialized);
$query->where('id', '>', 100); // Modify before execution
Schema Changes:
schema_version to your serialized payload and check it before execution.Binding Type Mismatches:
string vs int).serializeBindingsAs config option:
'bindings' => [
'serializer' => 'json', // or 'php'
'as_array' => true,
],
Raw Expressions:
DB::raw('DATE(created_at)')) may not serialize/deserialize correctly.Memory Limits:
with() relationships may exceed memory limits when serialized.take() or select() to limit the query scope before serialization.Timezone Sensitivity:
whereDate(), whereTime(), or timezone-aware comparisons may behave differently across environments.'timezone' => 'UTC',
Inspect Serialized Payload:
$serialized = SerializeQueries::serialize($query);
dd($serialized); // Check for missing or malformed parts
Validate Bindings: Ensure bindings match the expected types:
$query = SerializeQueries::deserialize($serialized);
dd($query->getBindings()); // Debug bindings
Log Query Differences: Compare original and deserialized queries:
$originalSql = User::query()->where('active', true)->toSql();
$deserializedSql = SerializeQueries::deserialize($serialized)->toSql();
\Log::info("Original SQL: {$originalSql}");
\Log::info("Deserialized SQL: {$deserializedSql}");
Custom Serializers: Override the default serializer for specific query types:
SerializeQueries::extend(function ($query) {
if ($query instanceof \Laravie\SerializeQueries\CustomQuery) {
return $query->customSerialize();
}
return null; // Use default
});
Post-Deserialization Hooks: Modify queries after deserialization:
SerializeQueries::deserialize($serialized, function ($query) {
$query->where('deleted_at', null);
return $query;
});
Binding Sanitization: Sanitize bindings before execution (e.g., escape user input):
SerializeQueries::deserialize($serialized, function ($query) {
$bindings = $query->getBindings();
$sanitized = array_map('htmlspecialchars', $bindings);
$query->bindings = $sanitized;
return $query;
});
Query Diffing: Compare serialized queries for testing:
$serialized1 = SerializeQueries::serialize(User::query()->where('active', true));
$serialized2 = SerializeQueries::serialize(User::query()->where('active', false));
dd(json_decode($serialized1) === json_decode($serialized2)); // false
Excluded Attributes: Configure which query attributes to exclude:
'exclude' => [
'columns', // Exclude selected columns
'wheres', // Exclude where clauses (use with caution)
],
Binding Format:
Choose between json or php serialization for bindings:
'bindings' => [
'serializer' => 'php', // Default is 'json'
],
Environment-Specific Settings:
Override settings per environment in .env:
SERIALIZE_QUERIES_BINDINGS_AS_ARRAY=true
SERIALIZE_QUERIES_TIMEZONE=America/New_York
How can I help you explore Laravel packages today?