128 lines
3.7 KiB
PHP
128 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers\Helpers;
|
|
|
|
use Hyperf\Coroutine\Coroutine;
|
|
use Hypervel\Support\Facades\Cache;
|
|
use Hyperf\Database\Model\Builder as ModelBuilder;
|
|
use Hyperf\Database\Query\Builder as QueryBuilder;
|
|
use Hypervel\Database\Eloquent\Model;
|
|
|
|
class CacheHelper
|
|
{
|
|
/**
|
|
* Get data from cache.
|
|
*
|
|
* @param mixed $query Can be SQL string or Builder object
|
|
* @param mixed $params Parameters to hash (array, object, or model)
|
|
* @return mixed|null
|
|
*/
|
|
public static function get_cache($query, $params = [])
|
|
{
|
|
try {
|
|
$key = self::generateKey($query, $params);
|
|
return Cache::get($key);
|
|
} catch (\Throwable $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set data in cache using a background coroutine (fire-and-forget).
|
|
*
|
|
* @param mixed $query Can be SQL string or Builder object
|
|
* @param mixed $params Parameters to hash
|
|
* @param mixed $data Data to cache
|
|
* @param int $ttl Time to live in seconds (default: 1 day)
|
|
*/
|
|
public static function set_cache($query, $data, $params = [], int $ttl = 86400)
|
|
{
|
|
$key = self::generateKey($query, $params);
|
|
|
|
// Fire-and-forget coroutine: we don't wait for completion
|
|
Coroutine::create(function () use ($key, $data, $ttl) {
|
|
try {
|
|
Cache::put($key, $data, $ttl);
|
|
} catch (\Throwable $e) {
|
|
// Silently fail as cache operations are non-critical
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Erase a specific cache entry.
|
|
*
|
|
* @param mixed $query Can be SQL string or Builder object
|
|
* @param mixed $params Parameters to hash
|
|
*/
|
|
public static function erase_cache($query, $params = [])
|
|
{
|
|
try {
|
|
$key = self::generateKey($query, $params);
|
|
Cache::forget($key);
|
|
} catch (\Throwable $e) {
|
|
// Silently fail
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate the cache key using the specific format:
|
|
* querycache:[queryhash]++++[parameterhash]
|
|
*/
|
|
public static function generateKey($query, $params): string
|
|
{
|
|
$sql = '';
|
|
$parameterData = $params;
|
|
|
|
// Automatically handle Builder objects
|
|
if ($query instanceof ModelBuilder || $query instanceof QueryBuilder) {
|
|
$sql = $query->toSql();
|
|
// If explicit params were not provided or empty, use the builder's bindings
|
|
if (empty($params)) {
|
|
$parameterData = $query->getBindings();
|
|
}
|
|
} else {
|
|
$sql = (string) $query;
|
|
}
|
|
|
|
$queryHash = md5($sql);
|
|
$paramHash = md5(self::serializeParams($parameterData));
|
|
|
|
return "querycache:{$queryHash}++++{$paramHash}";
|
|
}
|
|
|
|
/**
|
|
* Deep serialization of parameters for hashing.
|
|
* Supports arrays, objects, and models.
|
|
*/
|
|
private static function serializeParams($params): string
|
|
{
|
|
if (is_array($params)) {
|
|
$processed = [];
|
|
foreach ($params as $key => $value) {
|
|
$processed[$key] = self::serializeParams($value);
|
|
}
|
|
// Sort keys to ensure consistent hashing for the same associative array
|
|
ksort($processed);
|
|
return json_encode($processed);
|
|
}
|
|
|
|
if (is_object($params)) {
|
|
// Handle models specifically
|
|
if ($params instanceof Model) {
|
|
return get_class($params) . ':' . ($params->hashkey ?? $params->id ?? 'new');
|
|
}
|
|
|
|
if (method_exists($params, 'toArray')) {
|
|
return json_encode($params->toArray());
|
|
}
|
|
|
|
return get_class($params) . ':' . spl_object_hash($params);
|
|
}
|
|
|
|
return (string) $params;
|
|
}
|
|
}
|