Reading some manuals on redis I see that tree data are possible, like root object and nested inserted objects.
But when in laravel 9.4 app with predis/predis 2.1 I write data with
Redis::set('key', serialize( $data ) );
method and in phpRedisAdmin I see that all written data are written (with redis=>prefix parameter from config/database.php) on the same level, but not root object and nested inserted objects.
Which methods have I to use for data wring and tools to view data in tree structure ?
UPDATED BLOCK 1: I copypasted sample code into my app :
$data = [ 'name' => 'John Doe', 'email' => 'johndoe@example.com', 'address' => [ 'street' => '123 Main St', 'city' => 'Anytown', 'state' => 'CA', 'zip' => '12345' ]
];
Redis::hmset('user:1', $data); // Error is pointing to this 173 line
but I got error :
php artisan job:dispatchNow ArticlesCachingJob
ErrorException
Array to string conversion
at vendor/predis/predis/src/Connection/StreamConnection.php:365
361▕
362▕ foreach ($arguments as $argument) {
363▕ \Log::info( ' $argument::');
364▕ \Log::info( $argument);
➜ 365▕ $arglen = strlen(strval($argument));
366▕ $buffer .= "\${$arglen}\r\n{$argument}\r\n";
367▕ }
368▕
369▕ $this->write($buffer);
+8 vendor frames
9 app/Library/Services/ArticlesRedisCaching.php:173
Illuminate\Support\Facades\Facade::__callStatic()
I tried to debug code and added 2 logging lines into vendor/predis/predis/src/Connection/StreamConnection.php file:
public function writeRequest(CommandInterface $command) // line 352
{
$commandID = $command->getId();
$arguments = $command->getArguments();
$cmdlen = strlen($commandID);
$reqlen = count($arguments) + 1;
$buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
foreach ($arguments as $argument) {
\Log::info( ' $argument::');
\Log::info( $argument);
$arglen = strlen(strval($argument));
$buffer .= "\${$arglen}\r\n{$argument}\r\n";
}
$this->write($buffer);
}
2023-02-20 07:27:04] local.INFO: $argument::
[2023-02-20 07:27:04] local.INFO: _votes_db_user:1
[2023-02-20 07:27:04] local.INFO: $argument::
[2023-02-20 07:27:04] local.INFO: name
[2023-02-20 07:27:04] local.INFO: $argument::
[2023-02-20 07:27:04] local.INFO: John Doe
[2023-02-20 07:27:04] local.INFO: $argument::
[2023-02-20 07:27:04] local.INFO: email
[2023-02-20 07:27:04] local.INFO: $argument::
[2023-02-20 07:27:04] local.INFO: johndoe@example.com
[2023-02-20 07:27:04] local.INFO: $argument::
[2023-02-20 07:27:04] local.INFO: address
[2023-02-20 07:27:04] local.INFO: $argument::
[2023-02-20 07:27:04] local.INFO: array (
'street' => '123 Main St',
'city' => 'Anytown',
'state' => 'CA',
'zip' => '12345',
)
[2023-02-20 07:27:04] local.ERROR: Array to string conversion {"exception":"[object] (ErrorException(code: 0): Array to string conversion at /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/predis/predis/src/Connection/StreamConnection.php:365)
[stacktrace]
#0 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php(266): Illuminate\\Foundation\\Bootstrap\\HandleExceptions->handleError()
#1 [internal function]: Illuminate\\Foundation\\Bootstrap\\HandleExceptions->Illuminate\\Foundation\\Bootstrap\\{closure}()
#2 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/predis/predis/src/Connection/StreamConnection.php(365): strval()
#3 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/predis/predis/src/Connection/AbstractConnection.php(110): Predis\\Connection\\StreamConnection->writeRequest()
#4 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/predis/predis/src/Client.php(318): Predis\\Connection\\AbstractConnection->executeCommand()
#5 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/predis/predis/src/Client.php(301): Predis\\Client->executeCommand()
#6 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/laravel/framework/src/Illuminate/Redis/Connections/Connection.php(116): Predis\\Client->__call()
#7 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/laravel/framework/src/Illuminate/Redis/Connections/Connection.php(216): Illuminate\\Redis\\Connections\\Connection->command()
#8 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/laravel/framework/src/Illuminate/Redis/RedisManager.php(276): Illuminate\\Redis\\Connections\\Connection->__call()
#9 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(338): Illuminate\\Redis\\RedisManager->__call()
#10 /mnt/_work_sdb8/wwwroot/lar/MS/MS_Votes/app/Library/Services/ArticlesRedisCaching.php(173): Illuminate\\Support\\Facades\\Facade::__callStatic()
As you see strval can not work with array.
I wrapped 'address' array with serialize method - it worked ok and that is what I see in phpRedisAdmin : https://prnt.sc/BMnRev9HDwZz
Have I to wrap all subarray with serialize method ?
Searching in net I found some hints that HMSET is considered deprecated. I tried hset, but it raised the same Array to string conversion error in both cases with serialize( or without it
I have
Redis server v=5.0.7 sha=00000000:0 malloc=jemalloc-5.2.1 bits=64 build=66bd62
under kubuntu 20.04
UPDATED BLOCK 2:
I tried 2 ways :
$data = [ 'name' => 'John Doe', 'email' => 'johndoe@example.com', 'address' => (object)[ 'street' => '123 Main St', 'city' => 'Anytown', 'state' => 'CA', 'zip' => '12345' ]
];
Redis::hset('user:1', $data);
and I got :
Array to string conversion
at vendor/predis/predis/src/Connection/StreamConnection.php:365
361▕
362▕ foreach ($arguments as $argument) {
363▕ //\Log::info( ' $argument::');
364▕ //\Log::info( $argument);
➜ 365▕ $arglen = strlen(strval($argument));
366▕ $buffer .= "\${$arglen}\r\n{$argument}\r\n";
367▕ }
368▕
369▕ $this->write($buffer);
+8 vendor frames
9 app/Library/Services/ArticlesRedisCaching.php:176
Illuminate\Support\Facades\Facade::__callStatic()
10 app/Jobs/ArticlesCachingJob.php:52
App\Library\Services\ArticlesRedisCaching::addArticleToCaching()
and second :
$data = [ 'name' => 'John Doe', 'email' => 'johndoe@example.com', 'address' => [ 'street' => '123 Main St', 'city' => 'Anytown', 'state' => 'CA', 'zip' => '12345' ]
];
Redis::hset('user:1', (object)$data);
and error again :
Object of class stdClass could not be converted to string
at vendor/predis/predis/src/Connection/StreamConnection.php:365
361▕
362▕ foreach ($arguments as $argument) {
363▕ //\Log::info( ' $argument::');
364▕ //\Log::info( $argument);
➜ 365▕ $arglen = strlen(strval($argument));
366▕ $buffer .= "\${$arglen}\r\n{$argument}\r\n";
367▕ }
368▕
369▕ $this->write($buffer);
+8 vendor frames
9 app/Library/Services/ArticlesRedisCaching.php:176
Illuminate\Support\Facades\Facade::__callStatic()
looks like any value must be string(or serialized string) ?
Thank you!
After some tests I found a valid way top save/read structured data with redis as :
$articleArray = self::serializeArray($article->toArray());
Redis::hmset($redisUniqueKey, $articleArray);
...
protected static function serializeArray(?array $data): array
{
foreach ($data as $fieldName => $fieldValue) {
if (is_array($fieldValue)) {
$data[$fieldName] = serialize($fieldValue);
}
}
return $data;
}
and reading :
$cachedArticle = self::unserializeArray(Redis::HGETALL($redisUniqueKey));
...
protected static function unserializeArray(?array $data): array
{
foreach ($data as $fieldName => $fieldValue) {
if (isSerialized($fieldValue)) {
$data[$fieldName] = unserialize($fieldValue);
}
}
return $data;
}
That works for me with data I test.