diff --git a/src/Commands/Database/ListCommand.php b/src/Commands/Database/ListCommand.php index c6211389..65eceefa 100644 --- a/src/Commands/Database/ListCommand.php +++ b/src/Commands/Database/ListCommand.php @@ -11,6 +11,7 @@ use App\Libs\Guid; use App\Libs\Storage\StorageInterface; use Exception; use PDO; +use RuntimeException; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; @@ -61,11 +62,25 @@ final class ListCommand extends Command $guid, null, InputOption::VALUE_REQUIRED, - 'Search Using ' . ucfirst($guid) . ' id.' + 'Search Using ' . ucfirst($guid) . ' external id.' ); } - $this->addOption('parent', null, InputOption::VALUE_NONE, 'If set it will search parent GUIDs instead.'); + $this->addOption('parent', null, InputOption::VALUE_NONE, 'If set it will search parent external ids instead.') + ->addOption('key', null, InputOption::VALUE_REQUIRED, 'For JSON fields key selection.') + ->addOption('value', null, InputOption::VALUE_REQUIRED, 'For JSON fields value selection.') + ->addOption( + 'suids', + null, + InputOption::VALUE_NONE, + 'Search in (server side ids) JSON Field using (--key, --value) options.' + ) + ->addOption( + 'extra', + null, + InputOption::VALUE_NONE, + 'Search in (extra information) JSON Field using (--key, --value) options.' + ); } /** @@ -136,6 +151,32 @@ final class ListCommand extends Command } } + if ($input->getOption('suids')) { + $sField = $input->getOption('key'); + $sValue = $input->getOption('value'); + if (empty($sField) || empty($sValue)) { + throw new RuntimeException( + 'When searching using JSON fields the option --key and --value must be set.' + ); + } + + $where[] = "json_extract(suids,'$.{$sField}') = :suids_{$sField}"; + $params['suids_' . $sField] = $sValue; + } + + if ($input->getOption('extra')) { + $sField = $input->getOption('key'); + $sValue = $input->getOption('value'); + if (empty($sField) || empty($sValue)) { + throw new RuntimeException( + 'When searching using JSON fields the option --key and --value must be set.' + ); + } + + $where[] = "json_extract(extra,'$.{$sField}') = :extra_{$sField}"; + $params['extra_' . $sField] = $sValue; + } + if (count($where) >= 1) { $sql .= 'WHERE ' . implode(' AND ', $where); } @@ -189,9 +230,12 @@ final class ListCommand extends Command foreach ($rows as &$row) { $row['watched'] = (bool)$row['watched']; $row['updated'] = makeDate($row['updated']); - $row['guids'] = json_decode($row['guids'], true); - $row['parent'] = json_decode($row['parent'], true); - $row['extra'] = json_decode($row['extra'], true); + foreach (StateInterface::ENTITY_ARRAY_KEYS as $key) { + if (null === ($row[$key] ?? null)) { + continue; + } + $row[$key] = json_decode($row[$key], true); + } } unset($row); @@ -206,9 +250,12 @@ final class ListCommand extends Command foreach ($rows as &$row) { $row['watched'] = (bool)$row['watched']; $row['updated'] = makeDate($row['updated']); - $row['guids'] = json_decode($row['guids'], true); - $row['parent'] = json_decode($row['parent'], true); - $row['extra'] = json_decode($row['extra'], true); + foreach (StateInterface::ENTITY_ARRAY_KEYS as $key) { + if (null === ($row[$key] ?? null)) { + continue; + } + $row[$key] = json_decode($row[$key], true); + } } unset($row); diff --git a/src/Libs/Servers/PlexServer.php b/src/Libs/Servers/PlexServer.php index af6466f7..0008beb2 100644 --- a/src/Libs/Servers/PlexServer.php +++ b/src/Libs/Servers/PlexServer.php @@ -373,7 +373,7 @@ class PlexServer implements ServerInterface ], ], 'suids' => [ - $this->name => ag($item, 'ratingKey'), + $this->name => (string)ag($item, 'ratingKey'), ], ]; @@ -1696,7 +1696,7 @@ class PlexServer implements ServerInterface 'date' => makeDate($item->originallyAvailableAt ?? 'now')->format('Y-m-d'), ], 'suids' => [ - $this->name => $item->ratingKey, + $this->name => (string)$item->ratingKey, ], ]; diff --git a/src/Libs/Storage/PDO/PDOAdapter.php b/src/Libs/Storage/PDO/PDOAdapter.php index 7ea01184..3eab0b93 100644 --- a/src/Libs/Storage/PDO/PDOAdapter.php +++ b/src/Libs/Storage/PDO/PDOAdapter.php @@ -114,7 +114,8 @@ final class PDOAdapter implements StorageInterface $data = $entity->getAll(); foreach (StateInterface::ENTITY_ARRAY_KEYS as $key) { - if (is_array($data[$key] ?? null)) { + if (null !== ($data[$key] ?? null) && is_array($data[$key])) { + ksort($data[$key]); $data[$key] = json_encode($data[$key], flags: JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } } diff --git a/tests/Mappers/Import/MemoryMapperTest.php b/tests/Mappers/Import/MemoryMapperTest.php index fd604900..524ffa0c 100644 --- a/tests/Mappers/Import/MemoryMapperTest.php +++ b/tests/Mappers/Import/MemoryMapperTest.php @@ -119,13 +119,14 @@ class MemoryMapperTest extends TestCase $movie = $this->testMovie; $episode = $this->testEpisode; - ksort($movie['parent']); - ksort($movie['guids']); - ksort($movie['extra']); - - ksort($episode['parent']); - ksort($episode['guids']); - ksort($episode['extra']); + foreach (StateInterface::ENTITY_ARRAY_KEYS as $key) { + if (null !== ($movie[$key] ?? null)) { + ksort($movie[$key]); + } + if (null !== ($episode[$key] ?? null)) { + ksort($episode[$key]); + } + } $testMovie = new StateEntity($movie); $testEpisode = new StateEntity($episode); diff --git a/tests/Storage/PDOAdapterTest.php b/tests/Storage/PDOAdapterTest.php index 2b61a2fe..9d57ecec 100644 --- a/tests/Storage/PDOAdapterTest.php +++ b/tests/Storage/PDOAdapterTest.php @@ -55,9 +55,12 @@ class PDOAdapterTest extends TestCase { $test = $this->testEpisode; - ksort($test['parent']); - ksort($test['guids']); - ksort($test['extra']); + foreach (StateInterface::ENTITY_ARRAY_KEYS as $key) { + if (null === ($test[$key] ?? null)) { + continue; + } + ksort($test[$key]); + } $item = new StateEntity($test); @@ -105,7 +108,16 @@ class PDOAdapterTest extends TestCase public function test_update_conditions(): void { - $item = $this->storage->insert(new StateEntity($this->testEpisode)); + $test = $this->testEpisode; + + foreach (StateInterface::ENTITY_ARRAY_KEYS as $key) { + if (null === ($test[$key] ?? null)) { + continue; + } + ksort($test[$key]); + } + + $item = $this->storage->insert(new StateEntity($test)); $item->guids['guid_plex'] = StateInterface::TYPE_EPISODE . '/1000'; $updatedItem = $this->storage->update($item);